Python网络小说分析系统毕设实战:从爬虫到可视化

📅 发布时间:2026/10/16 13:05:59
Python网络小说分析系统毕设实战:从爬虫到可视化
最近我把“基于Python的网络小说分析系统”这个毕业设计项目从头到尾完整做了一遍。坦白说标题听起来像是个普通的爬虫项目实际动手之后才发现它把数据采集、文本清洗、分词、情感分析、主题聚类到可视化展示全链路都串在了一起做完之后你对Python数据分析的整个知识树会清晰很多。网络小说这个领域特别适合练手因为公开数据量大、文本风格鲜明、用户评论里情绪词非常多分析结果一眼就能看出规律。这篇文章我不打算讲太多虚的直接按我实际踩过的路把系统设计思路、技术选型、核心代码、坑点排查和答辩技巧一次性说清楚。如果你正准备选这个题目或者已经选了但不知道从哪里下手可以照着这个思路走。1. 项目整体解读毕业设计选型背后的逻辑做毕设第一步不是写代码而是想清楚“我要做一个什么东西、它要解决什么问题”。很多同学一上来就先写爬虫抓了一堆数据之后不知道能干什么最后只能堆一个词云交差。这个思路非常吃亏。这个系统本质上是一个“文本分析系统”网络小说只是它的载体。系统需要完成的事情可以拆成四层从站点页面抓取公开的小说元信息和读者评论清洗整理成结构化数据对文本内容做分词、去停用词、关键词抽取基于文本做情感倾向判断和主题分布计算最后把结果用图表展示出来形成可交互的分析看板。1.1 需求拆解这不是单纯的爬虫项目很多参考选题写的是“基于Python的网络小说分析系统”核心关键词有两个一个是网络小说一个是分析系统。网络小说限定数据来源和文本类型分析系统才是技术主菜。我把需求拆成四个功能模块数据采集模块负责从目标网站抓取小说名称、作者、分类、简介、字数、评分、状态这些元信息以及读者评论等文本数据。数据存储模块把抓下来的数据做清洗去重按照字段存入数据库。分析挖掘模块对简介和评论进行分词、停用词过滤、TF-IDF关键词提取再计算情感得分做分类与主题聚类。可视化展示模块将分析结果输出为词云、柱状图、折线图、雷达图等并通过网页的形式集中展示。如果想要拿高分还要把模块之间的数据流转做得足够清晰。我当时画不了复杂的架构图没关系但至少要做到每个模块可以独立替换。比如今天采集某个小说站点明天想换成漫画评论数据分析模块和展示模块不用动只需要改采集器的解析规则。1.2 整体流程从网页到看板系统整体流程我用大白话描述一下目标网站页面 - 爬虫请求页面 - BeautifulSoup解析HTML - 提取目标字段 - 数据清洗 - 存入SQLite - 读取数据库 - jieba分词 - TF-IDF关键词 - SnowNLP情感分析 - LDA主题建模 - 图表生成 - Flask展示看板。这个流程没有特别复杂的算法但是每一环都需要调通而且数据在每一步都会变化。很多同学在“存入SQLite”之后就没有下文了因为分词的输入格式和数据量要重新处理。这个衔接是毕设中最容易卡住的地方。1.3 数据合规与版权红线这里必须多说一句。网络小说站点的内容是有版权的毕业设计做爬虫不能碰全文重点是抽取公开可见的元数据、排行榜、简介和评论信息并且控制抓取频率尽量避开站点明确禁止的路径。我自己的做法是只对前若干页公开列表页建立本地分析数据集不抓全文不商用并在论文中把数据来源和处理方式写清楚。答辩老师很可能会问数据来源合不合规提前把这块讲明白比现场支支吾吾要好得多。2. 核心技术选型从采集到可视化的搭配方案技术选型决定了开发效率。这里直接给出我最终采用的组合requests BeautifulSoup做采集SQLite做存储pandas做数据处理jieba做分词SnowNLP做情感分析gensim做主题建模pyecharts做交互图表Flask做Web展示。2.1 requests BeautifulSoup 还是 Scrapy我第一版用的是Scrapy后来放弃了换成了requests加BeautifulSoup。原因很简单这个项目的数据体量不需要Scrapy这种重型框架反而需要快速调试和随时看中间结果。requests负责拿HTMLBeautifulSoup负责解析。写一个get_page()函数加入header模拟浏览器、超时设置和随机延时十行代码就够用了。Scrapy虽然自带并发、去重、中间件但学习成本高而且调试相对麻烦。如果你已经熟练使用Scrapy用它也可以如果只是刚接触Python爬虫不要为了炫技给自己挖坑。2.2 jieba分词和自定义词典文本分析绕不开分词。jieba是Python中文分词最常用的工具默认词典能覆盖大多数场景。但网络小说有自己的语言习惯比如“穿越”“系统流”“金手指”“爽文”“太监”这些词在通用词典里要么没有要么被切成单字。我跑了第一次分词后发现“金手指”被切成“金手”和“指”非常影响关键词效果。解决办法是建立自定义词典格式每行一个词还可以加词频和词性金手指 100 n 爽文 100 n 扮猪吃虎 80 v 系统流 80 n然后在分词前用jieba.load_userdict(userdict.txt)加载。这个小动作让后面的关键词质量上升了一个档次。2.3 情感分析SnowNLP和词典方法怎么配合情感分析我使用了两套方法做交叉验证。第一套是SnowNLP它对中文短文本的积极性判断比较友好直接给出一条评论的情感倾向值在0到1之间越接近1越积极。但SnowNLP有个明显问题它对网络流行语和新词不敏感比如“绝绝子”“YYDS”这种词默认情感分很容易跑偏。所以我又补了一个基于情感词典的打分逻辑。简单说就是维护一个自定义的积极词库和消极词库给每个词打一个权重分数然后加权求和。两者各跑一遍如果结果差异很大就把该条数据标记为“待复核”。在毕业设计答辩里提到这个细节会显得你有工程意识。2.4 存储和可视化组合存储直接用SQLite理由是单机、免安装、一个文件搞定适合演示。如果你用的是MySQL会在环境配置上浪费很多时间。pandas负责把抓下来的列表转成DataFrame做去重和类型转换后写入SQLite后续分析时pd.read_sql_query()一条语句就能把数据读回来。可视化我用的pyecharts它最大的好处是生成的是HTML图表可以嵌入Flask页面图表支持鼠标悬浮、缩放、切换答辩演示效果明显好于matplotlib的静态图。Flask本身足够轻量几个路由就够用没必要上Django。3. 核心环节实操手把手走一遍完整流程这一部分我直接把核心代码和当时的设计思路放出来。不要照抄关键是理解每一步在干什么。3.1 数据表设计与建库先设计两个主要的表小说信息表和评论表。我用的字段如下小说信息表book_idbook_nameauthorcategoryintroword_countstatusscoreupdate_time。评论表comment_idbook_idcontentsentiment_scorecomment_time。建表时最需要注意的是字段类型。比如word_count使用intscore使用floatintro使用text。SQLite的建表可以这么做CREATE TABLE IF NOT EXISTS book ( book_id INTEGER PRIMARY KEY AUTOINCREMENT, book_name TEXT NOT NULL, author TEXT, category TEXT, intro TEXT, word_count INTEGER, status TEXT, score REAL, update_time TEXT ); CREATE TABLE IF NOT EXISTS comment ( comment_id INTEGER PRIMARY KEY AUTOINCREMENT, book_id INTEGER NOT NULL, content TEXT NOT NULL, sentiment_score REAL, comment_time TEXT );我当时用pandas.to_sql()直接写入比手工execute方便得多但要注意if_exists参数设成append还是replace要想清楚。如果是重新抓数据建议先删除旧表再写入避免主键冲突。3.2 爬虫模块的核心代码爬虫模块的核心就两件事请求HTML和解析字段。我写了一个通用函数import requests from bs4 import BeautifulSoup import time import random HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0 Safari/537.36, Referer: https://example.com/ } def get_page(url, retries3): for i in range(retries): try: resp requests.get(url, headersHEADERS, timeout8) resp.raise_for_status() resp.encoding utf-8 return resp.text except Exception as e: print(f第{i1}次请求失败: {e}) time.sleep(2) return None解析部分我直接用CSS选择器比如提取一个书架列表里每本书的信息soup BeautifulSoup(html, html.parser) items soup.select(.book-list .book-item) for item in items: name item.select_one(.book-name).get_text(stripTrue) author item.select_one(.book-author).get_text(stripTrue) category item.select_one(.book-category).get_text(stripTrue) intro item.select_one(.book-intro).get_text(stripTrue)这里有两个容易忽略的细节。第一网页的class名经常带有空格式符号比如classbook-item hot用CSS选择器时只写.book-item就可以匹配但如果你抄别人的解析代码只看一部分很容易就漏字段。第二抓取之间一定要加随机延时不然请求太快会被封IP或者触发验证码。3.3 文本清洗与分词的细节抓下来的简介和评论并不是直接能用的。首先要做清洗我用正则把网页转义符、邮箱、电话号码、连续标点符号全部去掉所有英文字母转成小写数字根据分析需求保留或丢弃。清洗完之后用jieba做分词import jieba import re STOP_WORDS set() with open(stopwords.txt, r, encodingutf-8) as f: for line in f: STOP_WORDS.add(line.strip()) def clean_text(text): text re.sub(r.*?, , text) text re.sub(r[a-zA-Z0-9], , text) text re.sub(r\s, , text) return text.strip() def tokenize(text): text clean_text(text) words jieba.lcut(text) result [] for w in words: w w.strip() if len(w) 1 and w not in STOP_WORDS: result.append(w) return result停用词表可以在网上下载一份通用中文停用词再手动补充一些网络小说领域的常见弱义词比如“一样”“一个”“没有”“我们”。这一步非常重要否则词频统计里全是“的”“了”“是”真正的分析价值会被淹没。3.4 TF-IDF关键词提取和LDA主题建模在做关键词提取时我用jieba内置的analyse.extract_tags直接提取TF-IDF权重最高的词同时用sklearn做了对照。需要注意TF-IDF的关键词适合描述单篇文本的重点但不适合描述整个数据集的整体主题。比如你统计所有玄幻小说简介排名靠前的一定是“修炼”“功法”“大陆”这类词。import jieba.analyse def extract_keywords(text, topK10): return jieba.analyse.extract_tags(text, topKtopK, withWeightTrue)主题聚类这里要增加一点深度。我用了gensim的LDA模型先把所有简介分词后建立词典过滤掉出现次数过少和过多的词再训练主题数为5的LDA模型。主题数怎么选我一开始拍脑袋设成5后来发现三个主题高度重叠于是用模型困惑度做了一次对比按困惑度和可解释性最终选了4个主题。答辩时老师如果问“主题数怎么定”你就答“我做了不同K值的对比实验结合困惑度曲线和实际主题词判断不是随便写的”这个印象分立刻就不一样。3.5 可视化看板的实现可视化部分我用pyecharts生成图表再通过Flask渲染。核心逻辑是这样从SQLite读数据清洗统计然后生成图表的JSON配置最后在模板中导入ECharts渲染。比如做一个各分类小说数量的柱状图from pyecharts.charts import Bar from pyecharts import options as opts from pyecharts.globals import ThemeType category_data df[category].value_counts() bar ( Bar(init_optsopts.InitOpts(themeThemeType.LIGHT)) .add_xaxis(category_data.index.tolist()) .add_yaxis(小说数量, category_data.values.tolist()) .set_global_opts(title_optsopts.TitleOpts(title分类数量统计)) )生成图表后可以用bar.render_embed()拿到HTML代码再嵌入到Flask模板里。这里有个坑pyecharts版本不同API差异会很大新版用options as opts旧版可能是pyecharts.charts.Bar直接配置。在写代码之前先固定版本我用的pyecharts2.0.0所有代码都按这个版本写。Flask部分非常短from flask import Flask, render_template app Flask(__name__) app.route(/) def dashboard(): return render_template(dashboard.html, book_barbar.render_embed())注意render_template的传参方式图表多的时候不要一次性生成所有图表先用几个核心图表把框架搭出来再逐步增加。4. 常见问题与排查技巧我踩过的雷这个段落是真正值钱的部分。每个坑都对应一晚上的调试时间整理出来大家可以提前避开。4.1 请求被拦截和失败最常见的错误是403 Forbidden原因多半是没有带header或者header不够真实。解决办法是加User-Agent和Referer并且把浏览器完整的header都带上。另一个问题是如果同一IP短时间请求几十次会被站点封掉。我的处理方式很笨但有效每次请求之间time.sleep(random.uniform(1, 3))同时控制总页数不超过30页。如果确实因为演示需要临时抓取优先使用抓取频率更低的排行接口不要连续点击详情页。4.2 中文乱码和编码问题爬虫拿到网页之后直接resp.text可能乱码因为requests默认编码可能不是utf-8。解决方法是resp.encoding resp.apparent_encoding或者直接指定resp.encoding utf-8。写入SQLite和前端展示的时候也要注意数据库连接加charsetutf8导出JSON时用ensure_asciiFalse。如果不处理最典型的表现是数据库里中文正常但前端页面显示乱码。4.3 情感分析的准确率问题SnowNLP默认模型对“作者断更气死我了”这种句子会输出比较正的分数因为句子里的“作者”和“更新”可能被识别成积极词。我尝试用否定词规则优化在情感词前面如果有“不”“没”“无”这类否定词就把情感得分反转。同时建立领域情感词典比如“太监”“烂尾”“弃书”直接给负分“脑洞”“文笔”“期待”给正分。最终情感分析的正确率比单独用SnowNLP高了大概百分之十几这个比例没有精算但答辩时需要的不是精确值而是“我如何评估和优化”的思考过程。4.4 图表渲染空白和内存过大页面图表多的时候浏览器渲染会变慢甚至出现空白。我排查后发现是因为Flask调试模式下反复刷新导致资源加载占用后来把app.run(debugFalse)就稳定多了。如果你用的是render_embed()所有的ECharts库都会打进HTML页面体积会非常大。更好的做法是使用pyecharts的get_js_links()列出JS链接或者直接把图表生成JSON配置文件交给前端ECharts去加载。数据量特别大的时候先做聚合再传给图表不要把所有评论都传给前端。5. 从“能跑”到“能答辩”系统优化和加分思路很多项目的现状是功能都能跑但外观和工程化程度不够答辩时很难展示。这里分享一下怎么把一个“能跑的系统”包装成“能答辩的优秀毕设”。5.1 合理的项目目录结构不要把所有代码都堆在main.py里。我的项目目录是这样的project/ ├── README.md ├── requirements.txt ├── data/ │ └── novel.db ├── crawler/ │ ├── spider.py │ └── cleaner.py ├── analysis/ │ ├── word_process.py │ ├── sentiment.py │ └── topic_model.py ├── web/ │ ├── app.py │ └── templates/ │ └── dashboard.html └── docs/ ├── 需求文档.md └── 设计文档.md模块拆开之后答辩老师让你“演示一下爬虫模块”时你可以直接演示crawler/spider.py单独运行而不是在Flask项目里找半天。这个细节会让人觉得你工程素养不差。5.2 加入时间维度做趋势分析基础功能是静态统计加了时间维度之后整个系统会高级一截。比如抓取小说列表的更新时间字段按月份统计不同分类的小说数量变化可以看出一段时间“玄幻和都市哪个类型更热”。用折线图展示前端加一个下拉筛选器选择分类这个交互效果会让老师觉得系统有“分析”的味道。5.3 文档和演示的准备工作毕设文档按需求分析、概要设计、详细设计、系统测试、总结几个大块来写。每个模块配一张运行截图截图要加上边框和标注不要随手截一个黑窗口。答辩演示时准备两条链路离线数据展示和现场抓取演示。离线数据确保图表稳定现场抓取展示爬虫真能跑。我复试时吃了亏只准备了一条链路结果现场网络波动爬虫卡在请求上。后来每次演示都准备了两套方案慌乱感少了很多。6. 做完这个项目后的一些真实体会代码写完、文档写完、答辩结束之后我再回头看这个项目最大的感受是“分析系统”的核心不在算法有多复杂而在于你有没有把数据从无到有、从脏到净、从静到动地跑通。很多同学在选题时想要创新算法但毕业设计的评价标准更多是看工程和思维你面对问题怎么拆解、遇到坑怎么排查、结果怎么解释。这也是我写这篇长文的原因。如果让我给后来者一条最重要的建议那就是不要怕用现成的库但一定要弄懂每个库在流程里的位置。jieba和SnowNLP都是工具真正的设计是你对“网络小说文本如何分析”这个问题的理解。你自己把数据流逻辑搞清楚了就算不用这些库也能拿Python基础语法一步步实现。如果后续想扩展可以加爬虫调度任务、评论时间序列情感趋势、基于标签的推荐模块或者把前端换成Vue搭建一个更现代的可视化平台。这些方向每一个都能继续做深。毕业设计只是一个阶段的总结先做出来再慢慢变好是这个项目给我的最大收获。