简介:这份毕业设计论文资源面向计算机相关专业的本科毕业生,聚焦基于Python与协同过滤算法的电影推荐系统,帮助读者完成从选题、系统设计到论文撰写的完整毕设流程。压缩包内共1个doc文件,约2.43MB,即论文正文文档,涵盖摘要、绪论、系统设计与实现等章节,可直接作为论文写作模板与参考范例。论文以Django框架与MySQL数据库为技术栈,划分管理员与用户两类角色:管理员负责个人中心、用户管理、电影分类与信息管理、电影评分管理及系统管理;用户可注册登录、浏览电影资讯、评分、评论与收藏,并借助协同过滤算法实现个性化推荐。内容预览显示,论文结构完整,包含中英文摘要、目录、研究背景与意义等规范模块,对界面简洁美观、功能模块布局及数据安全方案均有论述。目前已有80人学习下载,适合需要参考完整毕设论文结构、技术选型与功能模块划分的读者,也可为推荐算法类课题的写作提供思路借鉴。
1. 从一份 .doc 说起:这套协同过滤电影推荐系统到底能跑出什么
如果你正在为计算机毕业设计发愁,或者手头拿到了一份名为「基于python和协同过滤算法的电影推荐系统.doc」的文档,第一反应大概率是:这玩意儿到底能不能跑起来?里面写的协同过滤是真算法还是套壳?我拆过不少这类毕设包,说实话,大部分文档写得像产品说明书,真正能落地的代码逻辑藏在字缝里。这份文档的核心价值在于它给了一套完整的 Django + MySQL 电影推荐系统骨架,包含用户注册登录、电影分类管理、评分评论收藏,以及最关键的协同过滤推荐模块。它适合两类人:一是需要快速搭出一个能演示、能答辩的毕设系统的同学;二是想拿一个现成的 Django 项目练手,顺便把推荐算法从理论落到代码上的开发者。文档里提到的管理员和用户双角色、电影资讯管理、评分管理这些模块,都是实打实要写进 models.py 和 views.py 的东西,不是纸上谈兵。
2. 环境搭建与 Django 项目初始化:把文档里的技术栈跑通
2.1 为什么选 Django + MySQL 而不是 Flask + SQLite
文档里明确写了用 Django 框架和 MySQL 数据库,这个选型在毕设场景下其实很合理。Django 自带 Admin 后台和 ORM,你不需要从零写增删改查的 SQL,模型定义好之后迁移一下,后台管理界面直接就能用,省下来的时间可以花在协同过滤算法上。MySQL 相比 SQLite 的优势在于它更接近生产环境,答辩的时候老师问「你这数据存哪」,你说 MySQL 并且能当场连上数据库看表结构,比说 SQLite 要硬气得多。另一个现实原因是,很多学校的毕设要求里明确写了「必须使用主流关系型数据库」,MySQL 就是最稳妥的选择。
安装环节我一般会建议用虚拟环境隔离依赖,避免把系统 Python 搞乱。下面是 Ubuntu 和 Windows 下都通用的步骤,Python 版本建议 3.8 到 3.10,Django 用 3.2 LTS 或 4.2 LTS 都行,MySQL 用 5.7 或 8.0 都可以,但 8.0 的认证插件变化需要注意,后面避坑章节会细说。
# 创建项目目录并进入 mkdir movie_recommend && cd movie_recommend # 创建虚拟环境(Ubuntu/Windows 通用) python -m venv venv # 激活虚拟环境 # Ubuntu: source venv/bin/activate # Windows: # venv\Scripts\activate # 安装核心依赖 pip install django==4.2.7 mysqlclient==2.2.0 pandas==2.1.1 numpy==1.26.0 scikit-learn==1.3.2 # 创建 Django 项目 django-admin startproject movie_system . # 创建核心应用 python manage.py startapp recommend这里解释一下几个关键依赖的作用。mysqlclient是 Django 连接 MySQL 的驱动,比pymysql性能更好,但安装时依赖系统里的 MySQL 开发库,Ubuntu 下需要先sudo apt-get install libmysqlclient-dev python3-dev,Windows 下一般直接 pip 安装就能成功。pandas和numpy是用来处理评分矩阵的,协同过滤计算相似度的时候会用到。scikit-learn提供了余弦相似度和皮尔逊相关系数的现成实现,不用自己手写公式,减少出错概率。
2.2 settings.py 里必须改的四个配置项
Django 默认用 SQLite,要换成 MySQL 得改settings.py里的DATABASES配置。同时因为前端模板和静态文件要正常加载,TEMPLATES和STATIC相关配置也得调整。下面是我一般会改的四个地方:
# movie_system/settings.py # 1. 注册应用 INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'recommend', # 新增 ] # 2. 数据库配置 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'movie_db', # 数据库名,需要提前在 MySQL 里创建 'USER': 'root', # 你的 MySQL 用户名 'PASSWORD': 'your_password', # 你的 MySQL 密码 'HOST': '127.0.0.1', 'PORT': '3306', 'OPTIONS': { 'charset': 'utf8mb4', }, } } # 3. 模板路径 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [BASE_DIR / 'templates'], # 新增模板目录 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] # 4. 静态文件与媒体文件 STATIC_URL = '/static/' STATICFILES_DIRS = [BASE_DIR / 'static'] MEDIA_URL = '/media/' MEDIA_ROOT = BASE_DIR / 'media' # 中文支持 LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai' USE_TZ = FalseDATABASES里的NAME对应的数据库必须提前在 MySQL 里用CREATE DATABASE movie_db CHARACTER SET utf8mb4;创建好,Django 的migrate命令只建表不建库。OPTIONS里的utf8mb4是为了支持中文和特殊字符,用utf8在某些场景下会出问题。USE_TZ = False配合TIME_ZONE设置,能让后台显示的时间就是北京时间,不然默认 UTC 时间会让你在调试时怀疑人生。
2.3 建表与数据迁移:从文档里的表结构到 Django Model
文档里给了电影资讯表、电影信息表、电影信息评论表的结构,我一般会把这些字段直接翻译成 Django 的 Model 类。下面以电影信息表和评论表为例,展示怎么把文档里的表格变成可执行的代码:
# recommend/models.py from django.db import models from django.contrib.auth.models import User class Movie(models.Model): """电影信息表,对应文档中的电影信息表结构""" name = models.CharField(max_length=200, verbose_name='电影名称') category = models.CharField(max_length=100, verbose_name='电影类型') director = models.CharField(max_length=100, verbose_name='导演') actors = models.CharField(max_length=500, verbose_name='主演') region = models.CharField(max_length=100, verbose_name='制片地区') release_date = models.DateField(verbose_name='上映日期') description = models.TextField(verbose_name='电影描述') poster = models.ImageField(upload_to='posters/', verbose_name='电影海报') video_url = models.CharField(max_length=500, blank=True, verbose_name='电影视频') thumbs_up = models.IntegerField(default=0, verbose_name='赞') thumbs_down = models.IntegerField(default=0, verbose_name='踩') click_time = models.DateTimeField(auto_now=True, verbose_name='最近点击时间') created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') class Meta: db_table = 'movie_info' verbose_name = '电影信息' class Rating(models.Model): """电影评分表,协同过滤的核心数据来源""" user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='用户') movie = models.ForeignKey(Movie, on_delete=models.CASCADE, verbose_name='电影') score = models.FloatField(verbose_name='评分') # 1-5 分 comment = models.TextField(blank=True, verbose_name='评论') created_at = models.DateTimeField(auto_now_add=True, verbose_name='评分时间') class Meta: db_table = 'movie_rating' unique_together = ('user', 'movie') # 同一用户对同一电影只能评一次 verbose_name = '电影评分'unique_together这个约束很重要,它保证了评分矩阵里不会出现同一个用户对同一部电影有两条评分记录,否则计算相似度的时候会出问题。score用FloatField而不是IntegerField,是为了后续如果要做归一化或者加权计算时保留小数精度。on_delete=models.CASCADE表示用户或电影被删除时,对应的评分记录也一起删掉,避免脏数据。
模型写完之后执行迁移命令:
# 生成迁移文件 python manage.py makemigrations recommend # 执行迁移,在 MySQL 中建表 python manage.py migrate # 创建超级管理员,用于登录 Django Admin 后台 python manage.py createsuperusermakemigrations会根据 Model 定义生成 SQL 语句,migrate才会真正在 MySQL 里执行。如果这一步报错说无法连接数据库,先检查 MySQL 服务是否启动、用户名密码是否正确、数据库是否已创建。createsuperuser创建的账号可以登录/admin后台,文档里说的「管理员功能」其实大部分可以直接用 Django Admin 实现,不需要自己写管理界面。
3. 协同过滤推荐算法的落地:从评分矩阵到推荐列表
3.1 用户基于与物品基于:选哪种,为什么
协同过滤分两大流派:UserCF 和 ItemCF。UserCF 是找和你口味相似的人,把他们喜欢的电影推荐给你;ItemCF 是找你喜欢的电影相似的电影推荐给你。在电影推荐场景下,ItemCF 通常更稳定,因为电影的数量和属性相对固定,而用户的口味会随时间变化。文档里没有明确说用哪种,但从「电影评分管理」这个功能来看,评分数据是核心,我一般会优先实现 ItemCF,然后用 UserCF 做对比实验,答辩的时候两个都讲,显得工作量更饱满。
ItemCF 的核心步骤就三步:计算物品之间的相似度、根据用户历史评分找相似物品、加权打分生成推荐列表。相似度计算用余弦相似度或者皮尔逊相关系数,前者适合评分数据稠密的情况,后者对评分尺度差异更鲁棒。下面是一个完整的 ItemCF 实现:
# recommend/cf_algorithm.py import numpy as np import pandas as pd from sklearn.metrics.pairwise import cosine_similarity from .models import Rating, Movie def build_rating_matrix(): """从数据库构建用户-电影评分矩阵""" ratings = Rating.objects.all().values('user_id', 'movie_id', 'score') df = pd.DataFrame(list(ratings)) if df.empty: return None, None, None # 透视表:行是用户,列是电影,值是评分 matrix = df.pivot_table( index='user_id', columns='movie_id', values='score', fill_value=0 # 未评分的填 0 ) return matrix, matrix.index.tolist(), matrix.columns.tolist() def item_similarity(matrix): """计算物品之间的余弦相似度""" # 转置后行是电影,列是用户 item_matrix = matrix.T.values # 余弦相似度矩阵,shape 为 (电影数, 电影数) sim = cosine_similarity(item_matrix) # 对角线置 0,避免自己和自己相似度为 1 影响推荐 np.fill_diagonal(sim, 0) return sim def recommend_for_user(user_id, matrix, sim, top_n=10): """为指定用户生成推荐列表""" if user_id not in matrix.index: return [] user_idx = matrix.index.get_loc(user_id) user_ratings = matrix.iloc[user_idx].values # 该用户对所有电影的评分 # 只考虑用户评过分的电影 rated_indices = np.where(user_ratings > 0)[0] if len(rated_indices) == 0: return [] # 加权求和:相似度 * 用户评分 scores = np.zeros(matrix.shape[1]) for idx in rated_indices: scores += sim[idx] * user_ratings[idx] # 过滤掉用户已经评过分的电影 scores[rated_indices] = 0 # 取 top_n top_indices = np.argsort(scores)[::-1][:top_n] movie_ids = [matrix.columns[i] for i in top_indices if scores[i] > 0] # 返回电影对象列表 return list(Movie.objects.filter(id__in=movie_ids))build_rating_matrix用 pandas 的pivot_table把数据库里的评分记录转成矩阵,fill_value=0表示未评分用 0 填充,这样余弦相似度计算时不会因为缺失值报错。item_similarity里np.fill_diagonal(sim, 0)这行很关键,如果不置零,推荐结果里会出现用户已经评过分的电影自己推荐给自己。recommend_for_user里的加权求和逻辑是 ItemCF 的标准做法:用户对某部电影的评分乘以该电影与候选电影的相似度,累加后排序。scores[rated_indices] = 0确保已评分的电影不会被重复推荐。
3.2 把推荐结果接入 Django 视图和模板
算法写好了,得让它在页面上跑起来。我一般会在views.py里写一个推荐视图,然后在用户首页调用。下面是一个简化的实现:
# recommend/views.py from django.shortcuts import render from django.contrib.auth.decorators import login_required from .cf_algorithm import build_rating_matrix, item_similarity, recommend_for_user from .models import Movie @login_required def home(request): """用户首页,展示推荐电影和最新电影""" matrix, user_ids, movie_ids = build_rating_matrix() recommended = [] if matrix is not None and request.user.id in user_ids: sim = item_similarity(matrix) recommended = recommend_for_user(request.user.id, matrix, sim, top_n=8) # 如果推荐为空(新用户没有评分记录),展示热门电影 if not recommended: recommended = list(Movie.objects.order_by('-thumbs_up')[:8]) latest = Movie.objects.order_by('-created_at')[:8] return render(request, 'home.html', { 'recommended': recommended, 'latest': latest, })@login_required装饰器保证只有登录用户才能看到个性化推荐,未登录用户跳转到登录页。build_rating_matrix每次请求都重新构建矩阵,在数据量小的时候没问题,但如果评分数据上万条,这个查询会变慢,后面进阶章节会讲怎么优化。新用户没有评分记录时,recommend_for_user返回空列表,这时候用thumbs_up排序展示热门电影作为兜底,避免首页空白。
模板部分用 Django 的模板语法循环渲染即可:
<!-- templates/home.html --> {% extends 'base.html' %} {% block content %} <div class="recommend-section"> <h3>为你推荐</h3> <div class="movie-grid"> {% for movie in recommended %} <div class="movie-card"> <img src="{{ movie.poster.url }}" alt="{{ movie.name }}"> <h4>{{ movie.name }}</h4> <p>{{ movie.category }} | {{ movie.director }}</p> <a href="{% url 'movie_detail' movie.id %}">查看详情</a> </div> {% empty %} <p>暂无推荐,请先给一些电影评分</p> {% endfor %} </div> </div> {% endblock %}{% empty %}是 Django 模板里处理空列表的语法,比用{% if %}判断更简洁。movie.poster.url要求ImageField配置了MEDIA_URL和MEDIA_ROOT,并且在urls.py里加了静态文件服务,否则图片显示不出来。
3.3 评分数据的冷启动与推荐效果验证
新用户注册进来没有任何评分,协同过滤算不出相似度,这是推荐系统经典的冷启动问题。文档里没有提解决方案,但实际做毕设的时候老师很可能会问。我一般会做两件事:一是新用户首次登录时弹出一个「选几部你看过的电影打个分」的引导页,快速收集初始评分;二是用热门电影和最新电影做兜底推荐,保证首页不空。引导页的实现就是在用户注册成功后跳转到一个电影列表页,让用户勾选并打分,提交后写入 Rating 表。
验证推荐效果可以用离线实验:把评分数据按 8:2 分成训练集和测试集,用训练集算相似度,在测试集上预测评分,计算 RMSE 或 MAE。如果 RMSE 在 0.8 到 1.2 之间,说明推荐效果还可以接受。这个实验不需要在系统里做,单独写个脚本跑一下,把结果写进论文的「系统测试」章节就行。
4. 避坑与排查:那些文档里不会写的翻车现场
4.1 MySQL 8.0 认证插件导致 Django 连接失败
现象:migrate的时候报django.db.utils.OperationalError: (1045, "Access denied for user 'root'@'localhost'"),但密码明明是对的。
原因:MySQL 8.0 默认用caching_sha2_password认证插件,而mysqlclient在某些版本下不兼容这种认证方式。
解决:登录 MySQL 后执行ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_password';然后FLUSH PRIVILEGES;。或者升级mysqlclient到 2.2.0 以上版本,新版本已经支持caching_sha2_password。
4.2 评分矩阵稀疏导致相似度计算全为 0
现象:推荐结果为空,或者推荐出来的电影明显不相关。
原因:用户评分数据太少,矩阵里大部分是 0,余弦相似度计算出来接近 0,排序后没有有效推荐。
解决:设置一个相似度阈值,比如只保留相似度大于 0.1 的邻居;同时增加兜底策略,推荐列表为空时返回热门电影。另外可以在论文里说明这是冷启动问题,并给出你的解决方案,反而是加分项。
4.3 中文乱码:数据库、Django、模板三处都要设对
现象:电影名称显示成??????或者是影。
原因:MySQL 数据库字符集不是utf8mb4,或者 Django 的OPTIONS里没设charset,或者模板文件没保存为 UTF-8。
解决:建库时用CREATE DATABASE movie_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;,settings.py的DATABASES里加'OPTIONS': {'charset': 'utf8mb4'},模板文件用编辑器保存为 UTF-8 无 BOM 格式。三处都对了才不会乱码。
4.4 图片上传后前端显示 404
现象:后台上传了电影海报,前端<img>标签的src路径正确但图片加载不出来。
原因:Django 开发环境下MEDIA_URL的文件不会自动由 Django 服务,需要在urls.py里手动配置。
解决:在项目根urls.py里加from django.conf import settings和from django.conf.urls.static import static,然后在urlpatterns末尾加+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)。注意这个只在DEBUG=True时有效,生产环境需要用 Nginx 之类的服务器来处理静态文件。
4.5 协同过滤计算超时:每次请求都重算相似度矩阵
现象:首页加载越来越慢,评分数据到几千条的时候要等好几秒。
原因:home视图里每次请求都调用build_rating_matrix和item_similarity,这两个操作都是 O(n²) 复杂度,数据量上来之后扛不住。
解决:把相似度矩阵缓存起来,用 Django 的cache框架或者直接存到文件里,设置一个合理的过期时间(比如 1 小时)。评分数据变化不频繁的话,甚至可以写一个管理命令手动触发重算。具体做法在下一章展开。
5. 进阶技巧:缓存相似度矩阵与用管理命令批量重算
5.1 用 Django Cache 把相似度矩阵缓存到内存
上一章提到每次请求重算相似度矩阵会拖慢响应,最直接的优化就是用缓存。Django 自带缓存框架,开发环境用本地内存缓存就够了,生产环境可以换 Redis。配置和改造代码如下:
# movie_system/settings.py CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'movie-sim-cache', 'TIMEOUT': 3600, # 1 小时过期 } }# recommend/cf_algorithm.py from django.core.cache import cache import hashlib def get_cached_similarity(): """带缓存的相似度矩阵获取""" # 用评分记录数作为缓存 key 的一部分,数据变了 key 就变 rating_count = Rating.objects.count() cache_key = f'item_sim_{hashlib.md5(str(rating_count).encode()).hexdigest()[:8]}' sim = cache.get(cache_key) if sim is None: matrix, user_ids, movie_ids = build_rating_matrix() if matrix is None: return None, None, None sim = item_similarity(matrix) # 缓存相似度矩阵和对应的 movie_ids cache.set(cache_key, (sim, movie_ids), 3600) else: sim, movie_ids = sim matrix, user_ids, _ = build_rating_matrix() return matrix, sim, movie_ids缓存 key 里带上评分记录数,这样有新评分写入时 key 会变化,自动触发重算,不需要手动清缓存。cache.set的第三个参数是过期时间,单位秒。LocMemCache是进程内缓存,多进程部署时每个进程有独立的缓存,生产环境建议换django-redis。
5.2 写一个管理命令批量重算并导出推荐结果
如果不想在请求时算,可以写一个 Django 管理命令,手动或定时执行,把推荐结果预先算好存到数据库或文件里。这样前端只需要读结果,响应速度极快。
# recommend/management/commands/refresh_recommendations.py from django.core.management.base import BaseCommand from django.contrib.auth.models import User from recommend.cf_algorithm import build_rating_matrix, item_similarity, recommend_for_user import json class Command(BaseCommand): help = '批量重算所有用户的推荐结果并保存到文件' def handle(self, *args, **options): matrix, user_ids, movie_ids = build_rating_matrix() if matrix is None: self.stdout.write('暂无评分数据,跳过') return sim = item_similarity(matrix) result = {} for user in User.objects.all(): if user.id in user_ids: movies = recommend_for_user(user.id, matrix, sim, top_n=10) result[user.id] = [m.id for m in movies] else: result[user.id] = [] with open('recommendations.json', 'w', encoding='utf-8') as f: json.dump(result, f, ensure_ascii=False, indent=2) self.stdout.write(f'已为 {len(result)} 个用户生成推荐结果')执行python manage.py refresh_recommendations就会在项目根目录生成recommendations.json。前端视图改成读这个文件,速度从秒级降到毫秒级。这个命令可以配合系统的定时任务,比如每天凌晨跑一次。
5.3 答辩时怎么讲清楚「协同过滤」不是调包
很多同学担心老师问「你这算法是自己写的还是调库的」。我的经验是,坦诚说相似度计算用了sklearn的cosine_similarity,但整个推荐流程——从数据库取评分、构建矩阵、过滤已评分、加权排序、返回结果——都是自己实现的。然后可以现场打开cf_algorithm.py,指着recommend_for_user函数讲加权求和的逻辑,再打开models.py讲评分表的设计和unique_together约束。老师一般不会刁难,反而会觉得你对代码有掌控力。
从那以后我每次拿到这类毕设项目,都会先把settings.py和models.py过一遍,确认数据库配置和表结构没问题,再跑migrate和createsuperuser,最后才去调算法。这个顺序能帮你省下大量排查环境问题的时间。希望帮到你。
本文还有配套的精品资源,点击获取