☰
Django招聘推荐系统实战:协同过滤算法与爬虫数据链路解析
2026/10/6 5:22:05 网站建设 项目流程

简介:这是一套面向计算机专业学生与初学者的信息管理系统实战项目,采用Python3、Django、MySQL5.7、Vue与爬虫技术构建,可作为毕业设计、课程设计、大作业或工程实训的完整参考方案。项目围绕协同过滤推荐算法展开,首页提供导航条入口,用户可在个人中心更新个人信息,管理员后台则涵盖用户管理、留言板管理、信息发布等模块,功能链路完整。压缩包共508个文件,约23.35MB,包含161个svg图标、71个vue组件、46个py后端脚本、42张jpg图片、41个js文件及sql数据库脚本、docx文档等,前后端与数据层资源齐备。目前已有84人学习下载。读者可据此快速搭建可运行环境,理解Django与Vue的前后端分离结构,掌握协同过滤推荐在信息展示中的落地方式,并借助爬虫模块获取数据,为项目立项与答辩提供完整支撑。

1. 从一份 Django 招聘推荐源码说起:它到底能跑出什么结果

如果你手头正好有一份5p125基于协同过滤算法的招聘信息推荐系统_django+spider.zip,大概率是冲着两件事来的:一是想看看 Django 怎么把推荐算法塞进一个真实业务里,二是想知道那个 spider 到底爬了什么、怎么和推荐结果串起来。我拆过不少这类毕设/课设包,说实话,大部分要么是纯 CRUD 套壳,要么是算法和业务两张皮。这份包的价值在于它把「爬招聘数据 → 入库 → 协同过滤算相似 → 给用户推岗位」这条链路走通了,虽然工程化程度一般,但作为理解推荐系统落地的样本,够用。

它适合三类人:正在做 Django 项目实战的新手,想找一个带算法逻辑的完整参考;做毕业设计需要推荐系统模块的同学,可以直接对照数据流和算法实现;还有想快速验证协同过滤在招聘场景下效果的人。不适合指望开箱即用上生产的人,因为爬虫的健壮性、推荐的冷启动、并发性能这些,包里基本没怎么处理。下面我按「先跑起来 → 再看算法 → 最后避坑」的顺序,把这份源码拆开讲。

2. 环境搭建与项目启动:把 Django 和爬虫跑通

2.1 依赖安装与数据库初始化

拿到包先别急着改代码,第一步是把环境跑通。这类项目通常用 Python 3.7~3.9 加 Django 2.x/3.x,数据库大概率是 MySQL,也有用 SQLite 的。我一般会先看requirements.txt和settings.py里的DATABASES配置,确认版本再动手。

# 创建虚拟环境,避免污染全局 python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate # 安装依赖,如果 requirements.txt 缺失就手动装核心包 pip install django pymysql requests beautifulsoup4 numpy pandas # 初始化数据库,先改 settings.py 里的数据库连接 python manage.py makemigrations python manage.py migrate # 创建后台管理员,方便录入和查看数据 python manage.py createsuperuser

这里有几个参数要盯一下:settings.py里的ALLOWED_HOSTS本地调试可以设['*'],但上线必须收紧;DATABASES的NAME、USER、PASSWORD要和本地 MySQL 一致,很多人卡在Access denied就是这里没改。migrate报错的话,先检查pymysql有没有在__init__.py里伪装成MySQLdb,这是 Django 连 MySQL 的常见操作。

# 在项目同名目录的 __init__.py 里加这两行 import pymysql pymysql.install_as_MySQLdb()

逻辑说明:Django 默认用MySQLdb驱动,但 Python 3 下这个包不好装,所以用pymysql替代。参数上,install_as_MySQLdb()是让 Django 以为自己在用原生驱动,实际走的是 pymysql。不加这两行,migrate阶段就会报Error loading MySQLdb module。

2.2 爬虫模块的启动与数据入库

spider 部分通常是独立脚本或者 Django 的 management command。先找到爬虫入口,一般在spider/目录下,或者叫crawl.py、job_spider.py。运行前确认目标网站的页面结构没变,因为正则表达式和 CSS 选择器是最容易失效的地方。

