☰
Python网络小说分析系统:爬虫、文本挖掘与可视化毕设全攻略
2026/10/20 5:33:30 网站建设 项目流程

很多准备计算机毕业设计的同学,一看到“网络小说分析系统”这种题目,第一反应就是:这不就是个爬虫加词云吗?能做,但答辩时容易翻车。这个题目背后真正要考察的,远不止爬虫本身。我在帮几个同学调试类似项目时,发现“分析”二字才是拉开差距的关键——数据清洗、特征提取、可视化、结论生成,每一步都有坑。

这个基于Python的网络小说分析系统,具体做的事情是:从小说网站抓取热门小说的元数据和正文内容,然后对文本做分词、关键词提取、情感分析、章节长度统计、角色出现频率分析,最后生成一系列可视化图表和统计分析报告。程序、文档、讲解、定制全流程都有,适合用来做毕业设计或者课程项目。如果你正在纠结选题,或者已经选了这个但不知道从哪下手,这篇文章我把整个设计思路、代码结构、答辩要点和踩坑记录都梳理一遍,你可以直接当参考。

1. 项目整体设计与需求拆解

1.1 毕业设计题目到底在考什么

先把这个题目的潜台词拆开。网络小说分析系统,重点在“分析”而不是“爬虫”。评阅老师想看到的是:你能不能把非结构化文本变成结构化数据,能不能从数据里发现有意思的规律,能不能把结论可视化地表达出来。单纯的爬虫+词云在本科毕设里已经烂大街了,如果想拿高分,必须加入多维度分析。

我从几个同学的选题方案里总结了一套合理的模块划分:

  • 数据采集层:爬取小说列表页、详情页、正文页,支持多线程/异步,处理反爬。
  • 数据存储层:使用MySQL或SQLite存储书籍信息、章节内容、评论等,设计合理表结构。
  • 文本处理层:对正文进行分句、分词、去停用词、词性标注、命名实体识别。
  • 分析层:包括字数统计、章节变化趋势、情感倾向分析、角色出场频率、高频词提取、文本相似度等。
  • 展示层:使用Flask或Django搭建Web界面,或者用PyQt做桌面端,展示图表和报告。
  • 导出层:生成PDF/Word分析报告,方便答辩展示。

重点提醒一下:这个题目的难点不在某个单独模块,而在模块之间的数据流。爬虫抓下来的是HTML,需要清洗成纯文本;存储到数据库后,又要从库里读出文本进行分词;分词结果要保存到新表里供统计使用。每一步数据结构不一致,很容易在联调时出错。

1.2 需求功能列表的合理划分

一个合格的毕设文档,必须把需求分成功能需求和非功能需求。功能需求一般这样列:

  • 用户注册登录功能:普通用户和admin角色区分
  • 数据采集配置功能:可以指定小说分类、采集页数、采集间隔
  • 分析任务管理功能:创建任务、查看进度、查看历史结果
  • 可视化展示功能:排行榜、趋势折线图、词云、情感分布饼图
  • 报告导出功能:将分析结果打包为HTML或PDF

非功能需求要写性能指标,比如:采集1000章内容的时间不超过10分钟,分词处理速度不低于每秒5000字,系统并发用户数不少于100。这些数字不要乱写,答辩时老师可能会追问“你们实际测试过吗”,所以初始方案里就要预留测试方案。

我用Python + Flask + MySQL的组合时,数据流一般是这样的:

爬虫采集 -> BeautifulSoup/parsel解析 -> 清洗过滤 -> 存入MySQL 数据分析 -> jieba分词 -> 统计计数器 -> 生成JSON图表数据 前端展示 -> ECharts渲染 -> 导出报告

这套链路简单直接,适合毕设,也方便在论文里画架构图。

2. 技术选型与核心库解析

2.1 为什么选Python而不是Java

这个项目选择了Python,核心原因是文本处理生态系统太成熟了。Java做分词要引入一系列开源库,配置复杂;Python里一个jieba就能搞定大部分场景,snownlp或者sentiment_analysis可以处理情感分析,wordcloud生成词云只需要三行代码。对毕设来说,实现成本和调试成本低很多。

