ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Django新闻推荐系统毕业设计:选题、算法、WebSocket与答辩实战

Django新闻推荐系统毕业设计:选题、算法、WebSocket与答辩实战 做计算机毕业设计,选题是第一步也是最容易卡壳的地方。如果让我给你排一个推荐榜单,基于Django的新闻推荐系统这个题目绝对能排进前三。它踩中了信息流时代的真实痛点,又蹭上了推荐算法这个热门方向,更重要的是——技术栈足够成熟、社区资料多、工作量适中,非常适合单人在半年内独立完成。我见过太多同学选“XXX管理系统”,做着做着变成CRUD开发现场,答辩时老师问一句“你的系统没有核心算法,和培训班结业作业有什么区别?”就答不上来。而新闻推荐系统天然自带“算法”属性,再配合你熟悉的Web框架,既能展示工程能力,又能讲清楚推荐逻辑,属于典型的“进可攻退可守”选题。下面我从选题理由、系统设计、推荐算法、WebSocket实时推送、常见坑位到答辩准备,把我踩过的路和总结经验一次讲完。1. 为什么选这个题目:需求分析与选题逻辑1.1 新闻推荐系统的核心价值新闻阅读早就过了“编辑推什么你看什么”的阶段。现在的资讯平台每天产生上万条内容,用户面对的不是“信息匮乏”而是“信息过载”。推荐系统要做的事情,就是从海量新闻里,把用户最可能感兴趣的几条挑出来,放到首页或者“猜你喜欢”的位置上。这不是锦上添花,而是直接影响用户留存和阅读时长的关键模块。对毕业生来说,新闻推荐系统是一个极具“性价比”的载体。它涉及用户建模、内容分析、算法匹配这些推荐系统的核心环节,又不要求你有海量数据和分布式计算能力。你可以用Django自带ORM存用户行为,用Python的jieba分词做关键词提取,用余弦相似度计算新闻之间的相似性,再用Django模板或Vue/小程序做展示层。一套完整系统做下来,数据流、算法、前后端、部署、测试全都能覆盖到,而且每个环节都能在论文里展开写。1.2 为什么用Django而不是其他框架我知道你会纠结选Flask、FastAPI还是Django。这里我无脑站Django,原因很朴素:毕业设计不需要极致性能,需要的是开发效率和功能完整度。Django自带Admin管理后台、认证系统、ORM数据库映射、Form表单校验、中间件机制,这些功能在搭建新闻推荐系统时全都用得上。对比维度DjangoFlaskFastAPI开发效率极快,自带全套组件快,但需要自己拼装快,异步友好学习成本中(有约束)低中Admin后台自带,功能强大无无ORM自带,支持复杂查询需集成SQLAlchemy需集成SQLAlchemyRBAC权限自带PermissionGroup需自己写或装扩展需自己写或装扩展适合场景标准业务管理后台轻量API高并发API服务对于新闻推荐系统这种用户注册登录、行为记录、新闻管理、推荐结果展示全都要做的项目,Django的“全家桶”模式能帮你省下至少两周时间。而且网上关于Django的教程、问题解决方案非常多,你不会因为一个小报错卡住三天。说实话,我一开始也用过Flask做过一个小推荐demo,后来发现权限管理、分页、Admin审核新闻这些功能都得从零写,顿时觉得Django真香。1.3 毕业设计的评分点拆解很多同学不知道评委老师到底看什么。结合我多次参与毕业答辩旁听的经验,评分权重大致是:核心技术(算法与业务逻辑)30%、系统完整性(功能代码)30%、论文与答辩40%。也就是说,光把系统跑通只能拿及格分,真正拉开差距的是“你的推荐逻辑是什么,为什么这样设计,效果怎么样”。所以选题后第一步,不是急着敲代码,而是把推荐算法这条主线捋清楚。热门推荐解决冷启动,基于内容的推荐解决用户兴趣漂移,协同过滤解决发现新兴趣,三步混合构成一个完整的推荐架构。答辩时你能把这三个策略的触发条件和优缺点说清楚,就已经超过大半同学了。2. 系统整体设计与技术选型2.1 功能模块划分我习惯画一个功能列表来拆解系统,不画图是因为毕业设计文档里图太多反而容易暴露逻辑硬伤。一个完整的新闻推荐系统至少需要这几个模块:用户模块:注册、登录、登出、个人资料、偏好标签选择。新闻模块:新闻列表、详情页、分类检索、关键词搜索。行为采集模块:记录用户浏览、收藏、点赞、搜索等行为。推荐模块:热门推荐、内容相似推荐、协同过滤推荐、首屏个性化推荐。后台管理模块:新闻审核发布、用户管理、行为统计、推荐效果查看。每个模块对应Django中的一个app,比如users、news、behavior、recommend、admin_custom,app之间通过ORM和URL路由解耦。初学者最容易犯的错是一个app里塞进全部逻辑,导致后期改一个功能就要动全局代码。我当时把新闻和推荐拆成两个app,后面写算法的时候才不至于和视图耦合成一团乱麻。2.2 推荐算法选型:三种策略混合这里的核心是让推荐系统“先有用,再个性化”。我采用的是三段式混合策略:热门召回:根据新闻的浏览量、收藏次数、发布时间计算热度分,给新用户或未登录用户推荐。冷启动阶段没有行为数据,热门是唯一可用的信号。基于内容的推荐:对新闻标题和正文做分词,构建TF-IDF向量,计算新闻间余弦相似度。当用户浏览过某条新闻,就推荐与它最相似的若干条。基于用户的协同过滤:根据用户对新闻的评分矩阵(比如浏览记1分、收藏记3分、点赞记5分),找相似用户,把相似用户喜欢而当前用户未读的新闻推荐出来。三种策略不冲突。在Django里可以写一个推荐服务,依次按“行为数据是否充足、是否有相似新闻、是否有相似用户”来判断走哪条路。混合策略在论文里也好写,毕竟它对应了业界推荐系统的常规模块:召回、排序、混排。2.3 数据模型设计:Django ORM核心字段新闻推荐系统的数据模型要在第二周就定下来,不然后期加字段容易引发迁移地狱。我的核心模型大概长这样:from django.db import models from django.contrib.auth.models import AbstractUser class User(AbstractUser): # 扩展用户模型,加偏好字段 favorite_categories models.CharField(max_length255, blankTrue) avatar models.ImageField(upload_toavatar/, blankTrue) class Category(models.Model): name models.CharField(max_length50, uniqueTrue) description models.CharField(max_length200, blankTrue) class News(models.Model): title models.CharField(max_length200) content models.TextField() summary models.CharField(max_length500, blankTrue) category models.ForeignKey(Category, on_deletemodels.PROTECT) cover_image models.ImageField(upload_tonews/, blankTrue) tags models.CharField(max_length255, blankTrue) # 关键词标签 view_count models.IntegerField(default0) like_count models.IntegerField(default0) collect_count models.IntegerField(default0) is_active models.BooleanField(defaultTrue) # 软删除标记 published_at models.DateTimeField(auto_now_addTrue) class UserBehavior(models.Model): BEHAVIOR_TYPE ( (view, 浏览), (like, 点赞), (collect, 收藏), ) user models.ForeignKey(User, on_deletemodels.CASCADE) news models.ForeignKey(News, on_deletemodels.CASCADE) behavior models.CharField(max_length10, choicesBEHAVIOR_TYPE) score models.IntegerField(default0) # 行为对应的得分 created_at models.DateTimeField(auto_now_addTrue)有几个细节可能影响你后面写算法:Category的外键用了on_deletemodels.PROTECT,意思是如果分类下面还有新闻,就不允许直接删除分类,避免数据孤儿。这里用软删除字段is_active而不是直接删News记录,可以保留历史行为数据。用户行为表里加了score字段,方便后续构造评分矩阵。2.4 环境搭建与项目初始化初始化输入的命令已经是很成熟的东西,我直接给你一份“抄作业”清单:# 1. 创建虚拟环境 python -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate # 2. 安装依赖 pip install django # 这里建议装5.x pip install djangorestframework pip install channels pip install jieba pip install scikit-learn # 算TF-IDF和余弦相似度可能会用 # 3. 创建项目与app django-admin startproject news_recommend cd news_recommend python manage.py startapp users python manage.py startapp news python manage.py startapp behavior python manage.py startapp recommend python manage.py startapp chat # 后面做WebSocket推送用 # 4. 数据库迁移 python manage.py makemigrations python manage.py migrate # 5. 创建超级管理员 python manage.py createsuperuser这里出现了一个新手高频坑:django-admin startapp必须在项目根目录(manage.py所在目录)执行,否则app会创建到错误位置,或者在运行时会提示找不到模块。我当时初学Django时就把app建错了目录,结果vscode里引用local模块一直报错,排查了半天。小事,但值得记一笔。2.5 推荐算法选型的详细解释我说句大实话,有的同学一上来就想用深度学习做推荐,结果训练模型都跑不动。毕业设计讲究“说得清、跑得动、测得出”。开箱即用的策略是用scikit-learn的TfidfVectorizer和jieba分词。具体流程是:对新闻标题摘要做中文分词,去掉停用词,构造TF-IDF向量,然后计算余弦相似度。新闻总量几千条的规模,内存完全扛得住,计算也快,答辩时还能现场演示。如果是协同过滤,算法流程也不复杂:从UserBehavior表里取出用户和新闻的对应评分,构造稀疏矩阵,用皮尔逊相关系数或余弦相似度计算用户之间的相似度,再找topN相似用户,把那些用户喜欢且当前用户没看过的新闻加权求和取前15条。这个代码量在100行以内,核心逻辑约30行,论文里能画出清晰的算法流程图和公式。3. 核心功能实现:从用户登录到个性化推荐3.1 用户行为采集:埋点与记录推荐系统没有行为数据就是空壳。我在设计之初就明确:一切行为都要入库。记录行为不用在模板里写复杂JS,最直接的方式是在每个视图函数里调用一个通用函数。# behavior/utils.py def record_behavior(user, news_id, behavior_type): if not user.is_authenticated: return score_map {view: 1, like: 3, collect: 5} UserBehavior.objects.create( useruser, news_idnews_id, behaviorbehavior_type, scorescore_map.get(behavior_type, 1), ) # 同步更新新闻的计数,这里的F()避免并发覆盖 News.objects.filter(idnews_id).update( view_countdb.models.F(view_count) 1 )注意这里用了F()表达式,直接在数据库层面做原子操作。如果不加,在高并发情况下两条请求同时读出来的count一样,写回去就把更新互相覆盖了。这是生产环境才会遇到的问题,毕业设计写进论文里很加分。随后,你在新闻详情视图里调用record_behavior(request.user, news_id, view),在收藏视图里调用record_behavior(request.user, news_id, collect),用户行为数据就源源不断地进入了数据库。有一个小技巧:行为记录不要在推荐接口中同步写,因为推荐接口前端会轮询多次,再加上一次视图页访问又记录一条,会造成统计翻倍。更好的做法是在视图里判断,同一个用户对同一新闻的浏览行为允许短时间内(比如10分钟)只记一次。3.2 推荐引擎实现:基于内容的推荐这部分是核心中的核心。基于内容的推荐,我用“用户最近浏览的3条新闻”作为输入,为每条新闻找出最相似的10条,然后去重合并。# recommend/content_based.py import jieba import numpy as np from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity class ContentBasedRecommender: def __init__(self, news_list): # news_list: [(id, text)] self.news_list news_list self.vectorizer TfidfVectorizer(tokenizerself.tokenizer) def tokenizer(self, text): return [w for w in jieba.lcut(text) if len(w.strip()) 1] def build_matrix(self): corpus [text for _, text in self.news_list] self.tfidf_matrix self.vectorizer.fit_transform(corpus) def recommend(self, news_id, topn10): idx [i for i, (nid, _) in enumerate(self.news_list) if nid news_id][0] vec self.tfidf_matrix[idx] scores cosine_similarity(vec, self.tfidf_matrix).flatten() # 排除自身,取topn top_indices np.argsort(scores)[::-1][1: topn 1] return [self.news_list[i][0] for i in top_indices]这里有个原理要理解:TfidfVectorizer把每篇文本变成一个高维向量,维度是词表的长度,新闻之间相似度就是两个向量夹角的余弦值。余弦相似度范围在[-1,1],越接近1代表越相似。文本越长,TF-IDF对高频词的惩罚越明显,所以我们要在新闻摘要层面做文本拼接,而不是把几万字的正文全塞进去,否则计算量和噪声都会放大。3.3 协同过滤实现:找与你口味相同的人协同过滤的思路是“人以群分”。如果你和我都浏览了同样几条科技新闻,那我把最近读的一条前沿AI新闻推荐给你,你大概率也爱看。实现的关键是构造评分矩阵并计算用户相似度。# recommend/collaborative.py def build_user_item_matrix(): records UserBehavior.objects.values(user_id, news_id, score) from scipy.sparse import coo_matrix # 转换成矩阵,行是用户,列是新闻 rows [r[user_id] for r in records] cols [r[news_id] for r in records] data [r[score] for r in records] return coo_matrix((data, (rows, cols))) def user_similarity(user_id, matrix): import numpy as np target matrix.getrow(user_id).toarray() # 这里做简化:用余弦相似度计算所有用户与目标用户的相似度 norms np.sqrt(np.asarray(matrix.power(2).sum(axis1))).flatten() target_norm np.sqrt(np.sum(target * target)) sims matrix.dot(target.T).toarray().flatten() / (norms * target_norm 1e-8) return sims def recommend_by_collaborative(user_id, topn10): matrix, news_ids build_user_item_matrix() sims user_similarity(user_id, matrix) # 找出topN相似用户 top_users sims.argsort()[::-1][1:6] # 聚合这些用户喜欢、当前用户未浏览的新闻 # ... 具体实现略,注意把当前用户已看过的id排除协同过滤在数据稀疏时效果会差,所以我在代码里加了1e-8防除零,并且只取top5相似用户,这样计算量和准确度都比较均衡。做毕业设计时你不需要追求SOTA,能清楚地把矩阵、相似度公式、推荐流程说出来,就已经是扎实的成果。3.4 后台管理与RBAC权限Django自带Admin后台,但直接用会很粗放。我在选题要求里融入了django rbac这个点,所以建议你把它升级为带角色和权限控制的完整模块。具体做法是用Django自带的Group和Permission,再为不同角色分配权限:运营管理员:只对新闻模块有增删改查权限。系统管理员:还能管理用户、查看行为数据。普通用户:只能看前端页面,不能进入后台。在admin.py里注册模型后,可以用list_display、search_fields、fieldsets做界面优化。这里有个经验:不要过度封装权限系统,直接用Django的permission_required装饰器或mixins即可。你可以在视图里这样限制:from django.contrib.auth.mixins import PermissionRequiredMixin from django.views.generic import ListView class NewsManageView(PermissionRequiredMixin, ListView): permission_required news.change_news template_name admin_news.html4. 实时推送与动态更新:Django Channels与WebSocket实战4.1 为什么需要WebSocket:从轮询到推送新闻推荐系统有个应用场景很考验技术含量:当后台管理员审核通过一条突发新闻,前端要实时刷新推荐列表,而不是让用户手动刷新页面。传统HTTP是无状态的“一问一答”,前端只能通过定时器轮询接口,每秒请求一次推荐列表,这既浪费服务器资源,又做不到真正的“实时”。WebSocket建立的是端到端的全双工连接。服务器可以随时主动把新推荐的数据“推”到浏览器端,前端沿着通道收数据后更新界面。Django生态里实现WebSocket的主流方案是Channels库,Django4.x/5.x都可以配合使用。对毕业设计而言,用Channels实现“后台有数据、前端自动推送”是一个极高的亮点,面试官听了也会感兴趣。4.2 Channels配置与消费者实现首先安装依赖并创建app(前面已经创建了chat,所以我用这个app实现推送功能):pip install channels打开settings.py,将channels注册到INSTALLED_APPS,设置ASGI路由入口:INSTALLED_APPS [ channels, # ... ] ASGI_APPLICATION news_recommend.asgi.application然后在项目根目录的asgi.py里配置:import os from django.core.asgi import get_asgi_application from channels.routing import ProtocolTypeRouter, URLRouter from channels.auth import AuthMiddlewareStack from chat.routing import websocket_urlpatterns os.environ.setdefault(DJANGO_SETTINGS_MODULE, news_recommend.settings) application ProtocolTypeRouter({ http: get_asgi_application(), websocket: AuthMiddlewareStack( URLRouter(websocket_urlpatterns) ), })接着在chatapp下创建routing.py和consumers.py:# chat/routing.py from django.urls import path from . import consumers websocket_urlpatterns [ path(ws/recommend/int:user_id/, consumers.RecommendConsumer.as_asgi()), ]# chat/consumers.py import json from channels.generic.websocket import AsyncWebsocketConsumer class RecommendConsumer(AsyncWebsocketConsumer): async def connect(self): self.user_id self.scope[url_route][kwargs][user_id] # 加入一个房间,用于后端推送 room_name fuser_{self.user_id} await self.channel_layer.group_add(room_name, self.channel_name) await self.accept() async def disconnect(self, close_code): room_name fuser_{self.user_id} await self.channel_layer.group_discard(room_name, self.channel_name) async def send_recommendations(self, event): # 后台通过channel_layer发过来的消息,这里转发给前端 payload event[payload] await self.send(text_datajson.dumps(payload))上面的关键点是group_add和group_discard:前端建立连接时,消费者把当前用户的连接加入一个带用户ID的唯一房间;后台管理员一旦审核通过新新闻,就能通过channel_layer.group_send向该房间广播更新事件,前端实时收到新的推荐列表。这里建议用async的consumer,因为channel_layer在异步环境下的兼容性更好。4.3 常见问题:static文件在vscode里显示不了热词里提到的“vscode写img标签在django的static文件中显示不了”,这本质不是vscode的问题,是Django模板引擎中对于静态文件路径的处理。正确姿势是:{% load static %} img src{% static images/cover.png %} alt封面同时一定要在settings.py配置:STATIC_URL /static/ STATICFILES_DIRS [ BASE_DIR / static, ] # 如果是生产环境,还需要在urls.py中处理静态文件路由如果你用了{{ article.cover_image.url }}却显示不出图片,大概率是MEDIA_ROOT和MEDIA_URL没配好。用户在后台通过ImageField上传的文件走MEDIA路径,你要在urls.py追加:from django.conf import settings from django.conf.urls.static import static urlpatterns static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)我确定你在vscode里来回改路径时,十有八九会栽在staticfiles的目录结构上。最好的习惯是项目根目录下建static文件夹,与templates平级,然后用{% static %}模板标签来引用,绝对路径直接写/static/xxx那是死路一条,换环境就得崩。5. 常见问题与排查实战5.1 Django执行查询与删除对象的注意事项热词里“django执行查询-删除对象”实际上对应了两个步骤:查询和删除,但删除操作坑很多。首先是查询:news News.objects.filter(is_activeTrue, category_id1) # 惰性查询:这一步没有执行SQL,遍历时才真正查库 for item in news: print(item.title)然后是删除。你可能有三种选择:# 直接删除 News.objects.get(id3).delete() # 返回 (数量, {表名: 条数}) News.objects.filter(id3).delete() # 批量删除这里容易踩坑的是ForeignKey的on_delete行为。比如UserBehavior表外键指向News,如果News被删,行为记录默认会被级联删除(CASCADE),而你算法依赖的行为数据就彻底没了。所以前面我才建议用is_active做软删除,而不是真删记录。删除视图写成:def soft_delete_news(request, news_id): news get_object_or_404(News, idnews_id) news.is_active False news.save(update_fields[is_active]) return JsonResponse({status: ok})把“物理删除”改成“逻辑删除”能救你于水火,这是我在做推荐冷启动时吃了大亏之后总结出来的。5.2 查询优化:别把所有新闻全查出来推荐接口最容易踩的性能坑是“查全量”。如果你在视图里写News.objects.all(),然后交给内存里的推荐算法去算,随着线上新闻增多,接口响应会越来越慢。正确做法是分步骤查询:news_ids get_recommend_ids(user, topn30) news_qs News.objects.filter(id__innews_ids, is_activeTrue) \ .select_related(category) \ .order_by(-published_at)这里用select_related把外键查出来,避免每次访问news.category.name触发一次数据库查询。前端展示的推荐列表只需要30条,不要把你算出的300条结果一次性丢给模板,那样首屏渲染时间直接翻倍。5.3 新手常踩的其他坑:时区、模板与迁移Django的时区设置默认启用USE_TZ True,你存入数据库的时间是UTC时间,如果开箱即用直接展示,会差8小时。建议在视图中统一用本地时间格式化:from django.utils import timezone from django.utils.dateformat import format publish_time format(news.published_at, U) # 转时间戳 # 或者在前端模板中用 |date:Y-m-d H:i:s 过滤器模板里如果一直显示不出变量,先检查视图有没有传context参数,新手常犯的是直接用return render(request, x.html)忘记传数据。数据库迁移遇到makemigrations不识别模型改动,大概率是app没有注册到INSTALLED_APPS,这是20分钟就能排查完的事。6. 项目测试、打包与答辩准备6.1 单元测试与接口测试写测试不是可选项,是答辩时证明代码质量的硬证据。Django自带的unittest框架足够用,核心写推荐接口的测试:from django.test import TestCase from django.urls import reverse class RecommendApiTests(TestCase): def setUp(self): # 创建用户、新闻、行为记录 pass def test_recommend_anonymous_gets_hot_news(self): response self.client.get(reverse(recommend_hot)) self.assertEqual(response.status_code, 200) self.assertEqual(len(response.json()[data]), 10) def test_user_recommend_uses_collaborative(self): response self.client.get(reverse(recommend_for_user, args[1])) self.assertIn(scores, response.json())测试通过之后,再用python manage.py test跑一遍,输出绿色结果截图放进论文附录,答辩时给老师看比嘴上说“我测过了”有说服力得多。6.2 项目部署与本地演示毕业设计不一定非要部署到云服务器,但至少要做到“一键启动、演示流畅”。本地演示建议用SQLite,省掉MySQL配置麻烦。如果需要部署,我推荐最朴素的方式:pip install gunicorn生产环境用gunicorn nginx部署Django,并配上whitenoise管理静态文件。具体步骤我就不展开了,你只需要知道:答辩现场要保证网络环境稳定,推荐先录一段演示视频备用,以防现场掉链子。这一点真的是血泪教训,我见过好几个同学演示时数据库没起来,白瞎了准备两周的效果。6.3 论文与答辩要点:亮点塑造论文里不要写成“流水账式功能说明书”,而要突出三个亮点:混合推荐算法的设计理由:为什么用热门召回、内容推荐、协同过滤三种方式,分别在什么条件触发,冷启动怎么处理。WebSocket实时推送架构:相比HTTP轮询的优势,Channel Layer的group机制如何支撑多用户广播。Django RBAC权限控制:如何利用自带的PermissionGroup实现最小权限原则。答辩时,先花2分钟讲系统整体架构(模块、数据流),再用5分钟依次演示用户注册登录、行为采集、推荐结果、后台新闻审核、前端实时推送,最后拿出测试用例截图和分析结果。这套流程我给很多学弟学妹推荐过,亲测好用。我在实际指导学生的过程里发现,真正把系统做完的人很少会挂在答辩上,反而多数卡在前期“选题犹豫”和中期“算法不知道从哪下手”。做这个题目,你不需要自创算法,只需把成熟方法重组、落进Django工程里,把数据流打通,它就是一个完整且有技术含量的毕设作品。最后再分享一个小技巧:代码里每一个关键函数,至少要能画出一张流程图并解释每一步的输入输出。这样写到论文里自然言之有物,答辩时心里也不慌。祝你把推荐系统从选题变成实实在在的毕业作品。
返回列表