☰
Django+随机森林+Boss直聘数据分析可视化项目拆解
2026/9/26 6:53:55 网站建设 项目流程

每年到毕设季,我都要跟不少学生聊选题。大数据方向的毕设最容易掉进两个坑:要么是把爬虫当作全部,抓了一堆数据丢在CSV里就结束了,没有算法也没有平台;要么是抱着一个Jupyter Notebook调通了模型,结果连个能给别人演示的界面都拿不出来。今天想拆解的这套“Django + 随机森林算法 + Boss直聘数据分析可视化”项目,恰好是把数据采集、数据清洗、特征工程、随机森林建模、Web平台开发和可视化大屏串成了一条完整链路。对于正在准备大数据毕业设计、手里只有零散需求文档的同学来说,这套项目的价值在于:它让你从招聘数据中挖掘出薪资规律,再用网页把分析结果和预测能力真正“用起来”,不是纯概念的堆砌,而是每一步都能拿出实际产物。

我会按照一个完整项目的推进顺序来讲:先说为什么这套选题组合聪明,然后是环境与项目架构、数据清洗与建模、Django可视化落地,最后是论文写作和远程调试这些容易被忽视但决定毕设质量的细节。如果你打算用这个题目做毕设,或者想在此基础上二次定制,这篇文章应该能帮你省掉不少摸索的时间。

1. 这个毕设为什么值得做:选题组合背后的逻辑

1.1 覆盖面:一套项目打通数据全流程

大数据方向的毕设,评审老师最看重的不是单一技术有多深,而是你有没有完整的数据闭环能力。所谓闭环,指的是“获取数据 → 清洗数据 → 分析数据 → 建立模型 → 呈现结果”这条链路必须完整可见。

这套项目刚好踩在了这条链路上:收集招聘公开信息,得到包含岗位、城市、学历、经验、薪资等字段的数据集;随后是数据清洗和特征工程,把杂乱文本变成模型能吃的结构化特征;接着用随机森林算法训练薪资预测模型;最后通过Django搭建Web平台,把统计分析和模型预测结果以可视化大屏的方式展示出来。一个项目同时覆盖了数据采集、数据预处理、机器学习算法、Web开发和可视化五大模块,无论从论文的工作量还是答辩时能展示的成果来看,都非常饱满。

相比之下,很多学生做的“数据分析”毕设只有静态的Matplotlib图表,缺乏交互性;而单纯做Django增删改查的“管理系统”,又完全没有机器学习的含量。这套方案把算法和Web结合得很好,这也是它能在多个毕设题目中成为热门的原因。

1.2 为什么选中随机森林,而不是神经网络

很多学生一提到机器学习就想到深度学习,觉得“神经网络比随机森林高级”。但对于毕设场景,这个想法往往适得其反。招聘预测分析所用的数据量级通常是几千到几万条,特征维度也只有十几个,这种规模的数据丢给深度神经网络,不仅容易过拟合,训练慢,可解释性还差。最难的是,答辩时老师一定会问“你这个模型为什么有效”,深度神经网络像个黑盒,很难讲清楚。

随机森林的优势恰恰体现在这些方面:

对比维度随机森林线性回归决策树XGBoost神经网络
对非线性关系的拟合好差好但易过拟合好很好
可解释性较好(特征重要性)好很好一般差
对小数据集鲁棒性很好较好较差依赖调参差
实现难度低低低中高
论文可写篇幅丰富少少中需大量理论

随机森林由多棵决策树集成,每棵树在训练时随机抽样样本、随机选择特征,最后对回归任务取平均值。这种Bagging策略让它不容易过拟合,对异常值和缺失值也有较好的容忍度。另外,sklearn库直接提供了特征重要性输出,论文里可以画一张排序图,明确说明“工作年限和学历是影响薪资最重要的两个因素”,这就让论文的分析章节有血有肉,而不是空谈算法原理。

1.3 为什么是Boss直聘数据,获取过程如何保证合规

选择招聘平台数据做分析,是因为招聘信息的结构化程度高:岗位名称、薪资区间、学历要求、经验要求、公司规模、融资阶段都相对规范,非常适合数据清洗后建模。而且“求职”是每个毕业生都关心的话题,分析结论比如“一线城市程序员的平均薪资”“硕士学历对薪资的提升幅度”,很容易在论文里找到现实意义。