另外,爬虫这块Python优势更明显。requests加BeautifulSoup4是入门标配,遇到动态加载的网站还可以用playwright或者selenium兜底。其他语言当然也能爬,但写起来啰嗦,而且很多人只是临时用一下,Python从零到能跑的成功率最高。

2.2 框架选型:Flask vs Django

Flask和Django都有同学在用,我的建议是:

  • 如果目标是快速出Demo、展示效果、答辩时不卡壳,选Flask。
  • 如果论文里想写“使用了MTV模式的Django框架”,并且项目打算做得更完整,选Django。

两者不影响核心分析算法,但影响项目目录结构。Flask更灵活,可以很随意地组织代码,适合动手能力强但时间紧的人;Django自带admin后台和ORM,适合想把用户管理和数据库设计写得规范一点的人。我的经验是:毕设周期三个月以上,Django更从容;一个月赶工,用Flask,注意把代码分层写清楚。

2.3 数据库设计:MySQL还是SQLite

毕设作品里MySQL用得最多,原因是老师普遍认这个。如果你答辩机子没有安装MySQL服务,可以用Docker快速起一个,或者干脆用SQLite作为备选方案。我实际调试时发现,SQLite在单机毕设场景下完全够用,而且省去一堆环境问题。但要注意,论文里不要写“因为SQLite简单所以选它”,要写“由于本项目数据规模有限,采用轻量级嵌入式数据库SQLite以降低部署复杂度”。

数据库表结构建议这样设计:

  • book表:id、书名、作者、分类、简介、字数、阅读量、状态、url
  • chapter表:id、book_id、章节标题、正文文本、字数、更新时间
  • comment表:id、book_id、评论内容、评论时间
  • analysis_result表:id、book_id、分析类型、结果JSON、创建时间

把分析结果存成JSON是偷懒但实用的做法,展示层直接读JSON传给前端,避免了多表关联查询。论文里你可以解释“为满足不同类型分析结果的灵活存储,采用JSON字段存储半结构化数据”,完全说得通。

3. 核心模块拆解与代码实现

3.1 爬虫模块的封装思路

爬虫不能把所有代码堆在一起,否则答辩时老师问你“如果目标网站改版了怎么办”,你会很被动。我习惯写一个spider.py,里面定义三个类:

  • BookListSpider:负责爬列表页,提取书名、作者、简介、url。
  • ChapterSpider:负责进入详情页,提取所有章节链接。
  • ContentSpider:负责下载正文,并清洗HTML标签。

注意,内部方法不要直接互相调用产生强耦合,而是通过任务队列传递URL。比如列表页爬完后,把每本书的详情页地址放入queue,由章节爬虫消费。这样既符合软件工程的分层思想,也方便后续扩展成分布式爬虫。

采集限速是必须做的。有人在毕设里疯狂并发,导致对方服务器把整个IP封了,这是真实发生过的事。我建议至少在两次请求之间加time.sleep(random.uniform(1, 3)),并且使用requests.Session复用连接池。代码大致如下:

