前言
Boss直聘是一款备受欢迎的招聘平台,提供了丰富的岗位信息。对于数据分析人员或技术爱好者而言,通过爬虫技术获取这些数据可以深化我们对市场需求的洞察。本次爬虫项目采用Python的Selenium库,通过模拟浏览器自动化操作来抓取Boss直聘上的招聘信息。随后,我们利用pandas、matplotlib和pyecharts等库对数据进行清洗、处理和可视化分析,最终生成一系列图表,从不同城市、公司规模、学历要求等多个维度对职位数据进行深入解读。
一、数据爬取
以下是从招聘网站抓取的关键字段信息:
1. 岗位名称:具体职位的称呼
2. 工作地点:详细的职位所在地
3. 薪资范围:职位对应的薪酬区间
4. 工作经验:所需的最低工作年限
5. 学历要求:应聘该职位所需的最低教育背景
6. 公司名称:招聘公司的全称
7. 公司类型:企业的性质(例如,外资企业、国有企业等)
8. 融资阶段:公司的当前融资情况(如未融资、A轮、B轮等)
9. 公司规模:企业的人员数量(如0-20人、100-500人等)
10. 岗位福利:公司为员工提供的额外福利(如五险一金、带薪年假等)
11. 所在城市:职位所在的城市
实现多线程启动
start方法创建多个线程,并调用getData方法来实现数据抓取。每个线程负责爬取一个页面范围的数据,以此来提高数据抓取效率。
from threading import Thread
import pandas as pd
from selenium.webdriver.edge.service import Service
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
class Boss(object):
def start(self):
threadList = []
for i in range(14): # 14个线程
t = Thread(target=self.getData, args=(i,))
t.start()
print(f"线程{t.name}开始")
time.sleep(10) # 避免被封
threadList.append(t)
return threadList
数据抓取方法:getData
在`getData`方法中,我们通过Selenium模拟浏览器访问Boss直聘的招聘页面。该方法会抓取并返回工作岗位的相关信息,包括岗位名称、工作地点、薪资范围、公司类型等关键细节。
def getData(self, i):
service = Service("./driver/msedgedriver.exe") # Edge驱动
driver = webdriver.Edge(service=service)
for i in range(10): # 每个线程爬取10页数据
driver.get(f"https://www.zhipin.com/web/geek/job?query=python&city=101040100&page={i+1}")
driver.implicitly_wait(10)
content = driver.find_elements(By.XPATH, '//div/ul/li[@class="job-card-wrapper"]')
for massage in content:
gwm = massage.find_element(By.XPATH, './div/a/div/span[@class="job-name"]').text
gzdz = massage.find_element(By.XPATH, './div/a/div/span/span').text
xz = massage.find_element(By.XPATH, './div/a/div/span[@class="salary"]').text
gznx = massage.find_element(By.XPATH, './div/a/div/ul/li[1]').text
xlyq = massage.find_element(By.XPATH, './div/a/div/ul/li[2]').text
qymc = massage.find_element(By.XPATH, './div/div/div/h3/a').text
# 公司类型、融资情况、公司规模等
data = {
"岗位名": gwm,
"工作地址": gzdz,
"薪资": xz,
"工作年限": gznx,
"学历要求": xlyq,
"公司名称": qymc,
}
print(data)
self.save(data) # 调用保存方法
driver.quit()
数据存储方法:save
save方法负责将抓取的数据保存至CSV文件。该方法首先检查文件是否存在,若存在则读取并将新数据追加,确保最终文件数据去重且结构完整。
def save(self, data):
filename = "boss职位.csv"
df = pd.DataFrame([data])
if pd.io.common.file_exists(filename):
existing_df = pd.read_csv(filename)
combined_df = pd.concat([existing_df, df], ignore_index=True)
unique_df = combined_df.drop_duplicates(subset=['岗位名'], keep='first')
unique_df.to_csv(filename, index=False, encoding='utf-8-sig')
else:
df.to_csv(filename, index=False, encoding='utf-8-sig')
启动爬虫
__main__部分启动整个程序,并调用start和stop方法实现数据抓取的启动与终止。
if __name__ == '__main__':
bs = Boss()
threads = bs.start()
bs.stop(threads)
在每个线程启动时,我们加入了10秒的延时设置,以降低触发反爬机制的风险,确保数据的顺利获取。在保存数据的过程中,我们实施了严格的数据去重措施,通过高效的去重算法避免了相同职位信息的重复写入文件,从而保证了数据的唯一性和准确性。
为了进一步提升数据的表现力和洞察力,我们对从Boss直聘获取的数据进行了一系列丰富的数据可视化处理。通过绘制多样化的图表,包括直观的地图、清晰的柱状图以及信息丰富的饼图等,我们能够更全面、更深入地了解当前招聘市场的状况。这些可视化工具不仅帮助我们更清晰地了解数据的分布、趋势和关系,而且为我们的分析提供了有力的支持,使我们能够从数据中发现更多有价值的信息和模式。
二、数据分析
数据预处理
首先,从CSV文件加载数据,检查是否存在缺失值,并进行数据清洗。对于空缺的公司规模字段,我们删除对应的数据行。
import pandas as pd
df = pd.read_csv('boss职位.csv')
df.drop(df[df['公司规模'].isnull()].index, inplace=True)
数据的分析与可视化
1. 城市岗位数量的地图分布
我们使用Pyecharts的Map图表绘制出全国主要城市的岗位数量分布,展示各地的招聘需求。
from pyecharts.charts import Map
from pyecharts import options as opt
df['城市'] = df['工作地址'].str.split('·', expand=True)[0]
map_chart = Map(init_opts=opt.InitOpts(width="1600px", height="600px"))
data = df.groupby('城市').size().reset_index().rename(columns={0: '数量'})
data['城市'] = data['城市'] + '市'
data = [list(z) for z in zip(data['城市'], data['数量'])]
map_chart.add("数据", data, "china-cities")
map_chart.set_global_opts(
title_opts=opt.TitleOpts(title="热门城市岗位数量分布地图"),
visualmap_opts=opt.VisualMapOpts(is_piecewise=False)
)
map_chart.render("热门城市岗位数量分布地图.html")
2. 热门城市的岗位数量柱状图
通过将城市按岗位数量排序并绘制柱状图,可以看到各城市的招聘岗位数量差异,方便我们了解招聘需求较多的地区。
from pyecharts.charts import Bar
bar = (Bar()
.add_yaxis('岗位数量', city['岗位数量'].tolist(), color='blue')
.add_xaxis(city['城市'].tolist())
.set_global_opts(
title_opts=opt.TitleOpts(title="热门城市工作岗位数量"),
yaxis_opts=opt.AxisOpts(name="工作岗位数量", name_location="middle", name_gap=30),
xaxis_opts=opt.AxisOpts(name="城市名称", name_location="middle", name_gap=30)
)
)
bar.render("热门城市岗位数量.html")
3. 热门城市的平均薪资
我们计算各城市的平均薪资,通过柱状图呈现热门城市的薪资水平,便于观察不同城市之间的薪资差异。
data['平均薪资'] = (data['最低薪资'] + data['最高薪资']) / 2
data1 = data.groupby('城市').agg(
平均最低薪资=('最低薪资', 'mean'),
平均最高薪资=('最高薪资', 'mean'),
平均薪资=('平均薪资', 'mean')
).reset_index()
bar = (Bar()
.add_yaxis("下限", data1["平均最低薪资"].astype(int).tolist(), color='blue')
.add_yaxis("上限", data1["平均最高薪资"].astype(int).tolist(), color="orange")
.add_yaxis("平均薪资", data1["平均薪资"].astype(int).tolist())
.add_xaxis(data1['城市'].tolist())
.set_global_opts(
title_opts=opt.TitleOpts(title="热门城市平均薪资"),
yaxis_opts=opt.AxisOpts(name="平均薪资", name_location="middle", name_gap=50),
xaxis_opts=opt.AxisOpts(name="城市名称", name_location="middle", name_gap=30)
)
)
bar.render("热门城市平均薪资.html")
4. 岗位与学历的关系
通过对数据按学历要求进行分组,并使用饼图展示不同学历对应的岗位数量分布,可以直观地看到公司对不同学历的需求情况。
from pyecharts.charts import Pie
data3 = df.groupby('学历要求').size().sort_values(ascending=False).reset_index()
pie = Pie()
pie.add(
"岗位与学历关系",
[list(z) for z in zip(data3['学历要求'].tolist(), data3[0].tolist())],
radius=["40%", "75%"]
)
pie.set_global_opts(
title_opts=opt.TitleOpts(title="岗位与学历关系")
)
pie.set_series_opts(
label_opts=opt.LabelOpts(formatter="{b}: {c} ({d}%)")
)
pie.render("岗位与学历关系.html")
5. 工作年限与薪资的关系
公司对不同工龄的员工支付的薪资水平不同。我们通过自定义顺序展示各工作年限对应的平均薪资,形成清晰的薪资-工龄对比图。
custom_order = ['经验不限', '10年以上', '5-10年', '3-5年', '1-3年', '1年以内', '在校/应届']
data5 = data.groupby('工作年限').agg(平均薪资=('平均薪资', 'mean')).loc[custom_order].reset_index()
bar = (Bar()
.add_yaxis("平均薪资", data5["平均薪资"].astype(int).tolist(), color="orange")
.add_xaxis(data5['工作年限'].tolist())
.set_global_opts(
title_opts=opt.TitleOpts(title="工作年限与薪资的关系图"),
yaxis_opts=opt.AxisOpts(name="平均薪资", name_location="middle", name_gap=50),
xaxis_opts=opt.AxisOpts(name="工龄", name_location="middle", name_gap=30)
)
)
bar.render("工作年限与薪资的关系图.html")
6. 薪资与学历关系
根据学历要求计算平均薪资,并通过柱状图展示学历与薪资之间的关系。
data3 = data2.groupby('学历要求').agg(
平均薪资=('平均薪资', 'mean'),
).reset_index()
bar = Bar().add_xaxis(data3['学历要求'].tolist()).add_yaxis("平均薪资", data3["平均薪资"].astype(int).tolist(), color="green")
7. 工作年限与薪资关系
根据工作年限计算薪资,并绘制柱状图展示工作年限与薪资的关系。
data4 = data.groupby('工作年限').agg(
平均薪资=('平均薪资', 'mean'),
).reset_index()
bar = Bar().add_yaxis("平均薪资", data4["平均薪资"].astype(int).tolist(), color="orange")
8. 公司规模与薪资关系
根据公司规模计算平均薪资,并展示公司规模与薪资的关系。
data6 = data.groupby('公司规模').agg(
平均薪资=('平均薪资', 'mean'),
).reset_index()
bar = Bar().add_xaxis(data6['公司规模'].tolist()).add_yaxis("平均薪资", data6["平均薪资"].astype(int).tolist(), color="blue")
9. 融资情况与薪资关系
通过柱状图和折线图的组合展示不同融资情况的公司薪资分布。
data7 = data.groupby('融资情况').agg(
数量=('融资情况', 'count'),
平均薪资=('平均薪资', 'mean')
).reset_index()
bar = Bar().add_xaxis(data7['融资情况'].tolist()).add_yaxis("数量", data7['数量'].tolist(), color='blue', stack="stack1")
line = Line().add_xaxis(data7['融资情况'].tolist()).add_yaxis("平均薪资趋势", data7['平均薪资'].astype(int).tolist(), color='red', stack="stack1")
bar.overlap(line)
总结
在本次项目中,我们更加熟练地掌握了如何运用Python和Selenium技术抓取Boss直聘上的岗位信息,并将这些数据有效地存储到CSV文件中。项目的核心重点在于实现了多线程处理以及在数据存储过程中进行了去重操作,确保数据的准确性和唯一性。