Python爬取Boss直聘数据:从采集到薪资分析全流程

发布时间:2026/9/12 17:50:42
Python爬取Boss直聘数据:从采集到薪资分析全流程 简介面向求职市场分析与期末作业场景的招聘数据处理案例基于Boss直聘真实招聘数据构建适合高校学生、初入数据分析岗位的读者作为课程设计或期末项目参考。项目系统梳理了数据分析的标准流程从数据清洗、缺失值填充与格式统一到探索性统计分析通过直方图、箱线图、散点图发现薪资与行业、经验等维度规律再结合线性回归、决策树、随机森林等模型训练与测试集按七三划分并引入交叉验证用于预测岗位投递量或面试获取概率。资源为zip压缩包大小约12.51MB其中主文件夹包含数据集、分析报告、代码脚本等模块便于解压后按步骤复现。目前已有586人学习/下载。该案例在业务理解、图表制作、模型评估和可视化报告上都有实操演示既能节省从零搭建框架的时间也能加深对数据处理全链路和商业洞察力的理解。1. Boss数据分析案例为什么是期末作业的稳妥选型对于一门数据分析课或者商业分析课的期末作业选题往往比建模更重要。选一个拿不到数据、或数据量太小撑不起分析链路的题目后面每一步都会很被动。Boss直聘在招聘数据分析里属于数据获取门槛较低、字段结构化程度又比较高的数据源职位名称、薪资区间、经验要求、学历门槛、城市、公司规模这些字段可以直接拿来做概率统计、分组对比和回归分析做数据分析项目的性价比很高。这类项目的典型做法是先抓取某几个城市、某几个岗位方向的招聘 JD 作为观察样本然后走完“清洗 - 字段标准化 - 分组统计 - 可视化 - 结论”这条完整链路。它不需要分布式环境一台个人电脑跑 Pandas 就够了也便于在期末答辩时展示每一张图背后的真实业务含义。下面这套方案以 Python 为技术栈以 Boss 直聘网页端可见的公开职位信息为数据源完整覆盖从数据采集、薪资拆解到结论报告的所有环节。2. 用 Python 采集 Boss 直聘公开职位信息先建数据管道2.1 采集技术选型Requests 为主、Selenium 兜底不重装备Boss 直聘网页端在未登录状态下可以浏览部分职位列表但接口层面有签名参数和风控策略直接构造请求参数比较耗时。常见做法是先用 Requests 请求搜索列表页解析其中的 JSON 数据如果触发验证或拿不到数据再降级用 Selenium 启动浏览器模拟滚动加载。后者效率低但作为期末作业更稳妥因为不依赖逆向分析加密参数代码逻辑也更容易在答辩时讲清楚。我一般会将采集框架设计成三层请求层负责向目标地址发送带伪装头部的请求解析层负责从 HTML 或 JSON 中抽取职位字段存储层统一落地为 CSV方便后续用 Pandas 读取。这样一个结构清晰的管道在后面做数据分析时能省去大量重复性调试工作。2.2 采集岗位信息的最小可运行模型下面这段代码以“数据分析师”为搜索词、以“北京”为城市条件抓取列表页返回的职位卡片信息包括职位名称、薪资、经验、学历、公司名、公司规模。示例中使用了 requests 库并模拟了基础浏览器环境。import requests import csv headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36, Referer: https://www.zhipin.com/web/geek/job, Accept-Language: zh-CN,zh;q0.9, } def fetch_jobs(page1, city_code101010100): # 列表页接口query 为检索关键词 params { query: 数据分析师, city: city_code, page: page, } # 注意实际部署时应从网络请求面板中获取最新的请求地址 url https://www.zhipin.com/wapi/zpgeek/search/joblist.json resp requests.get(url, headersheaders, paramsparams, timeout10) data resp.json() # 接口外层是 zpData内层 jobList 才是职位数组 job_list data.get(zpData, {}).get(jobList, []) rows [] for job in job_list: # 有的字段可能为空做兜底处理 rows.append({ job_name: job.get(jobName, ), salary: job.get(salaryDesc, ), experience: job.get(jobExperience, ), education: job.get(jobDegree, ), company: job.get(brandName, ), company_size: job.get(brandScaleName, ), city: job.get(cityName, ), area: job.get(areaDistrict, ), }) return rows if __name__ __main__: all_rows [] for page in range(1, 6): # 抓前五页单页约 30 条 try: all_rows.extend(fetch_jobs(page)) except Exception as exc: print(f第 {page} 页失败: {exc}) continue with open(jobs_raw.csv, w, newline, encodingutf-8-sig) as f: writer csv.DictWriter(f, fieldnamesall_rows[0].keys()) writer.writeheader() writer.writerows(all_rows) print(f共采集 {len(all_rows)} 条职位记录)代码逻辑并不复杂先构建请求参数把“数据分析师”作为检索关键词传入接口然后解析返回 JSON 中职位列表。每个职位信息被抽取为字典最后统一写入 CSV 文件。需要注意两点第一编码使用utf-8-sig是为了让 Excel 直接打开不出现中文乱码第二采集页面数不宜过大期末作业 100 到 200 条样本已经足够支撑分组统计和显著性检验过大的样本量反而会暴露反爬问题。2.3 遇到验证码或风控时的降级处理如果请求频率过高网页端会弹出安全验证这时再用 requests 硬解析就不现实了。我建议把 Selenium 作为降级方案先手动完成登录或验证然后通过 Selenium 获取渲染后的页面源码再用 pandas 的read_html或者正则表达式抽取职位卡片数据。这种方式相对笨重但能够应对大多数交作业场景。另外每次请求之间加 2 到 4 秒随机休眠比用代理池更实用也更符合合规边界——采集范围控制在教学演示和个人学习场景的合理频度内。3. 薪资字段深水区解析“15-25K·14薪”这类复合薪资数据3.1 为什么薪资字段不能直接用于数据分析Boss 直聘展示的薪资是自然语言文本比如“15-25K·14薪”“10-20K·15薪”“200-300元/天”。如果直接把这一列当作数值参与均值计算Pandas 会直接报错因为字符串无法做算术运算。这个问题是所有以招聘平台为数据的分析项目必须迈过去的坎也是数据分析基本流程里最见功力的一步。常见做法是把薪酬文本拆成三个部分月薪区间下限、月薪区间上限、发薪月数。拆解逻辑要能同时兼容三种形态纯月薪、月薪加薪期、日薪实习岗。建议在数据清洗阶段写好一个函数统一处理输出结构化字段而不是反复在 Excel 里手动改值。3.2 用正则表达式实现薪资区间解析下面这段代码把原始薪资文本转换为数值字段同时新增“月薪中位数”和“年薪估算”两个衍生字段。派生字段在后面的回归分析和对比统计中会发挥重要作用。import pandas as pd import re df pd.read_csv(jobs_raw.csv) def parse_salary(text): if not isinstance(text, str): return None, None, None text text.strip() # 匹配“15-25K·14薪”或“15-25K•14薪”这类区间结构 range_match re.search(r(\d)-(\d)K, text) # 匹配年终或总薪月数 months_match re.search(r(\d)薪|(\d)薪, text) lower, upper None, None if range_match: lower int(range_match.group(1)) upper int(range_match.group(2)) months 12 # 默认按 12 薪拿不到就按最保守口径 if months_match: months int(months_match.group(1) or months_match.group(2)) # 日薪岗单独处理按每月 21.75 个工作日折算成月薪 if 元/天 in text: day_match re.search(r(\d), text) if day_match: daily int(day_match.group(1)) lower upper round(daily * 21.75 / 1000, 1) # 单位转换为 K return lower, upper, months df[salary_low], df[salary_high], df[salary_months] zip( *df[salary].apply(parse_salary) ) df[salary_mid] (df[salary_low] df[salary_high]) / 2 df[annual_salary] df[salary_mid] * df[salary_months] print(df[[salary, salary_low, salary_high, salary_mid, annual_salary]].head())正则在这里起的作用是模式识别。数字区间用(\d)-(\d)K捕获因为它能匹配到区间两端的数值总薪月数用别的方式来匹配具体写法要看薪资文本里使用的是点是还是星号。需要特别注意的是将日薪岗折算为月薪时用 21.75 是劳动法规中的月计薪天数这个口径在报告里需要写明否则答辩时解释不清楚。另一种处理方式是不折算、单独剔除日薪样本适用于只想看全职岗位薪资分布的项目。3.3 薪资解析的边界条件和异常处理薪资文本里还可能出现“薪资面议”“4-6K·13薪”“25-40K·16薪”等情况。第一类直接置空分析时剔除第二、三类常规解析已经覆盖。如果遇到“4000-6000元/月”这种不带 K 的写法上面的正则匹配不到需要单独加一个条件分支将数值除以 1000 后再复用区间构造逻辑。这种边界设计要提前想到因为 Python 数据分析项目里最耗时间的往往不是建模而是处理这种跨平台的口径不一致问题。4. 招聘数据清洗与可视化分析从多维度挖掘薪资和技能需求4.1 数据清洗的先手操作去重、裁剪异常值、切分技能词当薪资字段结构化完成后整个数据集就已经具备了做统计分析的基本形态但仍有几个容易忽略的清洗步骤需要优先处理。第一步是按职位名称和公司名做去重招聘平台经常出现同一公司多个 HR 发布同一个岗位的情况不去重会造成统计偏差。第二步是裁剪异常薪资例如将月薪中位数低于 3K 或高于 100K 的样本剔除因为这些数据多半是兼职、实习或信息录入错误。下面这段代码集中处理去重、裁剪学历经验字段的取值空间并抽取技能关键词。# 去除重复值同一公司 同一职位名 同一薪资视为一条 df df.drop_duplicates(subset[company, job_name, salary_low]) # 月薪中位数在 3K 到 100K 之间保留过滤异常样本 df df[(df[salary_mid] 3) (df[salary_mid] 100)] # 经验字段只保留常见枚举值 allowed_exp [经验不限, 1-3年, 3-5年, 5-10年, 10年以上] df df[df[experience].isin(allowed_exp)] # 从职位名称里提取技能关键词用于后续技能-薪资分析 skill_keywords [Python, SQL, Excel, Tableau, PowerBI, 机器学习, 数据挖掘] for skill in skill_keywords: df[skill] df[job_name].apply( lambda x: 1 if skill.lower() in str(x).lower() else 0 ) print(df[skill_sample] if False else df.groupby(experience)[salary_mid].mean())这段代码的价值不只是跑通流程而是给后续分析铺平道路。构造的每个技能字段都是一个二元变量后面做独立样本均值差检验时可以直接用 t 检验来判断“要求 Python 的岗位薪资是否显著高于不要求 Python 的岗位”。这一步是很多数据分析案例的进阶加分项也是区分两个相近方案质量差异的关键把文本化 JD 转成结构化特征使数据产生更多可计算、可验证的信息。4.2 用 Python 数据分析与可视化直出第一张业务图学历对薪资的影响数据清洗完成后先出一张分组柱状图看学历要求与月薪中位数的关系。可视化工具推荐使用 pyecharts 或 matplotlib前者生成的 HTML 图表可以动态查看交互效果后者更适合直接嵌入期末报告。下面以 pyecharts 为例因为它更贴近业务数据看板的展示习惯。from pyecharts.charts import Bar from pyecharts import options as opts edu_order [大专, 本科, 硕士, 博士] edu_salary df.groupby(education)[salary_mid].mean().reindex(edu_order) bar ( Bar() .add_xaxis(edu_order) .add_yaxis(平均月薪(K), [round(v, 1) for v in edu_salary.values]) .set_global_opts( title_optsopts.TitleOpts(title不同学历要求的平均月薪对比), yaxis_optsopts.AxisOpts(name月薪(K)), ) ) bar.render(salary_by_education.html)这张图的信息密度很高横轴是学历要求纵轴是平均月薪看到本科到硕士的涨幅就能判断当前数据分析师岗位的学历溢价。如果样本量足够还可以再按城市维度拆分观察一线城市和非一线城市之间学历溢价的差异。这个分析角度在答辩时非常讨巧因为多数作业只停留在统计均值层面缺少交叉维度。4.3 构建热力图城市、经验、薪资的三维透视接下来把城市和经验两个维度同时放进热力图里观察薪资热区。执行下面这段代码前需要确保数据里 contain 了 city 和 experience 两个字段。透视表能提供一个清晰的交叉分析视角这是期末作业中非常宝贵的一种分析视角。pivot pd.pivot_table( df, valuessalary_mid, indexcity, columnsexperience, aggfuncmean, ) pivot pivot.reindex(columns[经验不限, 1-3年, 3-5年, 5-10年]) import seaborn as sns import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] plt.figure(figsize(10, 6)) sns.heatmap(pivot, annotTrue, fmt.1f, cmapYlGnBu) plt.title(城市 x 经验 平均月薪热力图(K)) plt.tight_layout() plt.savefig(city_experience_heatmap.png, dpi150)热力图呈现的是二维信息读图逻辑是横向看同一城市不同经验段的薪资涨幅纵向看同一经验段跨城市的薪资差异。实际呈现时通常会在地理分布上带来差异这能支撑“区域薪资差异显著”“经验积累带来的薪资溢价在城市间不同”的结论。pivot 表格的填充值选择 mean 而不是 sum是因为这里比较的是平均薪资聚合口径必须先于可视化确定否则图出错了也很难察觉。4.4 技能需求词频分析定位岗位核心竞争力技能模块的词频分析是整个案例里最容易被忽略、但又极具信息量的一环。做法很简单从 JD 描述中搜索技能词是否出现统计出现频次然后输出 Top 技能排名。更精细的可以做共现分析比如统计同时要求 Python 和 SQL 的岗位占比这个指标对公司确定人才标准、对学生规划学习方向都很有参考价值。skill_cols [Python, SQL, Excel, Tableau, PowerBI, 机器学习, 数据挖掘] freq df[skill_cols].sum().sort_values(ascendingFalse) # 同时要求 Python 和 SQL 的岗位占比 both ((df[Python] 1) (df[SQL] 1)).mean() print(PythonSQL 岗位占比: {:.1%}.format(both))词频统计的结果建议画成横向条形图更符合阅读习惯。渲染图时注意中文字体问题Windows 使用 SimHei 可以避免乱码。如果这一层分析能结合薪资取均值就会形成“技能 - 需求频率 - 薪资溢价”的完整叙事链条整个案例的内容厚度会明显提升。5. 从“能跑通”到“能拿高分”验证分析结论的 4 个进阶技巧期末作业的评分维度通常有三个数据量是否够用、分析链路是否完整、结论是否经得起追问。数据量可以通过多城市多关键词采集来解决链路通过本文前四章可以完整搭好但结论的可靠性要专门补一步——做验证。下面这 4 个技巧能直接提升作业完成度。第一个技巧是对比验证。比如前面计算了不同学历的薪资差异可以再用 pyecharts 做一个城市级分组箱线图看中位数差异是否依然稳定存在。箱线图能展示数据分布和离群点若中位数趋势与均值趋势一致结论的可信度会更高若相反说明均值受到极值影响需要在报告中如实指出。第二个技巧是分组统计卡方检验这是招聘数据分析和商业数据分析中常用到的方法。可以用卡方检验判断学历要求与经验要求之间是否存在关联性。用 scipy.stats 实现只需一行代码但需要先把两列变量转为列联表。这一步能看出分析是否真正理解统计方法的应用场景而不是只把结果表现的图表堆在一起。from scipy.stats import chi2_contingency import pandas as pd ct pd.crosstab(df[education], df[experience]) chi2, p, dof, expected chi2_contingency(ct) print(f卡方值: {chi2:.2f}, p值: {p:.4f}) # 当 p 值小于 0.05 时认为学历与经验之间存在显著关联第三个技巧是结论反推。在报告末尾做一个“岗位画像”小节把薪资最高的 Top 岗位的共性条件列出来比如“一线城市 硕士 5-10 年 同时掌握 Python 和机器学习”。然后反向拆解一个薪资偏低的岗位样本说明它在哪些条件上缺失。这种写法很直观也是从数据到业务洞察的常见表达方式比单写“薪资受多种因素影响”更有信息含量。第四个技巧是为代码仓库写一个 README记录运行环境、采集时间、样本量、清洗规则和图表清单。期末作业答辩时能快速翻出关键信息也可作为后续面试的作品集展示。注意数据量不要写虚采集了多少条就写多少条清洗后保留多少条也要标注这恰恰是数据分析项目真实的执行过程。这条完整链路跑完后你会得到一份包含原始数据、清洗脚本、图表、统计检验结果和结论报告的数据分析项目。它不依赖任何商业数据库也无须在答辩时演示复杂环境一台装好 Python 和 Jupyter 的电脑就能复现全部结果这也是它适合期末作业的根本原因。本文还有配套的精品资源点击获取