32.5月15日 项目:使用DrissionPage爬取Boss直聘
本项目预期结果:

使用DrissionPage爬取Boss直聘
之前库的缺点和局限性
requests库
requests库效率很高,但有明显缺点:
- 需要手动处理heads和cookies
- 难以应对各个网站的反爬机制
- 获取网页元素的难度高
selenium库
selenium库实现难度低,能较好的处理登录等情况,缺点:
- 代码效率低
- 难以处理json形式数据
举例代码
from selenium import webdriver
from selenium.webdriver.common.by import By
from openpyxl import Workbook
import time
browser = webdriver.Edge()
browser.maximize_window()
workbook = Workbook()
sheet = workbook.active
data_row=['岗位名称','薪资','岗位要求','单位名称']
sheet.append(data_row)
browser.get("https://www.zhipin.com/web/geek/job?query=%E6%95%B0%E6%8D%AE&city=101020100°ree=202")
browser.implicitly_wait(10)
for i in range(1,31):
try:
title_e = browser.find_element(By.XPATH, '//*[@id="wrap"]/div[2]/div[2]/div/div[1]/div[2]/ul/li[' + str(i) + ']/div[1]/a/div[1]/span[1]')
title_e.click()
browser.implicitly_wait(5)
handles = browser.window_handles
browser.switch_to.window(handles[-1])
job_title = browser.find_element(By.XPATH, '//*[@id="main"]/div[1]/div/div/div[1]/div[2]/h1')
print("岗位名称:"+job_title.text)
salary = browser.find_element(By.XPATH, '//*[@id="main"]/div[1]/div/div/div[1]/div[2]/span')
print("薪资:"+salary.text)
requirements = browser.find_element(By.XPATH, '//*[@id="main"]/div[3]/div/div[2]/div[1]/div[2]')
print("岗位要求:"+requirements.text)
conmpany = browser.find_element(By.XPATH, '//*[@id="main"]/div[3]/div/div[1]/div[2]/div/a[2]')
print("单位名称:"+conmpany.text)
data_row=[job_title.text,salary.text,requirements.text,conmpany.text]
sheet.append(data_row)
browser.implicitly_wait(4)
# 关闭当前标签页
handles = browser.window_handles
browser.close()
browser.implicitly_wait(4)
browser.switch_to.window(handles[0])
time.sleep(2)
except:
print("cuo")
workbook.save(filename="example.xlsx")
print("完毕")
time.sleep(200)
DrissionPage库
DrissionPage 是一个基于 python 的网页自动化工具。
它既能控制浏览器,也能收发数据包,还能把两者合而为一。
可兼顾浏览器自动化的便利性和 requests 的高效率。
它功能强大,内置无数人性化设计和便捷功能。它的语法简洁而优雅,代码量少,对新手友好。
Drission 是作者自创的单词,为 Driver 和 Session 的合体,因此 Drission 读作“拽神”。
官方库:https://www.drissionpage.cn/
DrissionPage库,带有三个模式SessionPage和ChromiumPage和WebPage。
SessionPage类似requests
ChromiumPage类似selenium
WebPage 整合模式,后续操作一般使用WebPage模式。
安装DrissionPage库
请使用 pip 安装 DrissionPage:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple DrissionPage
WebPage是功能最全面的页面类,既可控制浏览器,也可收发数据包。
from DrissionPage import WebPage
启动浏览器
默认状态下,程序会自动在系统内查找 Chrome 路径。
执行以下代码,浏览器启动并且访问了项目文档,说明可直接使用,跳过后面的步骤即可
from DrissionPage import WebPage
page = WebPage()
page.get('https://www.bilibili.com/')
如果要使用Edge浏览器,打开Edge浏览器,在地址栏输入输入edge://version,回车。

如图所示,红框中就是要获取的路径。
指定路径浏览器,方法:
新建一个临时 py 文件,并输入以下代码,填入您电脑里的 Edge 浏览器可执行文件路径,然后运行。
from DrissionPage import ChromiumOptions
path = r'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe'
ChromiumOptions().set_browser_path(path).save()
这段代码会把浏览器路径记录到配置文件,今后启动浏览器皆以新路径为准。
监听链接
找到页面中相关信息的json链接。

在代码中指定监听链接
wp.listen.start('zpgeek/search/joblist.json')
打开网页,监听内容并输出
wp=WebPage()
wp.listen.start('zpgeek/search/joblist.json')
wp.get("https://www.zhipin.com/web/geek/job?query=%E6%95%B0%E6%8D%AE&city=101020100°ree=202")
packet=wp.listen.wait()
print(packet.response.body)
当前已经成功爬取了一页的信息
找到下一页按钮

实现点击
wp.ele(".ui-icon-arrow-right").click()
实现循环
for i in range(1,11):
packet=wp.listen.wait()
print("第"+str(i))
try:
wp.ele(".ui-icon-arrow-right").click()
time.sleep(2)
except Exception as e :
print(e)
break
实现对json的解析
job_list=packet.response.body['zpData']['jobList']
for job in job_list:
jobName=job['jobName']
salaryDesc=job['salaryDesc']
areaDistrict=job['areaDistrict']
businessDistrict=job['businessDistrict']
brandName=job['brandName']
welfareList=job['welfareList']
jobLabels=job['jobLabels']
skills=job['skills']
brandIndustry=job['brandIndustry']
data_row=[jobName,salaryDesc,areaDistrict,businessDistrict,brandName,welfareList,jobLabels,skills,brandIndustry]
print(data_row)
将信息存入csv文件中供后续分析
采集信息包括了
‘岗位名称’,‘薪资’,‘区域’,‘地点’,‘公司名称’,‘福利待遇’,‘经历要求’,‘技能要求’,‘行业’
先将信息存入DataFrame,后续直接将DataFrame转换成csv
import pandas as pd
column_names=['岗位名称','薪资','区域','地点','公司名称','福利待遇','经历要求','技能要求','行业']
df = pd.DataFrame(columns=column_names)
# ...............
# 在循环中,将每次循环数据加入df
data_row=[jobName,salaryDesc,areaDistrict,businessDistrict,brandName,welfareList,jobLabels,skills,brandIndustry]
df.loc[len(df)] = data_row
程序最后,将df转换成csv文件
df.to_csv('example.csv', index=False)
print("完毕")
结果如下:

