一、简介
随着互联网的普及,越来越多的招聘信息来源于各大招聘网站。然而,这些招聘信息往往以文本形式存在,使得人工阅读和分析变得耗时且低效。为了提高招聘数据的处理效率,本文将介绍如何使用Selenium进行BOSS招聘数据的自动化爬取。
而且BOOS的反爬机制很厉害,cookie经常发生变化,使用selenium通过自动化操作浏览器来获取页面上的数据会更简单一点
二、技术栈
本文将介绍使用的技术栈,Python、Selenium、Pandas、Random
三、难点
在通过使用Selenium自动操作浏览器访问BOOS招聘时,BOOS会通过各种手段包括浏览器指纹识别技术或者访问行为来判定操作对象是否是机器人,判断成立之后会返回一些错误的页面信息或者验证码之类的,所以在获取招聘数据之前,应该先学会伪装自己,让浏览器无法识别到是机器人在操作,
具体细节可以参考该文章selenium实战指南:如何防止被浏览器检测?
四、代码实现
1、打开谷歌浏览器
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from time import sleep
import pandas as pd
import undetected_chromedriver as uc
import random
#BOOS招聘网站
url = "https://www.zhipin.com/shanghai/"
#创建谷歌浏览器
browser = uc.Chrome()
#打开网页
browser.get(url=url)
#等待10秒钟,最好别访问太快
sleep(10)
这里可以选择等待十秒,也可以不等待,为了防止访问过快,采用其他等待方法也可以,也可以不等待
页面:
2、定位搜索框
先通过xpath定位到搜索框,然后搜索想要的内容,这里以搜索“金融”相关的职位公司为例,这里输入内容之后通过模拟键盘操作点击回车,也可以确认搜索按钮后直接点击搜索,不过也是尽量模仿真实人类操作的操作方式
然后还是等待10秒,防止操作过快,不过也可以通过
# 选择搜索框
searching = browser.find_element(By.XPATH,
'//input[@type="text" and @name="query" and @class="ipt-search" and @placeholder="搜索职位、公司"]')
# 输入金融,搜素金融相关的公司
searching.send_keys("金融")
# 点击搜索,通过回车点击
searching.send_keys(Keys.ENTER)
# 等待10秒
sleep(10)
3、确认获取页面的信息
现在已经进入到了招聘信息页面,先确认一下需要获取到那些信息,确认好信息之后就需要通过xpath定位每个信息的位置
- 招聘职位名称
- 公司所属地区
- 公司名称
- 薪资区间
- 工作经验要求
- 招聘人名称及岗位
- 公司名称
- 公司类型/融资情况/公司人员规模
- 岗位要求
- 公司福利
4、循环获取页面的全部数据
首先确认了一共有10页的数据,每页30条内容,一共300条,那么就通过循环开始获取每一条数据
通过循环获取全部数据,每循环30条数据(一页)之后点击下一页,当循环次数达到300次的时候就说明查找完了,通过random随机等待1-15秒,目的是为了模仿真实人的操作,也可以不进行等待,然后关闭浏览器
#获取表格数据
#定义一个变量来判断循环的次数
num = 1
#定义一个空数组,后面用来存储数据
pd_lis = []
while True :
if num % 30 == 0:
if num == 300:
#关闭浏览器
browser.close()
browser.quit()
else :
#获取a标签下最后一个a标签,那个就是点击下一页
page = browser.find_elements(By.XPATH,'//div[@class="options-pages"]/a')
last_page = page[-1]
#点击下一页
last_page.click()
#每循环一次num变量+1
num += 1
sleep(random.randint(1,15))
5、获取信息
通过开发者模式看到,每一个岗位信息都在一个<li>标签中,其中的ka属性会从1开始累加,我们要获取300条数据,那么一共就会累加到"search_list_300",在循环中通过上一步定义的num参数来进行不断累加,达到获取每一个岗位的要求
#定位到岗位信息所在的li标签,通过num变量来确认获取哪一行的岗位信息
mess = f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]'
li = browser.find_element(By.XPATH,mess)
# 指定职位等信息
div_1 = li.find_element(By.XPATH, f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]/div[@class="job-card-body clearfix"]/a')
# 获取职位
job_title = div_1.find_element(By.XPATH,
f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]/div[@class="job-card-body clearfix"]/a/div[@class="job-title clearfix"]/span[@class="job-name"]').text
# 获取公司位置
job_address = div_1.find_element(By.XPATH,
f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]/div[@class="job-card-body clearfix"]/a/div[@class="job-title clearfix"]/span[@class="job-area-wrapper"]/span[@class="job-area"]').text
# 获取薪资
salary = div_1.find_element(By.XPATH, f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]/div[@class="job-card-body clearfix"]/a/div[@class="job-info clearfix"]/span[@class="salary"]').text
# 确认工作经验所属位置
work_experience = div_1.find_element(By.XPATH, f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]/div[@class="job-card-body clearfix"]/a/div[@class="job-info clearfix"]/ul[@class="tag-list"]')
获取工作经验要求
可以看到工作经验要求在<ul>标签下的每一个<li>标签中,可能会存在两个或者三个不等,或者就是没有该信息,那么就通过 find_elements 来获取每一个<li>标签
通过循环遍历每一个<li>标签 ,定义一个空字符串变量,如果是第一次遍历该变量为空,等第二次和第三次的时候就跟上一个结果通过 / 符号拼接在一起,最后结果可能是“经验不限/大专”
# 获取学历和工作经验
work_experience_lis = work_experience.find_elements(By.TAG_NAME, 'li')
# 定义一个变量,用来拼接学历和工作经验,因为有的公司有两个信息,有的公司有三个信息,有的公司没有信息
work_experience = ""
for we_li in work_experience_lis:
work_experience = work_experience + we_li.text if work_experience == "" else work_experience + '/' + we_li.text
获取招聘人和招聘人所属岗位
像这样的情况,招聘人和招聘人岗位都在<div>标签下,但是招聘人岗位在<div>标签下的<em>标签中,如果直接输出<div>标签中的内容的话,结果就是 “张先生人力总监”,但是最终的结果想变成分开的,招聘人是一个字段,招聘人所属岗位是一个字段,操作实现请往下看
通过替换的方式获取这两个变量
# 获取招聘人员加招聘人员职位
recruiter_position = div_1.find_element(By.XPATH,
f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]/div[@class="job-card-body clearfix"]/a/div[@class="job-info clearfix"]/div[@class="info-public"]').text
# 获取招聘人员职位
position = div_1.find_element(By.XPATH, f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]/div[@class="job-card-body clearfix"]/a/div[@class="job-info clearfix"]/div[@class="info-public"]/em').text
# 将招聘人员加招聘人员职位中剔除招聘人员职位,直接替换成空
recruiter = recruiter_position.replace(position, "")
获取公司名称和公司类型/融资情况/人员规模
公司类型/融资情况/人员规模和上述的招聘人/招聘人所属岗位的获取方式一样
# 公司名称
company_name = div_2.find_element(By.TAG_NAME, 'h3').text
print(company_name)
# 获取公司类型/融资情况/人员规模
company_type_ul = div_2.find_element(By.XPATH, f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]/div[@class="job-card-body clearfix"]/div[@class="job-card-right"]/div[@class="company-info"]/ul[@class="company-tag-list"]')
company_type_lis = company_type_ul.find_elements(By.TAG_NAME, 'li')
company_join = ""
for company in company_type_lis:
#获取公司类型/融资情况/人员规模
company_join = company_join + company.text if company_join == "" else company_join + '/' + company.text
获取岗位要求
获取方式同获取招聘人/招聘人所属岗位的获取方式一样
# 获取公司对职位的要求ul标签
company_requirements_ul = li.find_element(By.XPATH,
f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]//div[@class="job-card-footer clearfix"]/ul[@class="tag-list"]')
# 获取公司对职位的要求li标签
company_requirements_lis = company_requirements_ul.find_elements(By.TAG_NAME, "li")
company_requirements = ""
for company_requirements_li in company_requirements_lis:
#公司对职位的要
company_requirements = company_requirements + company_requirements_li.text if company_requirements == "" else company_requirements + '/' + company_requirements_li.text
获取公司福利
公司福利信息都在<div>标签中,直接定位到该标签输出信息就可以了
# 获取公司福利
company_benefits = li.find_element(By.XPATH, f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]//div[@class="job-card-footer clearfix"]/div').text
将获取到的所有信息存储到数组中
'''
job_title 职位 -
job_address 公司位置
salary 薪资 -
work_experience 学历工作经验
recruiter 招聘人
position 招聘人所属职位
company_name 公司名称
company_join 公司类型/融资情况/人员规模
company_requirements 公司要求
company_benefits 公司福利
'''
pd_lis.append([company_name,job_title,salary, job_address, work_experience, recruiter, position, company_join,
company_requirements, company_benefits])
五、完整代码
# 自动化获取boss招聘网的信息
# 引入By Class,辅助元素定位
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from time import sleep
import pandas as pd
import undetected_chromedriver as uc
import random
#BOOS招聘网站
url = "https://www.zhipin.com/shanghai/"
#创建谷歌浏览器
browser = uc.Chrome()
#打开网页
browser.get(url=url)
#等待10秒钟,最好别访问太快
sleep(10)
# 选择搜索框
searching = browser.find_element(By.XPATH,
'//input[@type="text" and @name="query" and @class="ipt-search" and @placeholder="搜索职位、公司"]')
# 输入金融,搜素金融相关的公司
searching.send_keys("金融")
# 点击搜索,通过回车点击
searching.send_keys(Keys.ENTER)
# 等待10秒
sleep(10)
#获取表格数据
#定义一个变量来判断循环的次数
num = 1
#定义一个空数组,后面用来存储数据
pd_lis = []
while True :
#定位到岗位信息所在的li标签,通过num变量来确认获取哪一行的岗位信息
mess = f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]'
li = browser.find_element(By.XPATH,mess)
# 指定职位等信息
div_1 = li.find_element(By.XPATH, f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]/div[@class="job-card-body clearfix"]/a')
# 获取职位
job_title = div_1.find_element(By.XPATH,
f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]/div[@class="job-card-body clearfix"]/a/div[@class="job-title clearfix"]/span[@class="job-name"]').text
# 获取公司位置
job_address = div_1.find_element(By.XPATH,
f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]/div[@class="job-card-body clearfix"]/a/div[@class="job-title clearfix"]/span[@class="job-area-wrapper"]/span[@class="job-area"]').text
# 获取薪资
salary = div_1.find_element(By.XPATH, f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]/div[@class="job-card-body clearfix"]/a/div[@class="job-info clearfix"]/span[@class="salary"]').text
# 确认工作经验所属位置
work_experience = div_1.find_element(By.XPATH, f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]/div[@class="job-card-body clearfix"]/a/div[@class="job-info clearfix"]/ul[@class="tag-list"]')
# 获取学历和工作经验
work_experience_lis = work_experience.find_elements(By.TAG_NAME, 'li')
# 定义一个变量,用来拼接学历和工作经验,因为有的公司有两个信息,有的公司有三个信息,有的公司没有信息
work_experience = ""
for we_li in work_experience_lis:
work_experience = work_experience + we_li.text if work_experience == "" else work_experience + '/' + we_li.text
# 获取招聘人员加招聘人员职位
recruiter_position = div_1.find_element(By.XPATH,
f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]/div[@class="job-card-body clearfix"]/a/div[@class="job-info clearfix"]/div[@class="info-public"]').text
# 获取招聘人员职位
position = div_1.find_element(By.XPATH, f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]/div[@class="job-card-body clearfix"]/a/div[@class="job-info clearfix"]/div[@class="info-public"]/em').text
# 将招聘人员加招聘人员职位中剔除招聘人员职位,直接替换成空
recruiter = recruiter_position.replace(position, "")
# 指定公司信息
div_2 = li.find_element(By.XPATH,
f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]/div[@class="job-card-body clearfix"]/div[@class="job-card-right"]/div[@class="company-info"]')
# 公司名称
company_name = div_2.find_element(By.TAG_NAME, 'h3').text
# 获取公司类型/融资情况/人员规模
company_type_ul = div_2.find_element(By.XPATH, f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]/div[@class="job-card-body clearfix"]/div[@class="job-card-right"]/div[@class="company-info"]/ul[@class="company-tag-list"]')
company_type_lis = company_type_ul.find_elements(By.TAG_NAME, 'li')
company_join = ""
for company in company_type_lis:
#获取公司类型/融资情况/人员规模
company_join = company_join + company.text if company_join == "" else company_join + '/' + company.text
# 获取公司对职位的要求ul标签
company_requirements_ul = li.find_element(By.XPATH,
f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]//div[@class="job-card-footer clearfix"]/ul[@class="tag-list"]')
# 获取公司对职位的要求li标签
company_requirements_lis = company_requirements_ul.find_elements(By.TAG_NAME, "li")
company_requirements = ""
for company_requirements_li in company_requirements_lis:
#公司对职位的要
company_requirements = company_requirements + company_requirements_li.text if company_requirements == "" else company_requirements + '/' + company_requirements_li.text
# 获取公司福利
company_benefits = li.find_element(By.XPATH, f'//div[@class="search-job-result"]/ul[@class="job-list-box"]/li[@ka="search_list_{num}"]//div[@class="job-card-footer clearfix"]/div').text
'''
job_title 职位 -
job_address 公司位置
salary 薪资 -
work_experience 学历工作经验
recruiter 招聘人
position 招聘人所属职位
company_name 公司名称
company_join 公司类型/融资情况/人员规模
company_requirements 公司要求
company_benefits 公司福利
'''
pd_lis.append([company_name,job_title,salary, job_address, work_experience, recruiter, position, company_join,
company_requirements, company_benefits])
if num % 30 == 0:
if num == 300:
df = pd.DataFrame(data=pd_lis,
columns=['公司名称','招聘职位', '薪资区间', '公司位置', '要求的学历/工作经验', '招聘人', '招聘人所属职位',
'公司类型/融资情况/人员规模', '公司要求', '公司福利'])
df.to_csv('/xxxxx/recruitment.csv')#自己本地的路径
browser.close()
browser.quit()
else :
#获取a标签下最后一个a标签,那个就是点击下一页
page = browser.find_elements(By.XPATH,'//div[@class="options-pages"]/a')
last_page = page[-1]
#点击下一页
last_page.click()
num += 1
sleep(random.randint(1,15))
六、结尾
通过这篇文章给大家分享我在学习过程中的一些经验和心得,希望能够对大家有所帮助,同时也接受大家的建议和意见,共同进步、共同学习。
如果你觉得我的文章对你有所帮助,我诚挚地邀请你关注、点赞和分享。