网络小说数据分析系统实战:Python爬虫+MySQL+可视化全链路
发布时间:2026/9/26 14:03:33来源:尧图网络
简介这是一套面向高校计算机相关专业毕业设计场景的完整项目资料主题为基于Python爬虫的网络小说数据分析系统适合需要完成毕设、课程设计或想练习前后端与数据分析全链路开发的学习者。项目前台提供作者作品、分类占比、小说名称与分类统计等展示后台涵盖系统首页、个人中心、用户管理、网络小说与系统公告等模块技术栈涉及Python爬虫、数据分析、可视化与MySQL数据库。压缩包共415个文件约16.91MB以45个py源码、42个vue前端组件、34个js脚本、1个sql数据库文件及docx、md说明文档为主另含bat一键安装运行脚本便于快速部署。目前已有121人学习下载。资料包含源码、数据库、开发文档与LW可帮助读者理解爬虫抓取、Pandas数据处理、Matplotlib可视化及前后端联调思路也适合作为二次开发与功能扩展的参考模板。1. 从一堆 bat 脚本说起这套网络小说数据分析系统到底能跑出什么拿到这个压缩包的时候我第一反应不是看源码而是先翻根目录——安装全部.bat、init_sql.bat、运行.bat、1-install.bat、2-run.bat、3-build.bat还有main.js.bak和运行python程序.bat。这种把安装、初始化、启动拆成独立脚本的做法在毕业设计类项目里其实挺少见多数人只会丢一个 README 让你自己猜。它解决的核心问题很明确用 Python 爬虫抓网络小说数据落进 MySQL再做分类占比、作者作品分布这类统计最后用可视化把结果摊开给前台用户看后台则交给管理员管用户、管公告、管小说条目。适合谁正在做数据分析方向毕设、想找一个能跑通「爬虫→存储→分析→可视化」全链路的参考项目的人以及需要一套前后端都能改的模板来二次开发的人。它不是一个玩具 demo脚本分层说明作者至少在自己机器上反复跑过。2. 环境与依赖把 Python、MySQL 和前端工具链一次装对2.1 为什么这套项目对版本这么敏感网络小说数据分析系统横跨三块Python 爬虫与分析、MySQL 存储、前端页面。这三块各自有版本脾气凑在一起就容易出玄学问题。Python 这边requests抓页面、pandas做分组统计、matplotlib/seaborn出图如果 pandas 是 2.x 而代码里还写着df.append()直接报 AttributeError因为 2.0 已经把这个方法删了。MySQL 这边8.0 默认认证插件是caching_sha2_password而老一点的mysql-connector-python或pymysql不认连接时甩一个 Authentication plugin cannot be loaded。前端这边main.js.bak这个备份文件暗示主入口被改过如果 node 版本和依赖锁不一致npm run build会卡在依赖解析。我一般会先把版本钉死再动手而不是装完报错再回头降级。下面这套组合是我在 Windows 上跑通后记下来的不是唯一解但踩坑最少。组件建议版本说明Python3.9 ~ 3.113.12 部分科学计算轮子还没跟上MySQL8.0.x注意认证插件下面会给改法pandas1.5.x避开 2.x 的 API 删除requests2.28爬虫基础库matplotlib / seaborn3.6 / 0.12可视化出图Node.js16.x LTS前端构建别上太新的2.2 安装脚本拆开看每个 bat 到底干了什么项目把安装拆成1-install.bat和安装全部.bat前者大概率是装 Python 依赖后者是把 Python 依赖加前端依赖一起装。我不建议直接双击安装全部.bat就完事因为一旦中间某步失败窗口一闪而过你根本看不到错在哪。正确姿势是打开 cmd逐个手动执行把输出留在屏幕上。# 先建虚拟环境别污染全局 Python python -m venv venv venv\Scripts\activate # 升级 pip老 pip 装某些轮子会失败 python -m pip install --upgrade pip # 装 Python 依赖如果项目有 requirements.txt 就走它 pip install -r requirements.txt # 如果没有 requirements.txt按项目技术栈手动装 pip install requests pandas numpy matplotlib seaborn pymysql flask逻辑说明虚拟环境是为了让这套项目的依赖和你机器上其他项目隔离避免 pandas 版本打架。pymysql是纯 Python 实现的 MySQL 驱动比mysql-connector少一些编译依赖在 Windows 上更省心。参数上如果你要用 SQLAlchemy 做 ORM 存储爬虫数据再加一个pip install sqlalchemy这是现在比较主流的做法比裸写 SQL 好维护。前端依赖在项目根目录或前端子目录里通常是npm install。如果安装全部.bat里调用了 npm你要先确认 node 和 npm 在 PATH 里否则脚本会静默失败。# 进入前端目录按实际结构调整 cd frontend npm install # 如果依赖锁冲突用这个清一遍再装 npm cache clean --force npm install2.3 MySQL 初始化init_sql.bat 背后的两件事init_sql.bat干的事一般就两件建库、导表结构和初始数据。但它不会帮你改认证插件所以很多人卡在连接这一步。先手动确认 MySQL 服务在跑再用 root 登录建库。-- 建库字符集用 utf8mb4网络小说里有生僻字和 emoji CREATE DATABASE novel_analysis DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 如果驱动报认证插件错误把用户认证方式改回兼容模式 ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;逻辑说明utf8mb4是必须的网络小说标题和作者名里出现特殊字符的概率不低用utf8会在插入时截断报错。mysql_native_password这步是给老驱动兜底的改完记得FLUSH PRIVILEGES让权限生效。导入表结构时如果init_sql.bat里写的是source xxx.sql你要确保 sql 文件路径没有中文和空格否则 mysql 命令行会解析失败——这是血泪经验路径带空格时它会把后半段当成另一个参数。3. 爬虫与数据落库requests 抓取到 SQLAlchemy 存储的完整链路3.1 爬虫部分为什么用 requests 而不是框架网络小说数据分析系统的爬虫目标通常是小说列表页和详情页抓的是书名、作者、分类、简介这类结构化程度较高的字段。这种场景用requestsBeautifulSoup或lxml就够了上 Scrapy 反而重。项目正文里提到 Python 爬虫但没指定框架常见做法就是 requests 系。核心要注意的是请求头、翻页逻辑和限速不然要么被拒要么抓一半断掉。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, Referer: https://example.com/, # 按目标站点填 } def fetch_page(url, retry3): for i in range(retry): try: resp requests.get(url, headersHEADERS, timeout10) resp.encoding resp.apparent_encoding # 防止中文乱码 if resp.status_code 200: return resp.text except requests.RequestException as e: print(f第{i1}次失败: {e}) time.sleep(random.uniform(1, 3)) return None def parse_list(html): soup BeautifulSoup(html, lxml) items [] for node in soup.select(.book-item): # 选择器按实际页面改 items.append({ title: node.select_one(.title).get_text(stripTrue), author: node.select_one(.author).get_text(stripTrue), category: node.select_one(.category).get_text(stripTrue), }) return items逻辑说明apparent_encoding是根据内容猜编码比死写utf-8稳中文站点经常是 gbk。重试机制里加了随机 sleep这是最基本的限速避免请求过密。select里的类名是占位你要用浏览器开发者工具对着真实页面改这是爬虫最容易翻车的地方——页面结构一变选择器全废。3.2 用 SQLAlchemy 把爬到的数据写进 MySQL热词里sqlalchemy储存爬虫数据是有道理的裸写 INSERT 在字段多的时候很容易拼错。用 ORM 定义模型字段类型和表结构对齐插入时也顺手。from sqlalchemy import create_engine, Column, Integer, String from sqlalchemy.orm import declarative_base, sessionmaker Base declarative_base() class Novel(Base): __tablename__ novel id Column(Integer, primary_keyTrue, autoincrementTrue) title Column(String(255), nullableFalse) author Column(String(128)) category Column(String(64), indexTrue) # 分类要统计加索引 engine create_engine( mysqlpymysql://root:你的密码localhost:3306/novel_analysis?charsetutf8mb4, echoFalse, pool_pre_pingTrue, # 防止连接空闲后失效 ) Base.metadata.create_all(engine) Session sessionmaker(bindengine) def save_items(items): session Session() try: for it in items: session.add(Novel(**it)) session.commit() except Exception as e: session.rollback() print(f入库失败: {e}) finally: session.close()逻辑说明连接串里charsetutf8mb4必须带否则中文写入会出问题。pool_pre_pingTrue是让连接池在取连接前先 ping 一下避免 MySQL 8 小时空闲断连后报 MySQL server has gone away。category加索引是因为后面分类占比统计要按它 group by数据量上来后没索引会明显变慢。session.rollback()是后悔药批量插入中途失败时不清空会污染后续操作。3.3 数据分析与可视化分类占比怎么算、图怎么出前台要展示分类占比、作者作品分布本质就是 pandas 的groupby加value_counts再用 matplotlib 或 seaborn 出图。这一步是把数据库里的原始数据变成能看的结论。import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from sqlalchemy import create_engine engine create_engine(mysqlpymysql://root:你的密码localhost:3306/novel_analysis?charsetutf8mb4) df pd.read_sql(SELECT title, author, category FROM novel, engine) # 分类占比 cat_counts df[category].value_counts() print(cat_counts) # 出饼图 plt.rcParams[font.sans-serif] [SimHei] # 中文显示 plt.rcParams[axes.unicode_minus] False cat_counts.plot.pie(autopct%1.1f%%, figsize(8, 8)) plt.title(网络小说分类占比) plt.ylabel() plt.savefig(category_pie.png, dpi150, bbox_inchestight) # 作者作品数 Top10 top_authors df[author].value_counts().head(10) sns.barplot(xtop_authors.values, ytop_authors.index) plt.title(作者作品数量 Top10) plt.savefig(author_top10.png, dpi150, bbox_inchestight)逻辑说明read_sql直接把查询结果转 DataFrame省去手动拼列表。中文显示那两行是必须的不然图里全是方框这是 matplotlib 在 Windows 上的经典坑。bbox_inchestight防止保存时标签被裁掉。饼图适合看占比条形图适合看排名别用饼图展示 Top10超过 6 个扇区就没法看了。4. 前后端联调与启动run.bat 跑不起来时先查这四处4.1 后端接口和前端请求怎么对上项目是前后端结构前端发请求、后端返回 JSON。联调阶段最常见的问题是跨域和端口不一致。后端如果跑在 5000前端 dev server 跑在 8080浏览器会拦跨域请求。常见做法是后端加 CORS 头或者前端配代理。# Flask 后端加 CORS开发阶段图省事 from flask import Flask, jsonify from flask_cors import CORS app Flask(__name__) CORS(app) # 允许跨域生产环境要收紧到具体域名 app.route(/api/category_stats) def category_stats(): # 这里调用前面的 pandas 统计逻辑 return jsonify({data: cat_counts.to_dict()})逻辑说明CORS(app)是开发期最快解法但它对所有来源开放上线前要改成CORS(app, origins[http://你的域名])。接口返回用jsonifypandas 的 Series 要先to_dict()转成可序列化结构直接返回 Series 会报 Object of type Series is not JSON serializable。4.2 启动顺序和脚本对应关系2-run.bat和运行.bat大概率是启动后端3-build.bat是前端构建。顺序错了就连不上先起 MySQL再起后端最后起前端。如果运行python程序.bat里写的是python app.py你要确认当前目录下有app.py而且虚拟环境已激活否则会用全局 Python 跑依赖找不到。# 完整启动顺序 # 1. 确认 MySQL 服务在跑 net start mysql80 # 2. 激活虚拟环境后启动后端 venv\Scripts\activate python app.py # 3. 另开一个窗口启动前端 cd frontend npm run serve逻辑说明net start mysql80里的服务名按你实际安装的版本改可能是mysql或MySQL80。后端和前端要开两个终端窗口别想着一个窗口全搞定。如果前端npm run serve报端口占用改vue.config.js里的 port或者杀掉占用进程。5. 避坑与排查这套项目最容易翻车的五个地方5.1 现象init_sql.bat 一闪而过数据库没建起来原因脚本里用了相对路径找 sql 文件双击运行时工作目录不是脚本所在目录导致找不到文件或者 MySQL 没加进 PATHmysql命令根本不认识。解决不要双击右键用管理员身份打开 cmdcd到脚本所在目录再执行。确认mysql --version能输出版本号不能的话把 MySQL 的 bin 目录加进系统环境变量。5.2 现象爬虫跑一会就报连接错误或返回空原因请求太密被目标站点限流或者 User-Agent 被识别也可能是页面结构和你写的选择器不匹配。解决加大 sleep 区间换 User-Agent必要时加代理池这里只提思路具体方案自行评估合规性。选择器用浏览器 F12 重新核对页面改版是常态别指望一套选择器用到底。5.3 现象中文写入数据库变成问号或乱码原因建库时字符集不是 utf8mb4或者连接串没带 charset或者表字段排序规则不一致。解决建库、建表、连接串三处字符集统一成 utf8mb4。已经建错的库用ALTER DATABASE novel_analysis CHARACTER SET utf8mb4;改表也要单独改。5.4 现象前端页面能打开但数据全是空的原因后端接口没起或者前端请求的 baseURL 指向了错误端口或者跨域被拦。解决浏览器 F12 看 Network 面板请求是不是 404 或 CORS 报错。404 查后端路由CORS 报错按 4.1 加跨域配置。baseURL 一般在main.js或 axios 配置文件里main.js.bak可能就是改坏之前的备份可以对比看差异。5.5 现象matplotlib 出图中文全是方框原因默认字体不含中文且没关掉 unicode_minus。解决plt.rcParams[font.sans-serif] [SimHei]和plt.rcParams[axes.unicode_minus] False两行都加上。Linux 上没有 SimHei 就换成WenQuanYi Micro Hei之类已安装的中文字体。6. 进阶把分析结果做成可复用的接口与定时任务跑通之后这套项目真正的价值在于你能不能把它从「一次性脚本」变成「可持续跑的数据服务」。我一般会做两件事把统计逻辑抽成独立函数再挂一个定时任务定期刷新数据。# stats_service.py 把统计逻辑抽出来接口和定时任务都能调 import pandas as pd from sqlalchemy import create_engine ENGINE create_engine(mysqlpymysql://root:你的密码localhost:3306/novel_analysis?charsetutf8mb4) def get_category_stats(): df pd.read_sql(SELECT category FROM novel, ENGINE) counts df[category].value_counts() return { labels: counts.index.tolist(), values: counts.values.tolist(), total: int(counts.sum()), } def get_author_ranking(top_n10): df pd.read_sql(SELECT author FROM novel, ENGINE) top df[author].value_counts().head(top_n) return {authors: top.index.tolist(), counts: top.values.tolist()}逻辑说明把read_sql和统计逻辑收进函数接口层只负责调函数返回 JSON定时任务也只管调函数写缓存。这样改统计口径时只动一处。返回结构里带上total前端算百分比时不用再请求一次总数。定时刷新用schedule或系统计划任务都行核心是别在每次用户请求时都全表扫一遍数据量大了响应会很难看。import schedule import time from stats_service import get_category_stats def refresh(): stats get_category_stats() # 写入缓存表或 Redis接口优先读缓存 print(f刷新完成共 {stats[total]} 条) schedule.every(6).hours.do(refresh) while True: schedule.run_pending() time.sleep(60)逻辑说明6 小时是个折中值网络小说数据变化没那么快没必要实时。run_pending加sleep是最朴素的调度循环生产环境用系统的 cron 或计划任务更稳进程挂了还能自动拉起。验证方法很简单手动改一条数据库里的分类等定时任务跑完看接口返回有没有变。如果没变先查定时任务进程在不在再查缓存有没有被覆盖。我踩过一次坑缓存写进去了但接口读的是旧 key排查了半天才发现是 key 拼写不一致。从那以后我每次加缓存都强制走一遍「改数据→等刷新→验接口」的闭环不再凭感觉认为它生效了。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网