这里必须专门强调一下数据合规。项目演示和个人学习研究可以使用公开页面信息,但要注意几点:只采集公开可见的基础字段,不采集个人隐私信息;控制请求频率,不能对目标网站造成压力;不用于任何商业用途;在论文和系统页面中明确写出“数据仅供学习研究”。如果你用了爬虫框架,建议在代码中加入限速和随机延时,这既是对目标网站的基本尊重,也是实际项目部署时避免被封的必要手段。后续章节里的清洗和建模方法,都不依赖某一个特定来源的数据,换成其他招聘平台或公开数据集同样可复现。

2. 前置准备与项目架构:动手前先想清楚这些事

2.1 整体技术栈与运行环境

我见过太多毕设项目,代码写得没有任何问题,但环境装不上,最后答辩时只能在台上干瞪眼。为了少踩这种坑,建议在一开始就锁定技术版本,并且给出一份完整的requirements.txt。

这套项目比较稳妥的组合是:

  • Python 3.8 / 3.10(不要用最新的Python 3.13,部分库的wheel包可能还没跟上)
  • Django 3.2(稳定且文档多)或Django 4.2 LTS
  • MySQL 5.7 / 8.0(数据量大时比SQLite更可靠,也方便在论文里画ER图)
  • Scikit-learn 1.x(随机森林算法)
  • Pandas / NumPy(数据处理)
  • ECharts 5(前端可视化图表)
  • Redis 选装(如果要实现缓存或WebSocket消息代理)

写一个requirements.txt很简单,但很多人会漏掉mysqlclient、pymysql这些数据库驱动。要知道在Windows上源码编译mysqlclient是很痛苦的,建议直接使用pymysql并在Django项目的__init__.py里加一句:

import pymysql pymysql.install_as_MySQLdb()

这一行就能省掉大半天的折腾。

2.2 目录结构与代码模块划分

一套值得参考的源码,目录结构一定要清楚。不要把所有代码都堆在Django的默认结构里,建议按职责拆分:

boss_project/ ├── manage.py ├── requirements.txt ├── config/ │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── apps/ │ ├── jobs/ │ │ ├── models.py │ │ ├── views.py │ │ ├── urls.py │ │ ├── forms.py │ │ └── migrations/ │ └── users/ # 如果扩展登录功能 ├── data_process/ │ ├── crawler.py │ ├── clean_data.py │ ├── feature_engineering.py │ └── train_model.py ├── static/ │ ├── css/ │ ├── js/ │ └── vendor/ ├── templates/ │ ├── dashboard.html │ ├── job_list.html │ └── predict.html └── docs/ ├── 论文大纲.md └── 答辩PPT提纲.md

把爬虫、数据处理、模型训练独立成模块,和Django应用分开,是这套架构的关键决策。因为模型训练是一次性的离线过程,不应该和Web请求耦合在一起。你可以在自己电脑上跑完训练,把模型文件导出,Django启动时直接加载模型做推理,这样系统开销小,逻辑也清晰。

2.3 数据库设计与数据流转

数据流向大概是这样的:采集脚本把数据写入原始表,清洗脚本处理后生成标准表,Django的查询基于标准表;训练脚本读取标准表生成特征矩阵和模型文件;Web端既可以直接查表统计趋势,也可以调用模型做薪资预测。

数据库里建议建两张核心表。原始表(raw_job)记录采集到的原始文本,包括job_name、salary_text、city、edu、experience、company_name、company_size、finance_stage、skill_tags等字段。清洗后的表(job_info)则直接把薪资解析成数值型字段avg_salary,把学历、经验映射成等级数字。这样做的目的是:数据血缘清晰,论文里可以画出从数据库到模型再到页面的流转图,答辩时老师很难挑出“数据杂乱无章”的毛病。

3. 数据采集与清洗:分析结果的基石

3.1 采集公开招聘信息的基本思路

我不建议在毕设阶段把自己的时间耗在复杂的反爬对抗上。更合理的思路是:确定目标岗位和城市列表,比如“数据分析师”在北京、上海、广州、深圳、杭州这几个城市的岗位,采集公开列表页中每条职位的核心字段。

