【python大作业/爬虫实战】——爬取前程无忧(51job)数据+可视化(附完整代码)_用python实现爬虫爬取招聘网站的招聘信息
在当今数字化时代,网络爬虫技术已成为数据获取的重要手段之一。本文将通过一个实际案例——采集51job招聘信息,详细介绍如何使用Python和Selenium框架实现数据采集。我们将从环境准备、网页结构分析、采集字段说明到爬虫实现步骤等方面展开,帮助读者快速掌握相关技术。
> 本文章中所有内容仅供学习交流使用,不用于其他任何目的,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!
本篇文章使用的是seleniunm接管已经打开的浏览器进行数据采集,原理不懂的可以看之前的技术博客 ( selenium接管已经打开的浏览器)
更新时间:2025-8-14
可用版本:drissonpage版本、selenium版本
一、项目背景
51job是中国知名的招聘网站,提供了海量的职位信息。这些信息对于求职者和招聘者都具有重要价值。通过采集51job的招聘信息,我们可以进行数据分析、市场调研或为求职者提供决策支持。本文的目标是实现一个自动化采集程序,获取51job上特定关键词(如“大数据”)的职位信息,并将其保存为CSV文件。
二、环境准备
在开始项目之前,我们需要准备以下开发环境和工具:
-
Python环境
确保已安装Python(推荐3.8及以上版本)。可以通过以下命令安装必要的Python库:
pip install selenium pyautogui
-
Selenium WebDriver
Selenium是一个用于自动化Web操作的工具,支持多种浏览器。本文使用Chrome浏览器,因此需要下载与浏览器版本匹配的ChromeDriver。下载地址:ChromeDriver - WebDriver for Chrome。下载后,将
chromedriver.exe
放置在系统路径中或指定路径。 -
浏览器配置
为了方便调试,我们使用Selenium的远程调试功能。在启动Chrome浏览器时,添加以下启动参数:
chrome.exe --remote-debugging-port=9527
这样,Selenium可以通过该端口连接到浏览器。
-
其他工具
-
Pyautogui:用于模拟鼠标操作,处理滑块验证码。
-
CSV:用于存储采集到的数据。
-
三、采集字段说明
在爬取51job的招聘信息时,我们主要关注以下字段:
-
岗位名称:招聘职位的名称。
-
岗位薪资:该职位的薪资范围。
-
岗位地区:工作地点。
-
岗位标签:与该职位相关的标签,如“五险一金”、“带薪年假”等。
-
公司名字:招聘公司的名称。
-
公司行业:公司所属的行业。
-
公司规模:公司的员工规模。
-
公司类型:公司的类型,如“民营公司”、“外资公司”等。
-
HR活跃程度:HR的活跃程度,如“刚刚活跃”、“3天前活跃”等。
四、采集方式优缺点对比
以下是通过表格形式对 Selenium 接管已打开的浏览器采集、Requests 抓包采集(需要逆向 sign 和 cookies) 以及 直接使用 Selenium 采集 的优缺点进行详细对比:
五、网页结构分析
在编写爬虫之前,我们需要对51job的网页结构进行分析,以便准确提取所需信息。
-
职位列表页面
职位搜索结果页面的URL格式为:
https://we.51job.com/pc/search?keyword=
例如,搜索“大数据”相关职位时,URL为:
https://we.51job.com/pc/search?keyword=大数据
-
职位信息的HTML结构
每个职位信息以
的形式展示。以下是关键字段的HTML结构分析:-
岗位名称:
下的标签。
岗位薪资:
。
岗位地区:
。岗位标签:
下的多个标签。
公司行业、类型、规模:
下的多个标签。
HR活跃程度:
。分页结构
51job的职位搜索结果支持分页,每页显示一定数量的职位。翻页按钮的HTML结构为:
验证码处理
51job可能会在某些情况下弹出滑块验证码(采用接管的浏览器可以登录避免经常出现验证码)。为了应对这种情况,我们使用
pyautogui
库模拟鼠标操作,完成滑块验证。六、结果展示
七、完整代码
(一)数据采集(爬虫)部分
(二)可视化部分
1.岗位地域分布
from pyecharts import options as optsfrom pyecharts.charts import Mapmap_data = df[\'省份\'].value_counts().reset_index().values.tolist()map_chart = ( Map() .add( series_name=\"\", data_pair=map_data, maptype=\"china\", is_map_symbol_show=False, # 禁用标记点 label_opts=opts.LabelOpts(is_show=True, font_size=8), # 调整省份字体大小 ) .set_global_opts( title_opts=opts.TitleOpts(title=\"岗位地域分布地图\"), visualmap_opts=opts.VisualMapOpts( is_piecewise=True, # 分段显示 pieces=[ {\"min\": 100, \"label\": \"100+\", \"color\": \"#CD5C5C\"}, {\"min\": 50, \"max\": 99, \"label\": \"50-99\", \"color\": \"#F08080\"}, {\"min\": 1, \"max\": 49, \"label\": \"1-49\", \"color\": \"#FFA07A\"}, {\"min\": 0, \"max\": 0, \"label\": \"0\", \"color\": \"#FFFFFF\"}, ], ), ))map_chart.render_notebook()
2.企业规模占比分布
size_counts = df[\'公司规模1\'].value_counts()sns.set_theme(style=\"whitegrid\",rc={ \"font.sans-serif\": [\"SimHei\"], # 设置字体为黑体 \"axes.unicode_minus\": False, # 正确显示负号 \"font.family\": \"sans-serif\" # 设置字体族为 sans-serif })plt.figure(figsize=(8, 8)) plt.pie( size_counts, labels=size_counts.index, autopct=\'%1.1f%%\', startangle=90, colors=sns.color_palette(\"pastel\"), wedgeprops={\'edgecolor\': \'white\', \'linewidth\': 1.5}, textprops={\'fontsize\': 12, \'color\': \'black\'} )plt.title(\"企业规模占比分布\", fontsize=16, fontweight=\'bold\', pad=20)plt.show()
3.企业类型占比分布
size_counts = df[\'公司类型\'].value_counts()size_percentage = size_counts / size_counts.sum() * 100# 找出占比小于 2% 的类别small_categories = size_percentage[size_percentage < 2].index# 将占比小于 2% 的类别合并为“其他”size_counts[\'其他\'] = size_counts[small_categories].sum()size_counts = size_counts.drop(small_categories)sns.set_theme(style=\"whitegrid\", rc={ \"font.sans-serif\": [\"SimHei\"], # 设置字体为黑体 \"axes.unicode_minus\": False, # 正确显示负号 \"font.family\": \"sans-serif\" # 设置字体族为 sans-serif})plt.figure(figsize=(15, 10)) plt.pie( size_counts, labels=size_counts.index, autopct=\'%1.1f%%\', # 显示百分比 startangle=90, # 起始角度 colors=sns.color_palette(\"pastel\"), wedgeprops={\'edgecolor\': \'white\', \'linewidth\': 1.5}, textprops={\'fontsize\': 12, \'color\': \'black\'} )plt.title(\"企业类型占比分布\", fontsize=16, fontweight=\'bold\', pad=20)plt.show()
4.HR活跃程度统计分析
value_counts = df[\'hr活跃程度1\'].value_counts()plt.figure(figsize=(8, 6)) # 设置图形大小sns.barplot(x=value_counts.index, y=value_counts.values, palette=\'viridis\') plt.title(\'HR活跃程度统计分析\', fontsize=16)plt.xlabel(\'\')plt.ylabel(\'\')plt.show()
5.企业数量Top 7
top_7 = df[\'公司名字\'].value_counts().head(7)plt.figure(figsize=(15, 6)) sns.barplot(x=top_7.values, y=top_7.index, palette=\'viridis\') plt.title(\'企业数量Top 7\', fontsize=16)plt.xlabel(\'\',)plt.ylabel(\'\',)# plt.yticks(rotation=15, fontsize=12)plt.show()
6.岗位标签词云
from wordcloud import WordCloudimport jiebadf[\'岗位标签1\'] = df[\'岗位标签\'].apply(lambda x: \' \'.join(eval(x)))all_text = \' \'.join(df[\'岗位标签1\'])# 使用 jieba 进行分词words = jieba.lcut(all_text)all_text = \' \'.join(words)# 创建词云对象wordcloud = WordCloud( font_path=\'simhei.ttf\', # 设置字体路径(支持中文) width=800, height=600, background_color=\'white\', # 设置背景颜色 max_words=100, # 设置最多显示的词数 min_font_size=10, # 设置最小字体大小 max_font_size=100, # 设置最大字体大小 random_state=42 # 设置随机种子).generate(all_text)plt.figure(figsize=(10, 8))plt.imshow(wordcloud, interpolation=\'bilinear\')plt.axis(\'off\') # 关闭坐标轴plt.title(\'\', fontsize=16)plt.show()
-