注意:以下内容仅供技术研究,请遵守目标网站的robots.txt规定,控制请求频率避免对目标服务器造成过大压力!
1. 环境准备
python
python
import requests
from bs4 import BeautifulSoup
import pandas as pd
import re
import matplotlib.pyplot as plt
2. 爬虫核心代码(带反爬策略)
python
python
def fetch_51job_data(keyword, max_pages=5):
jobs = []
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Accept-Language': 'zh-CN,zh;q=0.9'
}
for page in range(1, max_pages+1):
url = f'https://search.51job.com/list/000000,000000,0000,00,9,99,{keyword},2,{page}.html'
try:
response = requests.get(url, headers=headers, timeout=10)
response.encoding = 'gbk' # 网站编码特殊处理
soup = BeautifulSoup(response.text, 'lxml')
for item in soup.select('.j_joblist .e'):
title = item.select_one('.jname').text.strip()
company = item.select_one('.cname').text.strip()
desc = item.select_one('.text').text.strip()
experience = item.select_one('.exp').text.strip() if item.select_one('.exp') else '不限'
salary = item.select_one('.sal').text.strip()
jobs.append({
'职位名称': title,
'公司名称': company,
'工作内容': desc,
'工作经验': experience,
'薪资范围': salary
})
except Exception as e:
print(f'第{page}页抓取失败:', e)
# 添加延迟避免被封
time.sleep(1.5)
return jobs
3. 薪资解析算法
python
python
def parse_salary(salary_str):
"""
支持格式示例:
- 1.5-2万/月
- 20-30万/年
- 5-8千/月
- 200-300元/天
"""
pattern = r'(\d+\.?\d*)-(\d+\.?\d*)(万|千|元)/(年|月|日)'
match = re.search(pattern, salary_str)
if not match: return None
low, high, unit, period = match.groups()
low, high = float(low), float(high)
avg = (low + high) / 2
# 转换为万元/年
conversion = {
('万', '年'): avg,
('万', '月'): avg * 12,
('千', '月'): avg * 12 * 0.1,
('元', '日'): avg * 22 * 12 / 10000
}
return conversion.get((unit, period), None)
4. 数据存储与清洗
python
python
# 数据清洗管道
def clean_data(jobs):
df = pd.DataFrame(jobs)
df['平均年薪'] = df['薪资范围'].apply(parse_salary)
df = df.dropna(subset=['平均年薪']) # 过滤无效数据
return df
# 保存到CSV
df.to_csv('51job.csv', index=False, encoding='utf-8-sig')
5. 数据分析与可视化
python
python
def analyze_salary(df):
# 薪资TOP10分析
top10 = df.sort_values('平均年薪', ascending=False).head(10)
# 可视化配置
plt.style.use('ggplot')
plt.figure(figsize=(12,8))
bars = plt.barh(top10['职位名称'], top10['平均年薪'], color='#4BACC6')
# 添加数据标签
for bar in bars:
width = bar.get_width()
plt.text(width+0.5, bar.get_y()+0.2, f'{width:.1f}万', va='center')
plt.title('高薪职位TOP10(单位:万元/年)', fontsize=14)
plt.xlabel('平均年薪', fontsize=12)
plt.gca().invert_yaxis() # 反转Y轴显示排名
plt.tight_layout()
plt.savefig('salary_top10.png', dpi=300)
plt.show()
6. 完整执行流程
python
python
if __name__ == '__main__':
# 数据采集
raw_data = fetch_51job_data('python', max_pages=5)
# 数据清洗
cleaned_df = clean_data(raw_data)
# 存储结果
cleaned_df.to_csv('51job.csv', index=False, encoding='utf-8-sig')
# 数据分析与可视化
analyze_salary(cleaned_df)
7. 关键要点扩展
-
反爬策略强化
-
代理IP池:使用
requests-ip-rotator
库实现自动IP切换 -
浏览器指纹:通过
selenium-wire
模拟真实浏览器环境 -
请求随机化:添加随机延迟(0.5-3秒)和随机User-Agent
-
-
数据质量优化
-
异常值过滤:设置薪资合理范围(如0.5<薪资<500万/年)
-
文本清洗:使用
jieba
分词提取技能要求关键词 -
地址解析:从工作内容中提取工作地点信息
-
-
分析维度扩展
python
python# 企业规模与薪资关系 df['公司规模'] = df['公司名称'].apply(detect_company_size) # 需自定义企业规模识别函数 df.boxplot(column='平均年薪', by='公司规模') # 技能需求词云 from wordcloud import WordCloud skills = extract_skills(df['工作内容']) # 需自定义技能提取函数 wordcloud = WordCloud().generate(' '.join(skills)) plt.imshow(wordcloud)
-
架构升级思路
-
使用Scrapy框架实现分布式爬虫
-
集成Airflow实现定时数据更新
-
数据存储改用MySQL+Redis缓存
-
使用Tableau实现动态数据看板
-
8. 常见问题解决方案
-
页面结构变化
-
定期运行监控脚本检查CSS选择器有效性
-
使用XPath代替CSS选择器提高容错性
-
建立元素匹配的备选方案列表
-
-
验证码破解
-
对接打码平台(如超级鹰)
-
使用CNN训练验证码识别模型
-
设置验证码触发后的等待策略
-
-
数据更新机制
python
python# 增量更新示例 existing = pd.read_csv('51job.csv') new_data = fetch_new_data() updated = pd.concat([existing, new_data]).drop_duplicates( subset=['职位名称', '公司名称'], keep='last' )