实际的采集脚本要具备三个基本能力:

  • 随机User-Agent和代理池切换(降低被识别为爬虫的风险)
  • 请求限速,每抓一页sleep 1到2秒
  • 解析失败重试机制,同一个URL最多重试3次

另外,如果某一字段解析失败,不要直接丢弃整条数据,而是先用占位符填充,留到清洗阶段处理。因为招聘页面的格式偶尔会变动,一条数据里的某个字段丢失很常见,整体丢弃会导致样本量缩水。

3.2 字段设计与薪资文本解析

原始数据里最麻烦的就是薪资字段。招聘平台通常不直接给数字月薪,而是展示成“15-25K·14薪”“2-3万/月·13薪”这种混合单位文本。要建模,必须统一转成“平均月薪(千元)”。

我一般用正则表达式处理:

import re def parse_salary(text): if not text: return None text = str(text) nums = re.findall(r'(\d+(?:\.\d+)?)', text) if not nums: return None if '-' in text: low = float(nums[0]) high = float(nums[1]) if len(nums) > 1 else low mid = (low + high) / 2 else: mid = float(nums[0]) if '千' in text: mid *= 1000 elif '万' in text: mid *= 10000 if '年' in text: mid = mid / 12 # 统一为千元/月 return round(mid / 1000, 2)

这里有三个细节很关键:一是“K”代表千元,需要乘以1000;二是部分岗位以“万/年”计薪,必须除以12换算成月薪,否则模型会把年薪误当成月薪,预测结果偏到离谱;三是“14薪”这类表述影响的是总收入,但对月薪建模来说可以暂时忽略,如果你想在论文里做得更细,完全可以构造一个“年薪总包”的标签再单独建模。

3.3 数据清洗与特征工程

清洗阶段的核心任务是让数据“干净且可计算”。主要做四件事:

第一,去重。同一公司同一岗位可能在多个页面出现,用company_name + job_name + city三字段做组合去重。

第二,缺失值处理。学历、经验为空时,用众数填充;技能标签为空时,填“无”。注意不要用平均值填充类别字段,那是常见错误。

第三,类别映射。学历和经验要转成有序数字,这个映射关系直接决定特征的可解释性:

edu_map = {'初中及以下': 0, '高中': 1, '中专': 2, '大专': 3, '本科': 4, '硕士': 5, '博士': 6} exp_map = {'经验不限': 0, '应届生': 1, '1-3年': 2, '1年以下': 1, '3-5年': 3, '5-10年': 4, '10年以上': 5}

城市、公司规模、融资阶段这种无序类别,不建议直接映射成数字,更适合用哑变量编码,或者在后续随机森林里直接类别编码,因为树模型对无序类别有一定容忍度。

第四,技能标签处理。把岗位要求里的“Python”“Java”“SQL”“数据分析”等关键词拆出来,统计每个岗位包含的技能数量,以及是否包含热门核心技能。这个字段在论文里可以做成词云,很出视觉效果。

3.4 数据质量检查

模型效果差,大多数时候不是算法问题,而是数据质量问题。我在跑完清洗后一定会做这几步检查:

  • 看薪资字段的分布,确认没有超出合理范围的异常值(比如月薪小于10元或大于100万元)
  • 看每个类别的样本数量,如果某个城市只有几十条数据,后面建模时它基本没有统计意义,可以直接合并成“其他城市”
  • 随机抽取10条清洗后的数据,人工核对原始文本和数值是否正确

这一步虽然枯燥,但能让后面的模型训练少走太多弯路。数据清洗在论文里也能独立成章,写出至少三页内容,属于典型的“低风险高回报”工作。

4. 随机森林建模与评估:从数据到预测能力

4.1 建模目标与特征矩阵

这个项目的建模目标是回归任务:输入岗位信息,输出平均月薪。不是分类,不是排序,而是预测一个连续值。选回归而不是分类的原因是你最终的Web页面要给出一个类似“预测薪资:21.5K/月”的结果,这对用户更直观。