# 典型爬虫结构:请求列表页 → 解析详情页 → 入库 import requests from bs4 import BeautifulSoup from myapp.models import JobInfo def crawl_page(url): headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'} resp = requests.get(url, headers=headers, timeout=10) soup = BeautifulSoup(resp.text, 'html.parser') for item in soup.select('.job-list .job-item'): job = JobInfo() job.title = item.select_one('.job-title').get_text(strip=True) job.company = item.select_one('.company-name').get_text(strip=True) job.salary = item.select_one('.salary').get_text(strip=True) job.save()

逻辑说明:requests.get带headers是为了绕过基础的反爬检测,timeout=10防止某个请求卡死整个脚本。select里的类名要和目标网站实际 DOM 一致,这是最容易翻车的地方。job.save()直接写库,没做去重,重复跑会堆数据,后面推荐结果会被污染。参数上,timeout建议设 5~15 秒,太短容易误判超时,太长拖慢整体进度。

提示:爬虫跑之前先在浏览器里手动翻两页,确认列表页和详情页的 URL 规律,以及关键字段的 HTML 结构。很多包里的爬虫代码是写死选择器的,网站一改版就废。

3. 协同过滤算法实现:用户相似度和岗位推荐怎么算

3.1 用户-岗位评分矩阵的构建

协同过滤的核心是「找相似的人,推他们喜欢的岗位」。这份包里通常用用户行为(浏览、投递、收藏)构造评分矩阵。先看数据模型里有没有UserBehavior或Rating表,字段一般是user_id、job_id、score或behavior_type。

import numpy as np from myapp.models import UserBehavior def build_matrix(): behaviors = UserBehavior.objects.all().values('user_id', 'job_id', 'score') users = sorted(set(b['user_id'] for b in behaviors)) jobs = sorted(set(b['job_id'] for b in behaviors)) user_index = {u: i for i, u in enumerate(users)} job_index = {j: i for i, j in enumerate(jobs)} matrix = np.zeros((len(users), len(jobs))) for b in behaviors: matrix[user_index[b['user_id']]][job_index[b['job_id']]] = b['score'] return matrix, users, jobs

逻辑说明:这段把数据库里的行为记录转成 numpy 二维数组,行是用户,列是岗位,值是评分。score可以是浏览记 1 分、投递记 3 分、收藏记 5 分这种加权。参数上,矩阵稀疏度通常很高,因为一个用户不可能对所有岗位都有行为,后面算相似度时要注意零值处理。如果数据量上万,这种全量加载会吃内存,常见做法是分页或只取活跃用户。

3.2 基于用户的协同过滤推荐

拿到矩阵后,用余弦相似度算用户之间的相似度,再取 TopN 相似用户的岗位做推荐。

from sklearn.metrics.pairwise import cosine_similarity def recommend(user_id, matrix, users, jobs, top_n=10): idx = users.index(user_id) sim = cosine_similarity(matrix) sim_scores = list(enumerate(sim[idx])) sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True) sim_users = [i for i, _ in sim_scores[1:6]] # 取最相似的5个用户 job_scores = {} for u in sim_users: for j in range(len(jobs)): if matrix[u][j] > 0 and matrix[idx][j] == 0: job_scores[j] = job_scores.get(j, 0) + matrix[u][j] * sim[idx][u] ranked = sorted(job_scores.items(), key=lambda x: x[1], reverse=True) return [jobs[j] for j, _ in ranked[:top_n]]

逻辑说明:cosine_similarity(matrix)一次算出所有用户两两相似度,sim[idx]是目标用户和其他人的相似度向量。取相似度最高的 5 个用户(跳过自己),把他们有行为而目标用户没行为的岗位按「相似度 × 评分」加权累加,最后排序取前 10。参数上,top_n控制推荐数量,相似用户数取 5~20 比较常见,太少推荐多样性差,太多引入噪声。matrix[idx][j] == 0这个判断是为了过滤掉用户已经看过的岗位,避免重复推荐。

