ARTICLE DETAIL

资讯详情

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

基于招聘数据的会计专业薪资预测:从爬虫到机器学习模型实战

基于招聘数据的会计专业薪资预测:从爬虫到机器学习模型实战 简介这份资源面向会计、财务及数据科学方向的在校生与研究者提供一套完整的机器学习薪资预测实战方案帮助解决会计专业毕业生薪资影响因素分析与预测建模问题属于数据挖掘方向的中等难度项目。压缩包共11个文件约11MB包含Python爬虫与建模脚本、CSV原始数据集、Jupyter Notebook分析文件、可视化HTML页面、词云图、实验报告文档及停用词库等覆盖从数据采集、清洗到建模评估的全流程。目前已有100人学习下载。读者可获得7000字实验报告系统梳理研究背景、特征工程与模型对比思路同时拿到可复用的爬虫脚本与数据集直接复现各城市岗位数量分布、公司福利词云等分析结果并借助Notebook逐步理解薪资预测的建模细节适合作为课程设计、毕业设计或数据挖掘入门项目的参考模板。1. 从一份会计专业薪资预测包说起数据挖掘能落地到什么程度会计专业毕业生薪资预测听起来像是一个典型的“课程作业”题目但真正动手做过的人知道这里面藏着不少门道。这份资源包给出的不是一份空泛的论文模板而是一套完整的、可复现的机器学习实践链路从招聘网站采集岗位数据到清洗、特征工程、模型训练再到一份7000字的实验报告。它解决的核心问题是——如何用真实招聘数据构建一个能预测会计专业毕业生起薪的回归模型并且把整个过程讲清楚、写明白。适合谁用如果你是数据挖掘或机器学习课程的在校生需要一份有真实数据、有代码、有分析过程的参考项目这份资源能直接帮你省掉从零爬数据、洗数据的痛苦。如果你是从业者想快速了解招聘数据从采集到建模的完整流程里面的爬虫脚本、特征处理思路和模型对比逻辑也能当作一个可运行的起点。它不承诺预测精度有多高但把“数据从哪来、怎么变成特征、模型怎么选、结果怎么解释”这条链路走通了。2. 拆开压缩包文件清单与数据采集链路2.1 每个文件在流程里扮演什么角色拿到一个压缩包先别急着跑代码。把文件按功能分类能帮你快速判断这套东西能不能用、缺什么。这份资源包里的文件大致可以分成四类文件类型作用spider.py爬虫脚本从招聘网站抓取会计相关岗位的原始数据chromedriver.exe驱动配合 Selenium 控制浏览器处理动态加载页面manager.py管理脚本可能用于调度爬虫、数据存储或流程控制job_data.csv数据集爬取后的原始岗位数据是后续分析的起点password.csv辅助数据从命名推测可能是账号或配置信息需检查内容停用词库.txt文本资源用于福利词云图生成时的中文停用词过滤分析.ipynbNotebook数据清洗、特征工程、建模、可视化的主流程分析.html报告导出Notebook 的 HTML 导出方便直接查看分析结果各城市岗位数量分布.html可视化城市维度岗位数量的交互式图表公司福利词云图.png可视化福利关键词的词云图报告.docx文档7000字实验报告包含背景、方法、实验、结论注意password.csv 这个文件名容易让人误解打开前先确认里面是不是明文账号密码。如果是不要直接上传到公开仓库也不要在报告里截图展示。2.2 爬虫脚本的核心逻辑与运行前提spider.py 是整个数据链路的起点。常见做法是用 Selenium 模拟浏览器操作因为招聘网站的岗位列表通常是 JavaScript 动态渲染的直接 requests 拿不到完整数据。chromedriver.exe 就是配合 Selenium 使用的浏览器驱动版本必须和本机 Chrome 浏览器匹配否则会报 SessionNotCreatedException。运行爬虫前先确认三件事本机 Chrome 版本、chromedriver 版本、目标网站是否改了页面结构。前两个不匹配脚本直接起不来第三个变了选择器失效抓到的数据会是空的。# spider.py 典型结构示意基于常见 Selenium 爬虫模式 from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import csv import time # 初始化浏览器驱动chromedriver.exe 需与 Chrome 版本匹配 driver webdriver.Chrome(executable_path./chromedriver.exe) driver.get(目标招聘网站搜索页URL) # 显式等待岗位列表加载完成避免拿到空页面 wait WebDriverWait(driver, 10) job_list wait.until( EC.presence_of_all_elements_located((By.CSS_SELECTOR, .job-list-item)) ) results [] for job in job_list: try: title job.find_element(By.CSS_SELECTOR, .job-title).text salary job.find_element(By.CSS_SELECTOR, .salary).text city job.find_element(By.CSS_SELECTOR, .city).text company job.find_element(By.CSS_SELECTOR, .company-name).text results.append([title, salary, city, company]) except Exception as e: # 单条解析失败不影响整体记录后继续 print(f解析失败: {e}) continue # 写入 CSV编码用 utf-8-sig 防止 Excel 打开乱码 with open(job_data.csv, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([岗位, 薪资, 城市, 公司]) writer.writerows(results) driver.quit()这段代码的关键点有三个显式等待WebDriverWait比 time.sleep 更稳因为它是条件触发而不是固定延时单条解析用 try-except 包住避免一条数据格式异常导致整个爬虫崩掉CSV 写入用 utf-8-sig 编码否则用 Excel 打开会看到中文乱码。参数方面WebDriverWait 的 10 秒可以根据网络情况调整页面加载慢就加到 15 或 20选择器如 .job-list-item需要根据目标网站的实际 DOM 结构替换不能照抄。2.3 数据落盘后的第一轮检查爬完数据别急着进 Notebook。先打开 job_data.csv 看几行确认字段是否完整、有没有大量空值、薪资列是不是统一格式。常见问题是薪资写成“6-8千/月”“1.5-2万/月”“面议”混在一起后面做回归时必须先统一成数值。这一步在 分析.ipynb 里会处理但提前看一眼能帮你判断数据质量避免跑完整个流程才发现原始数据有问题。3. 从原始 CSV 到特征矩阵清洗、编码与薪资解析3.1 薪资字段的数值化处理会计专业毕业生的薪资数据原始形态通常是“6-8千/月”“4.5-6千/月”“面议”这类字符串。回归模型要的是数值所以第一步是把薪资区间转成单一数值。常见做法是取区间中位数再统一单位到“元/月”。import pandas as pd import re df pd.read_csv(job_data.csv) def parse_salary(s): 将薪资字符串转为月薪中位数元 if pd.isna(s) or 面议 in str(s): return None s str(s).replace( , ) # 匹配 6-8千/月 1.5-2万/月 8000-10000元/月 等格式 match re.search(r([\d.])-([\d.])([千万元]), s) if not match: return None low, high, unit float(match.group(1)), float(match.group(2)), match.group(3) if unit 千: low, high low * 1000, high * 1000 elif unit 万: low, high low * 10000, high * 10000 # 元 单位不做转换 return (low high) / 2 df[薪资数值] df[薪资].apply(parse_salary) # 丢弃无法解析的行避免后续建模报错 df df.dropna(subset[薪资数值])parse_salary 函数的逻辑是先用正则提取区间上下限和单位再按单位换算成元最后取中位数。参数方面正则里的([\d.])支持小数如 1.5 万([千万元])覆盖了三种常见单位。如果目标网站还有“年薪”格式需要额外加分支处理。丢弃无法解析的行是必要的否则模型训练时会因为 NaN 报错。3.2 城市与岗位特征的编码策略城市和岗位名称是类别特征不能直接喂给模型。常见做法有两种独热编码One-Hot和目标编码Target Encoding。对于城市这种取值有限的字段独热编码更直观对于岗位名称这种取值较多的字段目标编码能避免维度爆炸。# 城市独热编码限制类别数量避免维度过多 top_cities df[城市].value_counts().nlargest(10).index df[城市_简化] df[城市].apply(lambda x: x if x in top_cities else 其他) city_dummies pd.get_dummies(df[城市_简化], prefix城市) # 岗位名称用目标编码用该岗位的薪资均值替代类别 job_mean_salary df.groupby(岗位)[薪资数值].mean() df[岗位_编码] df[岗位].map(job_mean_salary) # 合并特征 features pd.concat([df[[薪资数值, 岗位_编码]], city_dummies], axis1)城市独热编码只保留前 10 个高频城市其余归为“其他”这样既保留了主要信息又不会让特征矩阵太稀疏。岗位目标编码用均值替代好处是直接引入了与目标变量相关的信息但要注意数据泄露风险——如果训练集和测试集用同一份均值测试集的编码会包含训练集信息。更严谨的做法是在交叉验证内部计算均值但作为课程项目直接用全量均值也能跑通。3.3 福利词云图的生成逻辑公司福利词云图.png 不是装饰品它反映的是招聘文本中高频出现的福利关键词。生成逻辑通常是把岗位描述或福利字段拼接成一个大文本用 jieba 分词加载停用词库.txt 过滤无意义词再用 wordcloud 库生成图像。import jieba from wordcloud import WordCloud # 读取停用词 with open(停用词库.txt, r, encodingutf-8) as f: stopwords set(f.read().splitlines()) # 假设福利文本在 福利 列 text .join(df[福利].dropna().astype(str)) words [w for w in jieba.cut(text) if w not in stopwords and len(w) 1] wc WordCloud(font_pathsimhei.ttf, width800, height400, background_colorwhite).generate( .join(words)) wc.to_file(公司福利词云图.png)关键参数是 font_path不指定中文字体词云图会显示成方块。停用词库的作用是过滤“的”“了”“和”这类高频但无意义的词让词云真正反映福利内容。如果词云图里出现大量“五险一金”“带薪年假”“节日福利”说明数据采集和清洗是有效的。4. 模型训练与实验报告从线性回归到集成模型4.1 为什么选回归而不是分类薪资预测的目标变量是连续数值月薪中位数所以这是一个回归问题不是分类。常见做法是先跑一个线性回归作为基线再尝试决策树、随机森林、梯度提升等模型对比 RMSE 和 R²。实验报告里通常会列出多个模型的对比表格说明为什么最终选了某个模型。from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_squared_error, r2_score import numpy as np X features.drop(薪资数值, axis1) y features[薪资数值] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) # 基线模型线性回归 lr LinearRegression() lr.fit(X_train, y_train) lr_pred lr.predict(X_test) # 集成模型随机森林 rf RandomForestRegressor(n_estimators100, random_state42) rf.fit(X_train, y_train) rf_pred rf.predict(X_test) for name, pred in [(线性回归, lr_pred), (随机森林, rf_pred)]: rmse np.sqrt(mean_squared_error(y_test, pred)) r2 r2_score(y_test, pred) print(f{name} — RMSE: {rmse:.0f}, R²: {r2:.3f})随机森林的 n_estimators 设为 100 是常见起点树太少容易欠拟合太多则训练变慢。random_state 固定后结果可复现。RMSE 的单位是元解释起来比 MSE 直观R² 反映模型解释了多大比例的薪资方差但要注意它会被异常值拉低。4.2 实验报告的结构与写作要点报告.docx 是这份资源的另一个核心产出。7000 字的实验报告通常包含研究背景与意义、数据采集与预处理、特征工程、模型构建与对比、结果分析与结论。写作时容易犯的错是把报告写成代码注释的堆砌或者把结论写成“模型效果良好”这种空话。更实用的写法是在结果分析部分具体说明哪个特征对薪资影响最大比如城市和岗位编码的系数模型在哪些样本上预测偏差大比如高薪岗位被低估以及数据本身的局限性比如样本量、行业覆盖。这些内容能让报告从“作业”变成“有分析深度的文档”。4.3 可视化 HTML 的生成方式各城市岗位数量分布.html 和分析.html 通常是 Plotly 或 Pyecharts 生成的交互式图表。Plotly 的优势是可以在 Notebook 里直接显示导出 HTML 后也能独立打开。import plotly.express as px city_counts df[城市].value_counts().reset_index() city_counts.columns [城市, 岗位数量] fig px.bar(city_counts.head(15), x城市, y岗位数量, title各城市会计岗位数量分布) fig.write_html(各城市岗位数量分布.html)这段代码生成的是一个柱状图展示岗位数量最多的前 15 个城市。write_html 导出的文件可以直接用浏览器打开不依赖 Python 环境。如果要在报告里嵌入截图或导出为静态图片也行。5. 避坑与排查跑通这套代码的五个血泪经验5.1 chromedriver 版本不匹配爬虫直接起不来现象运行 spider.py 后报 SessionNotCreatedException提示“This version of ChromeDriver only supports Chrome version XX”。原因本机 Chrome 自动更新后chromedriver.exe 还是旧版本。解决打开 Chrome 设置里的“关于 Chrome”查看版本号去 chromedriver 官网下载对应版本替换。如果不想每次手动换可以用 webdriver-manager 库自动管理。5.2 薪资解析遗漏格式导致大量数据被丢弃现象parse_salary 跑完后df 行数从几千变成几百。原因正则只覆盖了“千/万/元”三种单位但实际数据里还有“年薪”“日薪”“小时”等格式。解决先统计薪资列的所有唯一格式再针对性扩展正则。或者先用df[薪资].str.contains(年)筛出年薪样本单独处理。5.3 目标编码数据泄露测试集 R² 虚高现象随机森林的 R² 达到 0.95 以上但换一批数据预测效果骤降。原因岗位编码用了全量数据的薪资均值测试集的编码里包含了训练集信息。解决在交叉验证内部计算目标编码或者改用 K-Fold 方式生成编码。课程项目里如果只是演示流程可以在报告里注明这一局限性。5.4 词云图中文显示为方块现象公司福利词云图.png 里全是方框看不到汉字。原因WordCloud 默认字体不支持中文。解决指定 font_path 参数指向本机的中文字体文件如 simhei.ttf、msyh.ttf。Windows 系统一般在 C:\Windows\Fonts 下Linux 需要单独安装中文字体包。5.5 Notebook 导出 HTML 后图表不显示现象分析.html 打开后Plotly 图表区域是空白。原因Notebook 导出时没有嵌入 Plotly 的 JavaScript 库或者导出方式不对。解决用fig.write_html(分析.html, include_plotlyjscdn)确保 JS 库被引用或者用 nbconvert 导出时加上--to html --template classic参数。6. 进阶技巧用交叉验证和特征重要性把模型讲清楚跑通基础流程后真正让这份资源从“能跑”变成“能写进报告”的是两件事交叉验证和特征重要性分析。交叉验证能告诉你模型在不同数据划分下的稳定性特征重要性则能解释“为什么模型给出这个预测”。from sklearn.model_selection import cross_val_score from sklearn.ensemble import RandomForestRegressor import pandas as pd rf RandomForestRegressor(n_estimators100, random_state42) # 5 折交叉验证评估模型稳定性 scores cross_val_score(rf, X, y, cv5, scoringneg_mean_squared_error) rmse_scores np.sqrt(-scores) print(f交叉验证 RMSE: {rmse_scores.mean():.0f} ± {rmse_scores.std():.0f}) # 训练后查看特征重要性 rf.fit(X_train, y_train) importance pd.Series(rf.feature_importances_, indexX.columns) print(importance.sort_values(ascendingFalse).head(10))交叉验证的 cv5 表示把数据分成 5 份轮流用 4 份训练、1 份验证。输出的 RMSE 均值和标准差能反映模型是否稳定——如果标准差很大说明模型对数据划分敏感可能需要更多数据或更简单的模型。特征重要性输出的是每个特征对预测的贡献度排在前面的通常是城市、岗位编码这类强相关特征。把这两个结果写进报告比只列一个 R² 有说服力得多。还有一个实用技巧把预测结果和真实值做一个散点图看模型在哪个薪资区间偏差最大。如果低薪区间预测偏准、高薪区间普遍低估说明模型对高薪样本学习不足可以在报告里讨论样本不平衡的问题。import matplotlib.pyplot as plt plt.scatter(y_test, rf_pred, alpha0.5) plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], r--) plt.xlabel(真实薪资) plt.ylabel(预测薪资) plt.title(预测值与真实值对比) plt.savefig(预测对比图.png, dpi150)这张图能直观暴露模型的系统性偏差。红线是理想情况预测等于真实散点越靠近红线越好。如果散点在高薪区域明显低于红线说明模型低估了高薪岗位原因可能是高薪样本太少或者特征里缺少区分高薪的关键信息如学历要求、工作经验。从那以后我每次拿到一份带 Notebook 的资源包都会先跑一遍交叉验证和特征重要性再决定要不要把结果写进报告。这两个步骤花不了多少时间但能让分析从“跑通了”变成“讲得清”。希望帮到你。本文还有配套的精品资源点击获取
返回列表