import time import random import requests from bs4 import BeautifulSoup class BaseSpider: def __init__(self): self.session = requests.Session() self.session.headers.update({ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' }) def fetch(self, url): try: response = self.session.get(url, timeout=10) response.raise_for_status() response.encoding = response.apparent_encoding time.sleep(random.uniform(1, 2)) return response.text except requests.RequestException as e: print(f"请求失败: {url}, 错误: {e}") return None

这里response.encoding = response.apparent_encoding很重要,很多小说站点是GBK编码,不设置的话中文会乱码。这是最容易踩的坑之一。

3.2 文本清洗:如何从HTML中提取纯净正文

爬取下来的正文里会混入很多标签、广告文案、正则替换残留。如果不做清洗,分词结果会非常难看。清洗分三步:

  1. 用BeautifulSoup的get_text()去除标签。
  2. 去掉“本章未完,请点击下一页继续阅读”这类常见提示。
  3. 用正则清理多余的空白字符和特殊符号。

表现层可以封装一个TextCleaner类:

import re from bs4 import BeautifulSoup class TextCleaner: @staticmethod def clean(html): soup = BeautifulSoup(html, 'html.parser') text = soup.get_text(separator='\n') text = re.sub(r'[【】\[\]()()]', '', text) text = re.sub(r'第[一二三四五六七八九十百千0-9]+章.*', '', text) text = re.sub(r'\n{2,}', '\n', text) return text.strip()

不要小看这个类,答辩现场如果让老师自己搜一本书试试,得到整齐的纯文本,比你PPT上放十页架构图都有说服力。

3.3 分词与关键词提取

分词统一用jieba,它自带繁体支持、自定义词典功能。网络小说里有很多人名、地名、专有名词,比如“斗气化马”、“三段斗之气”,默认词典切得乱七八糟。解决办法是维护一个自定义词典custom_words.txt,在程序启动时加载:

import jieba jieba.load_userdict('custom_words.txt')

关键词提取我推荐用TF-IDF,不要用TextRank,因为TextRank在长文本上计算较慢。jieba.analyse.extract_tags用起来非常简单:

import jieba.analyse tags = jieba.analyse.extract_tags(text, topK=20, withWeight=True)

返回的withWeight=True会给出权重,可以画成条形图或者词云。词云背景建议使用圆形或者书本形状,图片不要太大,否则生成会卡。

高频词统计这里藏着一个关键点:如果不去停用词,“说道”、“知道”、“现在”、“心里”这些词会占据榜首,没有任何分析价值。一定要加载停用词表,网上有现成的中文停用词库,也可以自己根据小说语料补充。我在项目里是这么做的:

stopwords = set() with open('stopwords.txt', 'r', encoding='utf-8') as f: for line in f: stopwords.add(line.strip()) def filter_words(words): return [w for w in words if w not in stopwords and len(w) > 1]

这里保留长度大于1的词,是为了过滤单字虚词,比如“的”、“了”、“他”,这些即使不在停用词表里,统计也没有意义。

3.4 情感分析的实现方式

网络小说情感分析可以分两个层面:整体情感偏向和分章节情感曲线。整体偏向判断小说是爽文还是虐文,分章节曲线可以看整个故事的情绪起伏。

snownlp自带中文情感分析,简单易用:

from snownlp import SnowNLP s = SnowNLP(text) score = s.sentiments # 0~1,越接近1越积极

但snownlp的训练语料是商品评论,用在网络小说上误差很大。我实际测试下来,玄幻小说里“轰”、“杀”、“死”这些字会让情感值异常低,但其实在剧情里可能只是激烈打斗,读者并不觉得虐。为了提高可信度,我建议组合情感词典做二次修正。可以在snownlp的结果基础上,统计自定义褒贬词的出现次数来调整评分,公式很简单:

final_score = base_score + positive_count * 0.01 - negative_count * 0.01

把结果限制在0到1之间,然后画成折线图,可以看出高潮章节和低谷章节。答辩时解释清楚这个修正机制,反而能彰显你思考过问题,不只是调库。

3.5 角色出场频率统计

网络小说角色出场频率是特别有意思的分析维度,也是很多同学不知道怎么做的地方。两种实现思路:

  • 简单版:通过自定义词典+词频统计,如果“萧炎”出现了300次,那这一章他大概率是主角。
  • 进阶版:利用jieba.posseg进行词性标注,提取人名(nr),再合并同指称(如“萧炎”、“炎帝”、“萧炎兄弟”)。

进阶版的问题是同指消解难度大,不建议在毕设里做太深。我当时采用的方法是:先从小说简介里提取核心角色名,然后用正则和字符串匹配统计每个角色在本章的出场次数,最后算出占比。这个方法简单、结果直观,也方便答辩讲解。

示例:

import re def count_character_appearances(text, characters): result = {} for name in characters: result[name] = len(re.findall(name, text)) return result

如果想在论文里写得好看一点,可以说“基于规则的角色出场统计方法”,本质上就是正则匹配加上下文窗口。

3.6 可视化与报告生成

可视化统一用ECharts,前端通过Ajax从后端拿JSON数据。常用的图包括:

  • 书籍字数排行榜(横向柱状图)
  • 各分类占比(饼图/玫瑰图)
  • 每日更新数量趋势(折线图)
  • 高频词Top20(条形图)
  • 全书情感曲线(折线图)
  • 角色出场占比(环形图)

ECharts的图表配置很繁琐,建议封装一个公共函数,只传数据源和类型,这样重复代码少。我记得很多同学卡在标签中文字体显示上,记得设置textStyle: { fontFamily: 'Microsoft YaHei' }。

报告导出可以用两个方案,一是直接生成HTML报告,把ECharts静态渲染成图片插入;二是用python-docx生成Word版分析报告。HTML方案更稳,Word方案更正式。毕设建议两个都做:网页里展示动态图表,文档里用截图和表格描述分析结论。

导出报告时,注意图表的图片保存要等ECharts渲染完成,使用echarts-convert或者pyecharts的Snapshot_selenium都可以,但环境依赖较多。更省事的做法是后端用matplotlib生成静态图,虽然没有前端图表炫酷,但不会出现图片空白的问题。我的做法是前后端展示用ECharts,导出PDF时用matplotlib重新渲染一套静态图,两套逻辑共用同一个数据处理函数,这样代码量多了一些但整体可靠性高。

4. 实操过程与完整流程记录

4.1 环境准备与项目初始化

以一个模拟项目X为例,完整实操流程如下:

第一步,创建虚拟环境:

python -m venv venv source venv/bin/activate # Windows下执行 venv\Scripts\activate

第二步,安装依赖包:

pip install flask requests beautifulsoup4 lxml jieba snownlp wordcloud matplotlib pyecharts pandas

这里建议把所有依赖写到requirements.txt,方便对方复现环境。pandas主要用来做数据统计,虽然纯Python也能写,但用DataFrame处理章节字数分布、按分类分组聚合会高效很多。

第三步,初始化数据库。用SQLAlchemy来定义模型,好处是不用手写SQL建表语句,而且对MySQL和SQLite自动适配。模型代码:

from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() class Book(db.Model): __tablename__ = 'book' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(200), nullable=False) author = db.Column(db.String(100)) category = db.Column(db.String(50)) intro = db.Column(db.Text) word_count = db.Column(db.Integer, default=0) status = db.Column(db.String(20))

4.2 爬虫实操:从列表页到正文化

我和A同学一起调试时,发现很多同学卡在列表页的URL规则上。以常见的笔趣阁风格站点为例,列表页可能是/sort/1/,翻页是/sort/1_2/。第一页和后续页的URL模式不一致,很容易采重或采漏。解决方式是构造通用函数:

def build_list_url(category_id, page): if page == 1: return f"https://example.com/sort/{category_id}/" return f"https://example.com/sort/{category_id}_{page}/"

进入书籍详情页后,提取章节列表时要注意“最新章节列表”和“全部章节列表”的差异。有些网站有两个区块,用CSS选择器会误抓。稳妥做法是先定位<div id="list">或者<dl class="book_list">,再取所有<a>标签。这里一定要先看网页结构再写选择器,不要凭经验猜。

4.3 数据入库与清洗联调

爬虫把正文抓下来之后,先要经过TextCleaner.clean(),确保存进数据库的是干净的文本。同时计算字数:可以用len(text),但中文小说里标点、空格都算字符,如果要以“万字”为单位,通常需要去掉空白字符再统计:

word_len = len(re.sub(r'\s', '', clean_text))

在入库前用INSERT ... ON DUPLICATE KEY UPDATE防止重复数据。SQLAlchemy里可以设置unique=True在url字段上,然后循环每个章节:

chapter = Chapter(url=url, book_id=book_id, title=title, content=clean_text) db.session.merge(chapter)

merge会先查找记录是否存在,存在则更新,不存在则插入。不过数据量大时性能一般,我在实际项目中是用MySQL的原生语法配合session.execute批量插入,速度能提升十倍。

4.4 分析流程的调度代码

分析模块涉及多本书、多类分析,如果放在Web请求里同步跑,界面会卡死。我建议单独写一个analyzer.py,里面定义run_analysis(book_id),然后通过后台线程执行。为了追踪进度,可以用Redis里的计数器,或者简单在analysis_result表里存一个“正在分析”的状态字段。

一个典型的调用流程:

def run_analysis(book_id): book = Book.query.get(book_id) all_text = "".join(ch.content for ch in book.chapters) if not all_text: return clean_text = TextCleaner.clean(all_text) keywords = extract_keywords(clean_text) sentiments = analyze_sentiment_by_chapter(book) character_freq = count_characters(book) result = { "book_id": book.id, "keywords": keywords, "sentiments": sentiments, "character_freq": character_freq } save_result(book.id, "full_analysis", result)

比较极端的场景,一本书有几百万字,全量文本一次性拼接再分词,内存占用会飙到几百兆。有一种优化策略是按章节逐章分析,然后再汇总。这样内存稳定,但章节之间的连贯语义会丢失。折中方案是每十章做一次窗口,既保留局部语境,又不让单次计算量过大。我在毕设里按章节分析,因为论文里好描述——空间复杂度是O(章节数)。

4.5 前端展示的完整闭环

我推荐的前端页面包括:

  • 首页仪表盘:总书籍数、总章数、平均字数、最近采集时间
  • 小说排行页:可按字数、阅读量排序的表格
  • 书籍详情页:基本信息加三个Tab,分别展示词云、情感曲线、角色频率
  • 分析报告页:一键生成报告并下载

Flask路由很简单:

@app.route('/book/<int:book_id>') def book_detail(book_id): book = Book.query.get(book_id) result = AnalysisResult.query.filter_by(book_id=book_id, type='full_analysis').first() if result: data = json.loads(result.result_json) else: data = {} return render_template('book_detail.html', book=book, data=data)

前端用fetch或者直接<script>里嵌入JSON都行。答辩演示时,网络环境不稳定能访问不了CDN,所以ECharts依赖最好下载到本地静态目录,不要用外网CDN。这个细节很现实,很多同学在现场演示时图表加载不出来,就是因为没考虑到活动现场没有外网。

5. 常见问题排查与避坑实录

5.1 爬到一半IP被封怎么办

这是最高频的问题。如果你是短时间大量请求,异常表现为返回403或者需要验证码。建议:

  • 放慢速度,随机sleep间隔。
  • 设置重试和代理池,但毕设不要搞太复杂,维护两三个代理就够了。
  • 伪装Headers中加入Referer和Accept-Language字段。
  • 采集时间限制在白天而非深夜,避免触发风控。

如果对方封了IP,只能等一段时间。所以在系统设计上,要支持断点续爬。数据库里给章节加一个is_fetched字段,每次启动爬虫只采集未下载的章节。这个功能写进论文里也是加分项,说明你考虑了真实业务场景的运行稳定性。

5.2 jieba分词结果出现URL、乱码符号

原因很多,常见的是爬取内容没有清洗干净。有一些小说正文里混有“您可以在百度搜索...”这类站点推广文字,用正则把它们过滤掉。另外要检查requests获取内容时的编码,乱码通常就是因为编码解码错误。统一策略是先获取headers里的charset,如果找不到,再调用apparent_encoding。

5.3 ECharts图表不显示或显示空白

先看浏览器控制台有没有报错。常见的JSON解析错误是因为后端返回的字符串里包含单引号、换行、特殊符号。处理办法是用json.dumps(data, ensure_ascii=False),同时把HTML模板里的数据用{{ data|tojson }}输出,Jinja2的tojson过滤器会自动转义。

第二个原因是图表容器的高度没设置。ECharts初始化的div必须显式给高度,比如style="height:400px",否则图全部渲染不出来。

第三个原因是Ajax请求跨域,由于Flask和前端同源时不会出现,但如果分开部署,就需要配置CORS。毕设里避免跨域最简单的方式是让Flask同时托管静态文件。

5.4 情感分析结果和直觉完全相反

这个我在前面提到过,不能盲目信任snownlp。有一次分析一本热血玄幻小说,全书情感均值只有0.31,明显偏低,原因是“杀”“死”“滚”这些动词太密集。我后来用了一个很笨但有效的办法:统计每章正面词和负面词数量,然后根据比值生成情感得分,把这个得分与snownlp得分加权平均,结果就符合直觉了。权重可以动态调,但答辩时不需要展示调参过程,只需说明逻辑。

5.5 导出PDF/Word时图片丢失

python-docx插入图片时,需要传入一个文件路径,如果你用BytesIO,要保存到临时文件再插入。另一个坑是Word文档里中文乱码,通常是没有设置中文字体。我在python-docx里会这样处理:

from docx.oxml.ns import qn style = doc.styles['Normal'] style.font.name = 'Times New Roman' style.element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')

设置完eastAsia字体后,中文就正常了。答案里可能包含很多分析图表,建议正文用表格汇总数据,再插入柱状图,比直接堆图效果好得多。

6. 毕业设计文档与答辩准备技巧

6.1 论文结构怎么规划

毕业论文主要章节建议这样安排:

  • 绪论:研究背景、国内外研究现状、研究内容与意义
  • 需求分析:业务需求、功能需求、非功能需求、可行性分析
  • 系统设计:架构图、功能模块设计、数据库ER图、接口设计
  • 系统实现:每个模块的核心代码片段与实现效果截图
  • 系统测试:测试用例、功能测试、性能测试、兼容性测试
  • 总结与展望:主要内容总结、不足与改进方向

真正花时间的不是写文字,而是画图。架构图、时序图、ER图建议用专业工具画,保证风格统一。这里要注意,不要画那种看起来像从网上随便找的图,老师一眼就能看出来。所有图尽量自己画,做到真实反映你的系统。

6.2 答辩演示时应该准备哪些东西

答辩现场最多只给十分钟,演示顺序很关键。我的建议是:

  1. 打开系统首页,展示整体功能导航。
  2. 展示数据库中真实存在的小说数据量。
  3. 随机选择一本书,现场跑一次完整分析流程。
  4. 展示词云、情感曲线、角色频率等图表。
  5. 展示导出报告文件。

一定要准备备选方案:如果现场分析任务超时,列表中要有一个“提前预生成”的历史报告,随时能展示。演示前先清理数据库,避免出现之前调试时产生的异常脏数据。

6.3 关于“定制”这件事,怎么理解

标题里写了“定制”,说明客户可能希望系统能适配不同网站或者不同的分析需求。作为具备开发能力的同学,你要做到的是把爬虫和分析部分设计成可配置的:配置文件中可以指定采集网站的URL规则、选择器、清洗规则等,而不是把所有逻辑写死在代码里。我常用的做法是让每个小说源对应一个SourceParser类,里面定义统一的接口,不同的解析逻辑各自独立。这样换一个数据源只需要新增一个类,其他代码完全不用动。

答辩的时候,如果老师问“如果我想分析其他网站的小说,系统要改多少”,你能说出这种可扩展设计,绝对是加分的。

7. 最后的经验补充

做这个题目的过程中,我最大的体会是:不要试图把“分析系统”做成一个大而全的东西。很多同学一开始就计划爬几十个网站、分析几十本小说、做一堆花哨功能,结果直到答辩前一天还在调试爬虫。更合理的路径是,选一个稳定的目标站,爬三四本不同分类的小说,把分析模块做扎实,界面做到干净整洁,论文里的测试数据和分析结论做得细致一些,已经足够拿一个不错的成绩。

实用性小技巧方面,强烈建议在项目里写一个批量测试脚本,能够一键启动爬虫、一键运行全部分析、一键生成报告。这个脚本可以放在run.py里,用Python标准库的argparse接收命令行参数。这样不仅自己调试方便,答辩演示时输入一行命令就开始运转,观感也专业。

如果你打算用这个题目做后续扩展,可以考虑加入评论情感分析、小说热度预测、读者画像等方向。网络小说领域的数据挖掘价值不只是文本本身,评论区和读者行为里藏着的规律更有意思。后续可以做推荐算法,让用户根据喜好找到书,这会是个完整的推荐系统课题,比单纯分析文本更有挑战性,也是能够继续深入的方向。

最后再分享一个细节:论文致谢部分,可以写感谢开源社区为你提供了jieba、ECharts等优秀工具,感谢某导师给予的指导——这些真实且恰当的表达,比空泛的套话要有力得多。技术项目最重要的还是自己动手把每条链路跑通,你投入的每一步调试,最终都会变成答辩现场的信心。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询