注意:如果用户数或岗位数很大,cosine_similarity的全量计算会非常慢,常见做法是先用倒排索引或局部敏感哈希缩小候选集,再算相似度。这份包大概率没做这层优化,数据量小的时候能跑,大了就卡。

4. 避坑与常见问题排查:跑不起来先看这几条

4.1 爬虫数据字段缺失或乱码

现象:入库后发现title或company为空,或者中文变成\uXXXX。原因通常是页面结构变了导致选择器匹配不到,或者响应编码没处理。解决:先用resp.encoding = resp.apparent_encoding让 requests 自动推断编码,再检查选择器是否命中。如果字段确实缺失,加个if item.select_one(...)判空,别让None.get_text()直接抛异常。

4.2 推荐结果为空或全是热门岗位

现象:调用推荐接口返回空列表,或者推来推去就那几个岗位。原因一般是评分矩阵太稀疏,新用户没有行为数据,或者相似度计算时零值太多导致相似度全为 0。解决:对冷启动用户走热门岗位兜底,或者用基于物品的协同过滤补充。另外检查score字段是不是全为 0,有时候爬虫入库时忘了给默认分。

4.3 Django 静态文件 404 或后台样式丢失

现象:runserver起来后页面没样式,控制台报GET /static/... 404。原因是DEBUG=True时 Django 不会自动服务静态文件,需要配static()路由。解决:在urls.py里加from django.conf.urls.static import static和urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT),同时确认STATICFILES_DIRS指向正确目录。

4.4 数据库连接超时或 Too many connections

现象:跑一段时间后报OperationalError: (2006, 'MySQL server has gone away')或连接数爆满。原因是 Django 默认每个请求开一个连接,爬虫脚本里循环save()也会频繁建连。解决:在settings.py里设CONN_MAX_AGE复用连接,爬虫里用bulk_create批量入库,别一条条save()。

4.5 协同过滤计算时内存溢出

现象:用户或岗位上千后,cosine_similarity直接吃满内存。原因是全量矩阵是稠密的,稀疏数据被展开成n×m的 float 数组。解决:改用scipy.sparse存矩阵,或者只对活跃用户算相似度,分批处理。常见做法是先用TruncatedSVD降维再算相似度,能显著降内存。

5. 进阶技巧:把推荐结果做成可验证的接口

跑通之后,我一般会做一件事:把推荐逻辑封装成一个独立接口,方便用脚本批量验证效果,而不是每次点页面看。这样调参、换相似度算法、对比不同 TopN 的效果都快得多。

# views.py 里加一个返回 JSON 的推荐接口 from django.http import JsonResponse from .cf import recommend, build_matrix def api_recommend(request): user_id = int(request.GET.get('user_id', 1)) top_n = int(request.GET.get('top_n', 10)) matrix, users, jobs = build_matrix() if user_id not in users: return JsonResponse({'code': 1, 'msg': '用户无行为数据', 'data': []}) result = recommend(user_id, matrix, users, jobs, top_n) return JsonResponse({'code': 0, 'data': result})

逻辑说明:build_matrix()每次请求都重新查库构建矩阵,数据量小的时候没问题,大了要加缓存。user_id不在users里说明该用户没有任何行为,直接返回空并提示,避免后续索引报错。参数上,top_n通过 URL 传入,方便对比 5、10、20 的推荐效果。验证时可以用curl或 Postman 批量请求不同用户,看返回的岗位是否合理。

验证维度检查方法合格标准
推荐覆盖率统计有推荐结果的用户占比活跃用户应接近 100%
推荐多样性看 Top10 里不同公司/岗位类别的数量不应全是同一类岗位
响应时间记录接口平均耗时小数据量下应低于 500ms
冷启动处理用无行为用户请求应返回兜底热门岗位而非报错

从那以后我每次拿到这类推荐系统源码,都强制先跑一遍接口验证,而不是只看页面能不能打开。页面能打开不代表推荐逻辑是对的,很多包里的推荐结果其实是随机或者写死的,只有接口返回的数据能说明问题。希望帮到你。

本文还有配套的精品资源,点击获取

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

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

立即咨询