特征矩阵大概包含这些列:

  • 城市编码
  • 学历等级(0到6)
  • 经验等级(0到5)
  • 公司规模编码
  • 融资阶段编码
  • 技能标签数量
  • 是否为核心岗位关键词(如“算法”“开发”)
  • 岗位类别编码(数据分析师、开发工程师、产品经理等)

这里有个很容易忽视的点:岗位类别对薪资影响很大,算法岗和运营岗的薪资差可能是两倍以上。但岗位名称是高基数类别特征,直接用原始字符串喂给模型效果很差。我建议先人工归类成“技术类”“产品类”“运营类”“设计类”“其他”这几个粗粒度类别,再编码成特征,模型的效果会显著提升。

4.2 模型训练与参数调优

训练代码本身很简单,难在参数选择。基线模型可以直接这么搭:

from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score import joblib X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) model = RandomForestRegressor( n_estimators=300, max_depth=20, min_samples_split=5, min_samples_leaf=2, random_state=42, n_jobs=-1 ) model.fit(X_train, y_train) # 评估 y_pred = model.predict(X_test) print("RMSE:", mean_squared_error(y_test, y_pred, squared=False)) print("MAE:", mean_absolute_error(y_test, y_pred)) print("R2:", r2_score(y_test, y_pred))

关于参数,我必须说实话:n_estimators并不是越大越好,超过300之后误差下降非常平缓,训练时间却线性增长;max_depth限制树深度可以有效防止过拟合,但要结合数据量,数据只有几千条时max_depth设在15到25之间比较合适。更严谨的做法是把候选参数丢给GridSearchCV:

from sklearn.model_selection import GridSearchCV param_grid = { 'n_estimators': [200, 300], 'max_depth': [10, 20, None], 'min_samples_split': [2, 5], } grid = GridSearchCV( RandomForestRegressor(random_state=42), param_grid, cv=5, scoring='r2', n_jobs=-1 ) grid.fit(X_train, y_train) best_model = grid.best_estimator_

网格搜索会慢一些,但会给你一个“我的参数是通过交叉验证选出来的”的底气。答辩时这句话的价值远大于“我试了好几次觉得这个参数效果好”。

4.3 模型评估与特征重要性

回归任务的评估报告至少要包含三项:R2分数、RMSE、MAE。R2通常在0.6到0.8之间就算不错,因为影响薪资的因素很多,模型只用岗位公开信息预测,不可能把个人能力和面试表现也预测出来。

随机森林最吸引人的一点是可以输出特征重要性,直接排序并打印:

importance = pd.Series( best_model.feature_importances_, index=feature_columns ).sort_values(ascending=False) print(importance)

实际跑下来你会发现,经验等级、学历等级、城市编码通常排在最前面,技能标签数量紧跟其后。这张特征重要性条形图一定要放进论文里,它是连接“算法”和“业务分析”的桥梁。你可以针对它写一小段业务解读:为什么经验比学历更重要、哪些岗位的技能标签对薪资提升更明显,让论文的分析章节不再是干巴巴的模型指标。

4.4 模型持久化与复用

训练完模型后,导出模型文件和特征列清单:

joblib.dump(best_model, 'model/salary_rf.pkl') # 同时保存特征列,预测时加载使用 import json with open('model/feature_columns.json', 'w', encoding='utf-8') as f: json.dump(feature_columns, f, ensure_ascii=False)

这里有一个特别容易踩的坑:训练时特征列的顺序是固定的,预测时如果输入特征的顺序变了,模型结果就完全乱了。所以一定要把feature_columns保存下来,Django端预测接口读取模型后,按照同样的列顺序重新构建输入向量。很多学生在这一步翻车,症结就是“训练预测不一致”。

5. Django可视化平台:模型落地的完整Web界面

5.1 Django项目结构与核心配置

Web端是整个项目的门面,也是答辩时评委停留时间最长的部分。Django部分我建议按功能拆分成两个app:一个负责岗位数据浏览和图表接口,一个负责薪资预测。这样代码不会全部堆在一个views.py里。

核心配置里有几个容易出问题的点:

数据库配置用pymysql连接MySQL:

DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'boss_db', 'USER': 'root', 'PASSWORD': 'your_password', 'HOST': '127.0.0.1', 'PORT': '3306', 'OPTIONS': {'charset': 'utf8mb4'}, } }

静态文件和模板路径一定要用BASE_DIR拼接,不要写绝对路径。Debug开关在部署演示时如果调成False,记得同时配置好STATIC_ROOT和whitenoise或者手动收集静态文件,否则页面样式会全部丢失。

5.2 可视化大屏设计与ECharts图表实现

可视化大屏是这个项目最出彩的部分。整体布局推荐经典的后台看板结构:顶部是标题和核心指标卡片,中间是岗位城市分布地图,下方并列展示热门技能词云、学历薪资对比柱状图和经验薪资折线图。

Django负责提供数据接口,比如返回城市岗位量和平均薪资:

from django.http import JsonResponse from django.db.models import Count, Avg from apps.jobs.models import JobInfo def city_chart_data(request): data = ( JobInfo.objects .values('city') .annotate(count=Count('id'), avg_salary=Avg('avg_salary')) .order_by('-count')[:20] ) result = [ {'name': item['city'], 'value': item['count'], 'avgSalary': round(item['avg_salary'], 2)} for item in data ] return JsonResponse({'code': 0, 'data': result})

前端用ECharts渲染,核心就是初始化图表,setOption时填入Ajax拿到的数据。这里我强烈建议后端把所有统计计算做完,前端只负责展示,不要用JavaScript再去算平均值或者过滤数据。原因很简单:答辩演示环境很可能没有外网,CDN的JavaScript和地图GeoJSON文件如果没下载到本地,页面就会白屏,到时候临时改代码是非常狼狈的。

5.3 薪资预测功能的表单到模型调用

薪资预测功能是证明“机器学习不是摆设”的关键。在Django里单独建一个predict页面,表单包含城市、岗位类别、学历、经验、公司规模、融资阶段这几个下拉框。提交后调一个独立的推理模块,加载之前保存的模型文件,进行预测。

推理模块可以这样设计:

import joblib import json import os from django.conf import settings class SalaryPredictor: _model = None _columns = None @classmethod def load(cls): if cls._model is None: model_path = os.path.join(settings.BASE_DIR, 'model', 'salary_rf.pkl') col_path = os.path.join(settings.BASE_DIR, 'model', 'feature_columns.json') cls._model = joblib.load(model_path) cls._columns = json.load(open(col_path, encoding='utf-8')) return cls._model, cls._columns @classmethod def predict(cls, features): model, columns = cls.load() X = [[features.get(col, 0) for col in columns]] return round(model.predict(X)[0], 2)

注意上面的写法用到了类变量缓存模型,只在首次请求时加载一次,避免每次预测都重新读文件拖慢响应。这个技巧在并发量不高的毕设系统里足够用,而且代码简单,答辩时也讲得清楚。

表单的映射关系要和训练时的编码完全一致。比如前端提交“本科”,后端要转成数字5;提交“北京”,要转成训练时北京对应的编码。这个映射字典最好和训练脚本共用,避免前后端各写一份导致对不上。

5.4 WebSocket实时推送与后台任务的进阶实现

如果你的题目或者评审老师比较关注“实时性”,可以为系统增加一个后台数据更新后推送到前端的功能。常见做法有两种:一种是前端定时轮询接口,简单稳定,适合入门;另一种是用Django Channels实现WebSocket,后台数据有更新时主动推送Message给前端页面。

这里给一个务实建议:只做展示用的话,轮询就够了,30秒请求一次图表接口,页面上的数据就会自动刷新。没必要为了展示WebSocket而引入异步消息队列,增加复杂度只会增加出Bug的概率。但如果你确实想写进论文里作为技术亮点,Channels结合Redis作为channel layer是标准方案。核心流程是:采集模块清洗完数据后,通过Channel Layer的group_send发消息,前端页面在WebSocket收到消息后调用ECharts的setOption刷新图表。这个功能写进论文的“系统创新点”是可以的,但最多占两页篇幅,不要喧宾夺主。

6. 远程联调、论文撰写与答辩演示的实战经验

6.1 从源码到一键运行:环境复现的坑

