1. 项目概述:Boss直聘行业薪资数据抓取实战
最近在帮朋友做职业规划时,发现行业薪资数据对决策帮助很大。但手动收集各大平台的招聘信息效率太低,于是决定用Python写个爬虫自动抓取Boss直聘的薪资数据。这个项目特别适合刚学完Python基础想练手的朋友,涉及requests库、数据解析、反爬策略等核心知识点。
重要提示:爬虫开发需遵守Robots协议和目标网站的使用条款,本案例仅用于技术学习,采集频率控制在合理范围,避免对目标网站造成负担。
2. 技术选型与工具准备
2.1 为什么选择这些技术?
经过对比几种常见方案,最终技术栈如下:
- 请求库:requests + requests-html(比纯requests更方便处理动态内容)
- 解析库:pyquery(语法类似jQuery,比BeautifulSoup更简洁)
- 数据存储:csv文件(轻量级,方便后续分析)
- 其他工具:Fiddler(抓包分析)、Chrome开发者工具
选择pyquery而非BeautifulSoup的主要原因是:
- CSS选择器语法更接近前端开发习惯
- 处理多层嵌套DOM时代码更简洁
- 性能略优于BeautifulSoup
python复制# 基础环境安装
pip install requests requests-html pyquery
3. 爬虫核心架构设计
3.1 整体工作流程
- 请求层:模拟浏览器发送HTTP请求
- 解析层:提取薪资、职位、公司等关键信息
- 存储层:将结构化数据保存到CSV
- 控制层:实现分页爬取和异常处理
mermaid复制graph TD
A[起始URL] --> B[发送请求]
B --> C{状态码200?}
C -->|是| D[解析数据]
C -->|否| E[异常处理]
D --> F[存储数据]
F --> G{还有下一页?}
G -->|是| H[构造下一页URL]
G -->|否| I[结束]
H --> B
3.2 关键代码结构
python复制class BossSpider:
def __init__(self):
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...'
}
def fetch(self, url):
# 实现请求逻辑
pass
def parse(self, html):
# 实现解析逻辑
pass
def save(self, data):
# 实现存储逻辑
pass
def run(self):
# 主控制流程
pass
4. 反爬策略实战应对
4.1 常见反爬手段及破解
Boss直聘主要采用这些防御措施:
-
User-Agent检测:
- 解决方案:轮换常用浏览器UA
python复制USER_AGENTS = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...' ] -
请求频率限制:
- 解决方案:随机延迟 + 代理IP
python复制time.sleep(random.uniform(1, 3)) -
Cookie验证:
- 解决方案:保持会话 + 定期更新
python复制
session = requests.Session()
4.2 高级技巧:处理动态渲染
对于部分动态加载的内容,可以采用:
- 分析XHR接口(推荐)
- 使用requests-html的渲染功能
python复制from requests_html import HTMLSession
session = HTMLSession()
r = session.get(url)
r.html.render() # 执行JavaScript
5. 数据解析核心实现
5.1 页面结构分析
通过Chrome开发者工具分析DOM结构,发现薪资数据通常位于:
html复制<div class="job-primary">
<div class="info-primary">
<h3 class="name">职位名称</h3>
<span class="red">15-30K·14薪</span>
</div>
</div>
5.2 使用pyquery精确提取
python复制from pyquery import PyQuery as pq
def parse(self, html):
doc = pq(html)
jobs = []
for item in doc('.job-primary').items():
job = {
'title': item.find('.name').text(),
'salary': item.find('.red').text(),
'company': item.find('.company-text a').text()
}
jobs.append(job)
return jobs
6. 数据存储与导出
6.1 CSV存储实现
python复制import csv
def save_to_csv(data, filename='jobs.csv'):
with open(filename, 'a', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=['title', 'salary', 'company'])
if f.tell() == 0: # 如果是新文件,写入表头
writer.writeheader()
writer.writerows(data)
6.2 数据清洗技巧
原始薪资字符串如"15-30K·14薪"需要进一步处理:
python复制def clean_salary(salary_str):
# 处理月薪范围
if 'K' in salary_str:
monthly = salary_str.split('K')[0]
low, high = map(float, monthly.split('-'))
# 计算年薪(考虑14薪等情况)
...
return {
'monthly_low': low,
'monthly_high': high,
'annual_salary': annual
}
7. 完整代码示例
python复制import random
import time
import csv
from pyquery import PyQuery as pq
import requests
class BossZhiPinSpider:
def __init__(self):
self.headers = {
'User-Agent': random.choice(USER_AGENTS),
'Referer': 'https://www.zhipin.com/'
}
self.session = requests.Session()
def fetch_page(self, url):
try:
resp = self.session.get(url, headers=self.headers)
resp.raise_for_status()
return resp.text
except Exception as e:
print(f"请求失败: {e}")
return None
def parse_jobs(self, html):
doc = pq(html)
jobs = []
for item in doc('.job-primary').items():
job = {
'title': item.find('.name').text().strip(),
'salary': self.clean_salary(item.find('.red').text()),
'company': item.find('.company-text a').text().strip(),
'experience': item.find('.info-primary p').text().split('|')[1].strip()
}
jobs.append(job)
return jobs
def run(self, keyword='Python', pages=3):
base_url = f'https://www.zhipin.com/web/geek/job?query={keyword}'
for page in range(1, pages+1):
url = f"{base_url}&page={page}"
print(f"正在抓取第{page}页: {url}")
html = self.fetch_page(url)
if not html:
continue
jobs = self.parse_jobs(html)
self.save_to_csv(jobs)
time.sleep(random.uniform(2, 5))
if __name__ == '__main__':
spider = BossZhiPinSpider()
spider.run(keyword='数据分析', pages=5)
8. 常见问题排查
8.1 返回403状态码
- 检查User-Agent是否有效
- 尝试更换代理IP
- 检查Cookie是否需要更新
8.2 数据提取为空
- 确认DOM结构是否变化
- 检查CSS选择器是否正确
- 尝试打印整个HTML确认是否获取到有效内容
8.3 被封IP怎么办
- 暂停爬取1-2小时
- 更换User-Agent和代理
- 降低请求频率至每分钟不超过5次
9. 项目优化方向
9.1 扩展功能
- 多城市数据抓取
- 自动生成薪资分布图表
- 职位技能关键词分析
9.2 性能优化
- 使用异步请求(aiohttp)
- 实现分布式爬取
- 添加断点续爬功能
python复制# 异步请求示例
import aiohttp
async def fetch_async(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
10. 法律与道德注意事项
- 严格遵守目标网站的robots.txt规定
- 设置合理的爬取间隔(建议≥3秒/请求)
- 不爬取个人隐私信息
- 数据仅用于个人学习研究
- 商业使用需获得平台授权
重要提醒:本文代码仅用于技术学习,请勿用于大规模商业数据采集。实际开发中建议使用官方API(如有提供)。
这个项目从技术实现到伦理考量都给了我很多启发。在实际开发中最深的体会是:比起技术实现,更关键的是要找到技术应用与商业伦理的平衡点。建议大家在完成基础功能后,可以尝试用这些数据做一些有价值的分析,比如不同城市的薪资差异、技能与薪资的关系等,这才是数据爬取的真正意义所在。