对薪资数据进行处理
| 薪资 |
|---|
| 6-8K |
| 10-15K·13薪 |
| 13-20K·14薪 |
| 150-200元/天 |
为统一计量单位,统一转换为年薪。
月薪以平均数计算。日薪 * 250 = 年薪
def parse_salary_range(salary_range):
# 去除额外字符(如'·13薪'和'元/天'),同时判断是否按天计算
if '·13薪' in salary_range:
salary_range = salary_range.replace('·13薪', '')
annual_bonus_months = 13
elif '·14薪' in salary_range:
salary_range = salary_range.replace('·14薪', '')
annual_bonus_months = 14
elif '·15薪' in salary_range:
salary_range = salary_range.replace('·15薪', '')
annual_bonus_months = 15
elif '·16薪' in salary_range:
salary_range = salary_range.replace('·16薪', '')
annual_bonus_months = 16
else:
annual_bonus_months = 12 # 默认按照12个月计算
if '元/天' in salary_range:
salary_range = salary_range.replace('元/天', '')
is_daily_salary = True
else:
is_daily_salary = False
if is_daily_salary == False:
salary_range = salary_range.replace('K', '')
if '-' in salary_range:
min_salary, max_salary = map(int, salary_range.split('-'))
else:
min_salary = max_salary = int(salary_range)
average_yearly_salary = ((min_salary + max_salary) / 2) * (annual_bonus_months )*1000
return average_yearly_salary
if is_daily_salary == True:
if '-' in salary_range:
min_salary, max_salary = map(int, salary_range.split('-'))
else:
min_salary = max_salary = int(salary_range)
average_yearly_salary = ((min_salary + max_salary) / 2) * 250
return average_yearly_salary
用以上函数,实现对薪资的处理。输出为岗位平均年薪数据
完整代码
以下代码实现了对特定岗位基本信息的爬取。
下一步,我们将实现:
- 数据基础分析
- 对技能要求的分析和可视化处理
from DrissionPage import WebPage
from DrissionPage import ChromiumOptions
import time
import pandas as pd
def parse_salary_range(salary_range):
# 去除额外字符(如'·13薪'和'元/天'),同时判断是否按天计算
if '·13薪' in salary_range:
salary_range = salary_range.replace('·13薪', '')
annual_bonus_months = 13
elif '·14薪' in salary_range:
salary_range = salary_range.replace('·14薪', '')
annual_bonus_months = 14
elif '·15薪' in salary_range:
salary_range = salary_range.replace('·15薪', '')
annual_bonus_months = 15
elif '·16薪' in salary_range:
salary_range = salary_range.replace('·16薪', '')
annual_bonus_months = 16
else:
annual_bonus_months = 12 # 默认按照12个月计算
if '元/天' in salary_range:
salary_range = salary_range.replace('元/天', '')
is_daily_salary = True
else:
is_daily_salary = False
if is_daily_salary == False:
salary_range = salary_range.replace('K', '')
if '-' in salary_range:
min_salary, max_salary = map(int, salary_range.split('-'))
else:
min_salary = max_salary = int(salary_range)
average_yearly_salary = ((min_salary + max_salary) / 2) * (annual_bonus_months )*1000
return average_yearly_salary
if is_daily_salary == True:
if '-' in salary_range:
min_salary, max_salary = map(int, salary_range.split('-'))
else:
min_salary = max_salary = int(salary_range)
average_yearly_salary = ((min_salary + max_salary) / 2) * 250
return average_yearly_salary
column_names=['岗位名称','薪资','区域','地点','公司名称','福利待遇','经历要求','技能要求','行业']
df = pd.DataFrame(columns=column_names)
path = r'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe'
# ChromiumOptions().set_browser_path(path)
ChromiumOptions().set_browser_path(path).save()
# co = ChromiumOptions().auto_port()
wp=WebPage()
wp.listen.start('zpgeek/search/joblist.json')
wp.get("https://www.zhipin.com/web/geek/job?query=%E6%95%B0%E6%8D%AE&city=101020100°ree=202")
for i in range(1,11):
packet=wp.listen.wait()
# wp.execute_script("window.scrollTo(0, document.body.scrollHeight);")
print("第"+str(i))
job_list=packet.response.body['zpData']['jobList']
for job in job_list:
jobName=job['jobName']
# salaryDesc=job['salaryDesc']
salaryDesc=job['salaryDesc']
salaryDesc=parse_salary_range(salaryDesc)
areaDistrict=job['areaDistrict']
businessDistrict=job['businessDistrict']
brandName=job['brandName']
welfareList=job['welfareList']
jobLabels=job['jobLabels']
skills=job['skills']
brandIndustry=job['brandIndustry']
data_row=[jobName,salaryDesc,areaDistrict,businessDistrict,brandName,welfareList,jobLabels,skills,brandIndustry]
df.loc[len(df)] = data_row
try:
wp.ele(".ui-icon-arrow-right").click()
time.sleep(2)
except Exception as e :
print(e)
break
df.to_csv('example.csv', index=False)
print("完毕")