这个标题后面挂着“远程调试+讲解+定制”,所以源码交付必须要让别人能顺利跑起来。我自己在帮学生远程调试时,最常遇到问题的地方就三个:

第一,模型文件路径写死。训练代码在Windows上运行,模型路径是C:\Users\...\model\rf.pkl,换一台电脑或部署到云服务器就直接加载失败。解决方案是统一用相对路径,并且以工程根目录为基准拼接,Django里就用settings.BASE_DIR,普通脚本就用os.path.dirname(__file__)逐级向上找到项目根目录。

第二,Python版本不一致。有人用3.7跑Django 4就会直接报错,我建议在README开头就写清楚“本项目在Python 3.10测试通过”,并在requirements.txt里锁定所有第三方库版本,而不是只写django>=3.2这种宽松范围。

第三,数据文件编码。读CSV时一定要指定编码,encoding='utf-8-sig'能同时兼容Windows记事本带BOM头的问题。如果你给别人发代码,千万记得把数据库初始化脚本和示例数据一起发过去,没有数据是空谈。

6.2 论文写作结构建议

一套完整的毕设文档会直接影响评阅分。论文结构可以这样安排:

章节内容要点对应本项目的素材
绪论研究背景、国内外现状招聘市场信息不对称、薪资不透明问题
相关技术Django、随机森林、可视化技术原理介绍加为什么选它
需求分析功能性需求、非功能性需求数据采集、清洗、可视化、预测的用例图
系统设计整体架构、数据库设计、模块设计数据流转图、ER图、接口文档
数据采集与处理采集方案、清洗规则、特征工程这部分最容易写,代码和截图直接放
算法模型随机森林原理、训练过程、评估指标调参表格、ROC曲线之外再加R2指标
系统实现Web页面截图、核心代码讲解可视化大屏和预测页面
测试功能测试、模型性能测试测试用例表、预测误差统计

论文字数一般要求1万到2万字,按这套结构推进,内容完全够写,而且每章节都有实际产物支撑,不会出现“编数据”的尴尬。

6.3 答辩演示的黄金路径

答辩现场时间有限,演示顺序非常关键。我的建议是“先看结果,再讲过程”:打开系统页面,先让评委看到可视化大屏和薪资预测效果,把“指标卡片、城市地图、薪资预测”这几个高视觉冲击力的页面轮流展示一遍;然后回到PPT,简单讲解技术架构和数据流转;最后如果评委问算法细节,再展开讲随机森林原理和调参过程。

为什么这样排?因为评委对项目的第一印象往往决定了后续提问的宽松程度。一上来就翻代码和数据库表,很难在短时间内建立项目“完整度高”的印象。另外,要把模型预测演示准备成“输入一个带偏激条件的岗位”,比如“成都、初中及以下学历、无经验、创业公司”,让预测结果明显偏低,形成话题点,评委通常会顺着这个例子继续追问,你提前准备好解释就好。

6.4 二次定制扩展的方向

如果你的毕设需要增加差异化,或者讲师建议补充创新点,可以考虑这些方向:

  • 算法对比:加一个决策树和线性回归模型,和随机森林做R2对比,论文里形成模型对比实验表
  • 用户系统:用Django自带认证实现注册、登录和求职者个性化收藏,导向“个性化推荐系统”
  • 薪资推荐:基于相似岗位的历史平均薪资,用一个协同过滤思路,把“预测薪资”升级为“推荐的薪资区间”
  • 部署上线:把项目打包到云服务器,用Gunicorn + Nginx部署,文档里加一节“系统部署交付”

扩展的方向不少,但我建议不要贪多。一个核心创新点加一个辅助功能,对于本科毕设来说已经是比较完整的体量,重点是把已有的功能做到稳定、好演示。

最后说一点个人体会。这类项目做下来,我发现最容易翻车的从来不是模型算法,而是数据准备和工程细节。训练一个随机森林模型只需要几分钟,但从薪资文本里准确解析出数值、把学历经验编码到模型能用的特征、让Django加载模型时保持特征顺序一致,这些“脏活累活”反而决定了整个项目能不能跑通。希望这篇拆解能帮你少走一些弯路,在做毕设或者带项目时把精力花在真正有价值的地方。

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

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

立即咨询