1. 为什么选这个组合DjangoVue.js爬虫推荐一条龙毕设的架构拆解每年到毕设选题季都有大量同学在技术栈怎么选上纠结一两个月。前端要好看、后端要能扛事、还得有亮点最好还能塞进一点大数据元素——结果往往是贪多嚼不烂最后做出来个四不像。我这个小说推荐系统项目选择 DjangoVue.js 的组合不是头脑发热而是把毕设的评分点拆开之后一项项对上的。先看后端。Django 自带 Admin 后台、ORM、认证体系和模板引擎一个小说系统里最常见的用户注册登录、书籍管理、评论收藏这些功能Django 几乎是开箱即用。它的 ORM 对新手极其友好你不需要写原生 SQL 就能完成大部分增删改查这在毕设时间紧张的情况下非常关键。而且 Django 的 ecosystem 成熟用django-rest-framework写 API 接口比 Flask 手撸序列化省一半功夫。再看前端。Vue.js 的核心优势是响应式数据绑定和组件化开发。做小说推荐系统这种页面交互多的项目左侧分类、中间推荐列表、右侧热榜每个模块拆成独立组件数据一变页面自动刷新开发体验比 jQuery 时代舒服太多。搭配 Element UI 组件库表格、表单、分页器、标签这些现成的样式直接拿来用半小时就能把后台管理页面的框架搭出来。至于小说爬虫和数据可视化这俩是这个项目的加分项。很多同学的毕设就是简单的增删改查做得再熟练也拿不到高分。爬虫模块证明了你有数据采集和清洗的能力可视化大屏则直接展示了你的数据呈现能力推荐算法更是给大数据这个关键词一个落地支撑。一套组合下来开题报告、中期检查、答辩PPT都有内容可写。1.1 前后端分离的项目骨架如何搭建项目代码仓库建议直接分成三个目录backend/、frontend/、spider/。这样在论文里写模块划分时也清楚答辩时老师一眼就能看出你的工程化思维。后端我用 Django 3.2 Django REST Framework 搭建Python 3.8 环境。创建虚拟环境后装依赖然后新建一个名为novel_api的独立 App专门负责提供 RESTful API另一个userApp 管用户认证novelApp 管小说数据recommendApp 管推荐逻辑。这样划分的好处是后续写推荐算法时不用在视图函数里堆一堆业务代码直接调recommend.services里的函数就行。前端用 Vue CLI 创建一个frontend项目配好 Vue Router 和 Vuex状态管理。开发阶段前后端通过代理连接在vue.config.js里配置devServer.proxy把/api开头的请求转发到 Django 起的127.0.0.1:8000这样就不会有跨域问题本地联调非常顺。// vue.config.js module.exports { devServer: { proxy: { /api: { target: http://127.0.0.1:8000, changeOrigin: true } } } }生产部署时再让 Nginx 统一代理前后端不过毕设阶段跑通开发模式就够了没必要折腾部署。1.2 数据模型的合理设计小说系统的核心数据表并不复杂关键在于表之间的关系设计。我的数据模型分四块用户表Django 自带的User扩展、小说表Book、分类表Category、用户行为表收藏、阅读记录、评分。推荐算法需要用到用户行为数据所以Rate表是重中之重。Book表的字段我建议这样设计title书名索引字段爬虫下载后写入author作者category外键指向Category表intro内容简介推荐算法做文本相似度时会用word_count字数用于可视化统计status连载状态连载中/已完结cover_url封面图链接score爬虫带回来的初始评分后面会被用户评分修正Rate表记录用户对小说的评分和互动这是协同过滤算法的输入数据。字段包括user外键、book外键、rating整型1-5分、timestamp时间戳。光是这个表在后面扩展基于贝叶斯平均的评分修正时也很有用捡了芝麻后面还能开西瓜。不要小看这个数据库设计推荐算法的效果好不好七成取决于你有没有把用户行为数据存全、存干净。很多人做毕设推荐系统最后跑出来的结果离谱根源就是行为数据表太简陋。2. 小说数据怎么来爬虫模块从设计到落地的完整链路爬虫是这个项目最有意思的部分也是最容易翻车的地方。我的目标非常明确采集公开的小说元数据书名、作者、简介、分类、字数、评分用于填充数据库和做可视化。不碰VIP章节内容不去破解任何付费/会员限制只抓免费公开展示的信息。2.1 目标站点分析与爬取策略先确定目标站点找那种结构清晰、反爬策略比较温和的网站。打开目标站点的分类页按 F12 看网络请求确认页面是服务端渲染还是 AJAX 动态加载。这一步直接决定你是用requests BeautifulSoup解析 HTML还是用requests直接请求 JSON 接口。我当时的做法是优先找 JSON 接口。原因很现实——解析 JSON 比解析 HTML 稳定得多HTML 结构一变你的解析代码就废了而 JSON 接口通常字段固定维护成本低。比如很多小说站点的分类页数据其实是通过一个/api/category/list接口返回的直接伪造一个 User-Agent 请求就能拿到完整的书籍列表。爬取策略分三层分类页列表数据拿到每本书的详情页 URL 和基础信息。详情页数据逐个请求详情页解析出简介、字数、状态等元数据。增量更新定期重新爬取发现新书就 INSERT发现字数变化就 UPDATE。import requests from bs4 import BeautifulSoup HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://example.com/ } def get_book_detail(book_url): resp requests.get(book_url, headersHEADERS, timeout10) resp.encoding utf-8 soup BeautifulSoup(resp.text, html.parser) # 以具体网站结构调整选择器 title soup.select_one(h1).text.strip() intro soup.select_one(.intro).text.strip() word_count soup.select_one(.word_count).text.strip() return { title: title, intro: intro, word_count: word_count, }2.2 数据清洗与入库的细节爬虫拿到的数据是脏的。数值字段经常是123.45万字这种带单位的中文字符串评分可能是8.7分连载状态可能是连载中1234章。这些都要在入库前统一格式化。我写了一个clean_data.py模块专门干三件事类型转换把123.45万字解析成纯数字int(12345)单位统一为千字存库方便后面可视化时做数值统计。去重按title author作为唯一键重复的书跳过不插入。空值处理简介为空的书直接丢弃没有评分的用网站的默认评分兜底。清洗逻辑直接决定后面的统计分析能不能做。如果你想做各分类小说平均字数对比这种可视化word_count字段是字符串根本没法计算还得回头清洗浪费时间。2.3 爬虫的合规和反爬处理这里必须说一句不太中听但很重要的话爬虫项目的核心能力不在于多能爬而在于知道什么不该爬。毕设阶段你的目的是展示数据采集和处理的完整链路不是去做一个商业爬虫平台。所以我在代码里刻意控制爬取频率每次请求间隔 3-5 秒单日采集量控制在几千条以内绝不并发轰炸目标站点。反爬方面我只做了最基础的伪装随机 User-Agent、Referer 补齐、偶尔用代理池。对于需要登录才能看的内容一律不碰。这里要提醒一下如果目标网站有robots.txt文件建议先看一眼尊重网站的爬取约定这既是法律意识也是职业习惯。2.4 爬下来的数据怎么验证数据入库后不能直接开跑必须有验证环节。我的做法是随机抽 50 条数据和源站人工比对核对书名、作者、简介字段是否有偏差。另外用一个SELECT COUNT(*)查询检查每个分类的书籍数量分布如果某个分类数量异常比如玄幻类突然有 10 万本其他分类只有 100 本基本可以断定解析时分类字段匹配出了 bug。这一步虽然繁琐但能让你的项目经得起答辩追问。答辩老师经常会问你爬了多少数据数据质量怎么保证你如果只回答爬了 5000 本那是送命题如果你能补充一句我抽了 50 条人工核对正确率 100%并且每本书都有唯一键去重基本的专业性就立住了。3. 推荐系统实现让猜你喜欢真正动起来的核心逻辑推荐算法是整个项目里最值得写进论文的核心章节也是拉开分数差距的地方。很多毕设项目写推荐系统就是简单的按评分排个序那不叫推荐系统叫排行榜。真正的推荐系统必须基于用户行为数据做个性化计算。3.1 三种常见推荐方案的对比选型我梳理了三种在毕设里最常见的方案基于内容的推荐、协同过滤推荐、混合推荐。基于内容推荐的关键是根据小说本身特征分类、标签、简介关键词计算相似度。优点是冷启动友好新书也能被推荐缺点是推荐结果同质化严重翻来覆去都是同一类。协同过滤推荐又分两种基于用户的UserCF和基于物品的ItemCF。UserCF 是和你兴趣相似的人也在看这本书ItemCF 是看了这本书的人还看了那些书。毕设场景里我推荐优先做 ItemCF原因是用户量通常不大基于用户的相似度计算容易遇到矩阵稀疏问题而 ItemCF 在物品数量固定的情况下计算更稳定。考虑毕设的演示效果我最终选择了混合推荐基础排序用 ItemCF冷启动用户用基于内容的推荐 热门榜兜底。这样无论老师在演示时创建新用户还是老用户登录页面都不会空。3.2 基于物品协同过滤的完整实现ItemCF 的核心就两步先建立用户-物品评分矩阵再计算物品间的相似度。评分矩阵我从Rate表里查出来存成一个二维结构然后计算物品相似度矩阵。物品相似度公式有很多种我用的是余弦相似度的变体——皮尔逊相关系数。核心思路是如果两本书被同一批用户喜欢过那么它们之间的相似度就高。import numpy as np from sklearn.metrics.pairwise import cosine_similarity def build_item_similarity(rate_matrix): # rate_matrix: 用户-物品评分矩阵行为用户列为物品 # 填充未评分项为 0计算物品间余弦相似度 item_sim cosine_similarity(rate_matrix.T) return item_sim def recommend_by_itemcf(user_id, top_n10): # 1. 取出该用户的评分记录找到已评分物品列表 # 2. 对每个已评分物品取相似度最高的 K 个未读物品 # 3. 用相似度加权评分得到预测分排序取 TopN pass这个算法的关键是我在注释里写的三步。实际实现时加权预测分的计算公式长这样预测分 SUM(物品i的相似物品j的相似度 * 用户对j的评分) / SUM(相似度)如果你用纯 Python for 循环遍历所有物品算相似度数据量到 5000 本书时就会明显卡顿。这里的性能优化我用的是 NumPy 矩阵运算一次性算完比循环快几百倍。这也是一个非常好的论文素材点——基于矩阵分解的推荐性能优化。3.3 冷启动问题的处理策略冷启动分两种情况新用户和新书。新用户没有任何评分记录协同过滤直接失效。我这里的做法是让前端在注册时勾选感兴趣的分类标签后端根据标签从新书/高分书中抽取推荐列表给用户第一次点击的机会。新书没有评分数据就用爬虫携带的初始评分和历史热门数据兜底。这个逻辑不复杂但很加分。答辩时老师问如果数据库里只有 100 条评分记录怎么办你能清晰说出冷启动处理方案说明你真的思考过系统鲁棒性这在毕设评分里是亮点。3.4 推荐结果的评分与排序推荐结果不能是随机堆给你的必须有一个可解释的排序规则。我最终的排序策略是先按预测分数降序排列。分数相同时优先推荐评分人数多的书说明是大众认可的。已经看过的书从结果集里排除。预测分数我做了基于贝叶斯平均的修正处理这是从热词里联想到的一个亮点。现实场景中一本只有 1 个人打 10 分的书不应该排在 100 个人打了 8.5 分的书前面。用贝叶斯平均公式把评分往平均分方向收缩修正评分 (平均分 * 总评次数阈值 该书评分 * 该书评次数) / (总评次数阈值 该书评次数)这个公式在论文里写出来导师一看就知道你做过功课不是随便拿个算法凑数。而且在答辩现场你可以直接现场演示把一本1 人打分 10 分的书和一本300 人打分 8.9 分的书放进推荐池看排序结果的变化非常有说服力。4. 小说可视化大屏从数据表到一看就懂的可视化面板可视化是本项目的门面也是大数据这个关键词最直观的体现。小说推荐系统本身是一个功能闭环但你要在毕设答辩的几分钟里让评委看懂你的数据有什么价值可视化大屏是最省力气的表达方式。4.1 可视化选型ECharts 还是其他方案可视化方案我对比了几种直接写原生 Canvas/SVG开发量大、Highcharts商用需授权、D3.js学习曲线陡峭、ECharts开源免费、中文文档全。最终的结论是 ECharts没有悬念。ECharts 对毕设项目的优势非常明显它内置了十几种图表类型折线图、柱状图、饼图、雷达图、词云都有现成模板响应式适配做得好电脑和投影仪上展示都不变形社区资源多你搜任何图表类型基本都能找到完整 Demo。4.2 核心图表的实现细节我的可视化大屏设计了六个图表模块每个模块对应一种分析维度图表类型分析维度数据口径柱状图各分类小说数量分布GROUP BY category折线图近30天新书入库趋势按发布时间统计饼图连载状态占比连载中/已完结词云小说标签高频词从标签表提取雷达图各分类平均字数/评分对比多维统计排行榜综合评分 Top10 小说贝叶斯修正后排名具体实现时不需要写一堆 ECharts 配置项把它封装成一个visualize.js工具函数统一从后端 API 拉数据再配置不同的option对象。// 饼图连载状态占比 const pieOption { tooltip: { trigger: item }, series: [{ type: pie, radius: [40%, 70%], data: pieData // 从 /api/stats/status 获取 }] };数据对接方面我在 Django 后端写了一个统计类视图直接用 ORM 的annotate和aggregate做聚合查询返回 JSON 给前端。这里的核心技巧是任何可视化图表的数据都应该由后端聚合好再返回而不是把全表数据丢给前端去算。你见过那种打开大屏页面加载 5 秒以上的毕设项目吗基本都是因为后端把所有数据返回了前端用 JS 挨个算慢得离谱。4.3 大屏数据对接 API 的优化技巧这里有一个实际优化过的细节ECharts 的词云图wordCloud组件对数据格式有苛刻要求标签字段必须是name和value。但我后端返回的字段叫label和count前端如果不做映射图表直接空白。我统一封装了一个transformToWordCloud(data)函数做字段转换并把加载失败的空状态提示也做好了演示时不至于翻车。另外一个经验所有图表的数据请求用Promise.all并发发出去然后统一渲染。如果你一个个请求串行执行四个图表的初始化时延加起来可能超过 2 秒现场演示非常尴尬。5. 大数据视角这个毕设里的大数据体现在哪说实话一个小说推荐系统动辄几万条数据离真正的大数据量级还很远。但毕设题目里写了大数据你就必须向这个方向靠拢至少要有大数据思维的体现。我的理解是不在于数据量多大而在于你是否用了处理大数据的思路去设计系统。5.1 数据量级的真实情况与架构应对我当时爬了大约 8000 本小说元数据用户行为表有 2 万多条评分记录。这个量级 MySQL 轻轻松松就能扛住但你写论文时不能只写用 MySQL 存储要描述你的架构能支持多少量级的数据增长以及当前设计的瓶颈在哪里。我论文里的表述方式是系统当前基于单机 MySQL 和 Django ORM 实现在万级数据量下性能良好当数据规模突破百万条时推荐计算模块可无缝切换到 Spark 进行批处理ORM 层替换为 Hive/Spark SQL 查询。这种表述的高明之处在于你坦然承认当前实现是单体架构但已有的抽象接口推荐算法独立成 service 模块、数据访问层全部走 ORM具备替换为大数据组件的可能性。5.2 推荐计算中的性能优化前面提到 ItemCF 的相似度计算用 NumPy 矩阵运算这个优化思路就是大数据领域的核心理念——把计算从循环迭代转为矩阵并行操作。我实测在 8000 本书的数据规模下原生 Python 循环算相似度需要 40 多秒改成 NumPy 后 2 秒内出结果这个性能对比数据建议写进论文的实验章节非常有说服力。另一个性能优化是热门推荐结果缓存。每天的推荐榜用户请求量大但计算结果变化缓慢我用 Django Cache 框架把 Top100 推荐结果缓存 30 分钟把推荐接口的响应时间从 700ms 降到了 40ms 左右。这也是大数据处理中典型的计算向存储转移策略。5.3 如果想让大数据成分更浓可以从这些方向扩展如果你的导师对大数据的要求更严格可以考虑这几个扩展方向数据采集层升级用 Scrapy 分布式爬虫替代简单 requests 脚本支持多节点协同采集。论文里写基于 Scrapy-Redis 的分布式爬虫设计。存储层升级把用户行为数据从 MySQL 迁移到 HBase 或 ClickHouse用列式存储承载高并发写入。计算层升级用 Spark MLlib 里的 ALS 交替最小二乘算法替代自写的 ItemCF用 PySpark 处理推荐模型的训练。调度层升级用 Airflow 编排每日爬虫任务、数据清洗任务和推荐模型更新任务。这些扩展不一定要全做哪怕只做一个实验性演示比如用 PySpark 在本地跑通一个 5000 条数据的 ALS 训练过程论文里就可以理直气壮地写系统支持 Spark 计算引擎本文实现了 ItemCF 与 ALS 双路推荐。6. 毕设避坑清单从开发到答辩经常翻车的点这部分是血泪经验总结。我在整个项目开发过程中踩了不少坑有些坑真的是写代码前根本想不到的这里一个个列出来给你省点时间。6.1 前后端联调中最常见的5个坑第一个坑是Django 的 CORS 跨域。本地开发时前端在 localhost:8080后端在 localhost:8000直接 AJAX 请求必然跨域。解决办法是在后端安装django-cors-headers在settings.py里配置CORS_ALLOW_ALL_ORIGINS True仅限开发环境。如果用了代理就不需要这个但保险起见全配上。第二个坑是Vue 的history模式刷新后 404。Vue Router 默认使用hash模式如果你改成history模式开发服务器刷新页面时可能 404。简单做法是用默认的 hash 模式虽然 URL 上有#符号但毕设不要求优雅 URL别在这上面浪费时间。第三个坑是Django 的 STATICFILES。把封面图、前端静态资源部署到 Django 时static文件路径经常配错导致图片不显示。我踩过很深的一次坑就是vscode里写的 img 标签引用的图片在浏览器里死活加载不出来排查半天发现是 STATIC_URL 配置不对。这个问题的解决办法是写模板时使用{% static images/xxx.jpg %}标签不要手写硬编码路径。第四个坑是时间格式序列化。Django DateTimeField 返回的是datetime对象直接 JSON 序列化会报错。Django REST Framework 默认能处理但如果你手写 JsonResponse 就会翻车。我统一用 DRF 的 Serializer 处理所有 API 输出规避了这个坑。第五个坑是Vue 组件里的this指向。在methods里写setTimeout回调或者axios.then回调时this指向会丢失拿不到this.books。解决办法是回调函数用箭头函数写或者在外部缓存const that this。这个问题新手必踩答辩现场调试时会很尴尬提前写好就行。6.2 论文和文档的写作重点很多同学代码写完了论文憋不出来其实是没掌握结构。我的论文目录大致是第一章 绪论背景、意义、国内外研究现状第二章 相关技术介绍Django、Vue.js、ECharts、推荐算法原理第三章 需求分析与系统设计用例图、架构图、数据库设计第四章 系统实现爬虫模块、推荐模块、可视化模块、前后端实现第五章 系统测试功能测试、性能测试、推荐效果评估第六章 总结与展望重点发力在第四章每实现一个模块都配一张截图和技术说明。推荐算法的公式推导更是重中之重皮尔逊相关系数公式和贝叶斯平均公式必须完整呈现别只写用了协同过滤算法一句话。LW 文档也就是毕设配套的说明文档内容就是从论文精简来的设计说明书重点写数据库表结构和接口设计。6.3 答辩演示的注意事项答辩演示时切记三件事第一准备一个干净的数据集。不要用爬虫原始数据做演示里面可能有乱码、错别字、封面缺失等意外情况。我专门造了一个类别分布均衡、评分数据量充足的演示数据集保证推荐结果和可视化图表都是好看的状态。第二提前录好演示视频备用。现场演示的最大风险是投影仪分辨率不兼容、网络不通、数据库连不上。我提前用 OBS 把完整的系统演示录制成 10 分钟视频现场如果环境出问题就说我准备了演示视频先看下实际效果反而显得准备充分。第三准备好三个深度问题的答案。老师最可能问的问题我总结成了三个推荐算法为什么选 ItemCF 不选 UserCF答数据稀疏性和计算稳定性爬虫采集的合规性怎么保证答只采集公开数据、控制频率、尊重 robots.txt系统如何扩展支持更大数据量答接口抽象 Spark 替换方案。把这三个问题的答案写熟答辩环节基本就稳了。7. 项目目录结构与源码组织建议最后分享一个非常实用但大多数同学会忽视的点源码的目录组织方式。很多人的毕设代码打开一团乱麻models.py 里写 500 行views.py 里塞了一整页逻辑答辩老师打开 GitHub 仓库第一眼就印象分暴跌。我的目录结构是这样的novel-recommend-system/ ├── backend/ # Django 后端 │ ├── novel_api/ # 主工程配置 │ ├── user/ # 用户模块 │ ├── novel/ # 小说模块 │ ├── recommend/ # 推荐模块 │ ├── stats/ # 可视化统计模块 │ ├── spider/ # 爬虫模块 │ └── requirements.txt ├── frontend/ # Vue 前端 │ ├── src/ │ │ ├── views/ # 页面组件 │ │ ├── components/ # 通用组件 │ │ ├── api/ # 接口封装 │ │ └── router/ # 路由配置 ├── docs/ # 论文、LW文档、PPT ├── data/ # 爬虫数据备份 └── README.mdREADME.md 不是摆设我用 20 分钟写了一个完整的项目启动说明环境版本、安装步骤、初始化命令、测试账号。这个文件在答辩时直接展示给导师看能极大提升项目完整性的印象。很多同学觉得 README 是开源项目才需要的毕设不用写但实际体验下来写一个清晰的 README 会让导师对你的工程素养刮目相看这比答辩时多吹两句代码质量有效得多。启动步骤记得写清顺序# 1. 创建并激活 Python 虚拟环境 python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate # 2. 安装后端依赖 pip install -r requirements.txt # 3. 数据库迁移与初始化 python manage.py makemigrations python manage.py migrate python manage.py init_data # 导入爬虫数据 # 4. 启动后端服务 python manage.py runserver # 5. 启动前端另开终端 cd frontend npm install npm run serve依赖版本锁定是另一个容易忽略的点。Django 3.2 和 Django 4.2 的某些 API 有区别Vue 2 和 Vue 3 的语法差异更大。如果requirements.txt不锁版本你换台电脑跑项目时可能装到最新版然后各种报错在上交代码前两天心态直接崩掉。我当时锁的是Django3.2.18、djangorestframework3.14.0、vue2.6.14这套组合现在依然能完美运行。个人实际体会是小说推荐系统这个毕设选题难度不低但天花板也不低。爬虫、推荐、可视化三个模块单独拎出来任何一个都可以写一篇小论文组合在一起就是一个技术栈完整、有算法深度、有可视化呈现的三位一体项目。最关键的是这套技术栈和对问题的处理思路冷启动、数据清洗、性能优化、贝叶斯修正放到真实工作中也是通用的底层能力。你做完这个项目不只是完成了一项毕业任务实际上是走通了一条原始数据—加工处理—算法建模—可视化呈现的完整数据流水线这种思维方式在后续的工作或考研项目中都会持续复用。