目录
一、项目背景
随着科技的不断进步与发展,数据呈现爆发式的增长,各行各业对于数据的依赖越来越强,与数据打交道在所难免,而社会对于“数据”方面的人才需求也在不断增大。因此了解当下企业究竟需要招聘什么样的人才?需要什么样的技能?不管是对于在校生,还是对于求职者来说,都显得十分必要。
对于一名小白来说,想要入门数据分析,首先要了解目前社会对于数据相关岗位的需求情况,基于这一问题,本文针对前程无忧招聘网站,利用python爬取了其全国范围内大数据、数据分析、数据挖掘、机器学习、人工智能等与数据相关的岗位招聘信息。并通过Tableau可视化工具分析比较了不同行业的岗位薪资、用人需求等情况;以及不同行业、岗位的知识、技能要求等。
可视化分析效果图示例:
二、数据爬取
- 爬取字段:岗位名称、公司名称、薪资水平、工作经验、学历需求、工作地点、招聘人数、发布时间、公司类型、公司规模、行业领域、福利待遇、职位信息;
- 说明:在前程无忧招聘网站中,我们在搜索框中输入“数据”两个字进行搜索发现,共有2000个一级页面,其中每个页面包含50条岗位信息,因此总共有约100000条招聘信息。当点击一级页面中每个岗位信息时,页面会跳转至相应岗位的二级页面,二级页面中即包含我们所需要的全部字段信息;
一级页面如下:
二级页面如下:
- 爬取思路:先针对一级页面爬取所有岗位对应的二级页面链接,再根据二级页面链接遍历爬取相应岗位信息;
- 开发环境:python3、Spyder
1、相关库的导入与说明
import json
import requests
import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from lxml import etree
from selenium.webdriver import ChromeOptions
由于前程无忧招聘网站的反爬机制较强,采用动态渲染+限制ip访问频率等多层反爬,因此在获取二级页面链接时需借助json进行解析,本文对于二级页面岗位信息的获取采用selenium模拟浏览器爬取,同时通过代理IP的方式,每隔一段时间换一次请求IP以免触发网站反爬机制。
2、获取二级页面链接
1)分析一级页面url特征
# 第一页URL的特征
"https://search.51job.com/list/000000,000000,0000,00,9,99,数据,2,1.html?"
# 第二页URL的特征
"https://search.51job.com/list/000000,000000,0000,00,9,99,数据,2,2.html?"
# 第三页URL的特征
"https://search.51job.com/list/000000,000000,0000,00,9,99,数据,2,3.html?"
通过观察不同页面的URL可以发现,不同页面的URL链接只有“.html”前面的数字不同,该数字正好代表该页的页码 ,因此只需要构造字符串拼接,然后通过for循环语句即可构造自动翻页。
2)构建一级url库
url1 = []
for i in range(2000):
url_pre = "https://search.51job.com/list/000000,000000,0000,00,9,99,数据,2,%s" % (1+i) #设置自动翻页
url_end = ".html?"
url_all = url_pre + url_end
url1.append(url_all)
print("一级URL库创建完毕")
3)爬取所有二级url链接
url2 = []
j = 0
for url in url1:
j += 1
re1 = requests.get(url , headers = headers,proxies= {'http':'tps131.kdlapi.com:15818'},timeout=(5,10)) #通过proxies设置代理ip
html1 = etree.HTML(re1.text)
divs = html1.xpath('//script[@type = "text/javascript"]/text()')[0].replace('window.__SEARCH_RESULT__ = ',"")
js = json.loads(divs)
for i in range(len(js['engine_jds'])):
if js['engine_jds'][i]['job_href'][0:22] == "https://jobs.51job.com":
url2.append(js['engine_jds'][i]['job_href'])
else:
print("url异常,弃用") #剔除异常url
print("已爬取"+str(j)+"页")
print("成功爬取"+str(len(url2))+"条二级URL")
注意:爬取二级URL链接时发现并非爬取的所有链接都是规范的,会存在少部分异常URL,这会对后续岗位信息的爬取造成干扰,因此需要利用if条件语句对其进行剔除。
3、获取岗位信息并保存
option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation'])
option.add_argument('--proxy-server=http://tps131.kdlapi.com:15818') #设置代理ip
driver = webdriver.Chrome(options=option)
for url in url2:
co = 1
while co == 1:
try:
driver.get(url)
wait = WebDriverWait(driver,10,0.5)
wait.until(EC.presence_of_element_located((By.ID,'topIndex')))
except:
driver.close()
driver = webdriver.Chrome(options=option)
co = 1
else:
co = 0
try:
福利待遇 = driver.find_elements_by_xpath('//div[@class = "t1"]')[0].text
岗位名称 = driver.find_element_by_xpath('//div[@class = "cn"]/h1').text
薪资水平 = driver.find_element_by_xpath('//div[@class = "cn"]/strong').text
职位信息 = driver.find_elements_by_xpath('//div[@class = "bmsg job_msg inbox"]')[0].text
公司类型 = driver.find_elements_by_xpath('//div[@class = "com_tag"]/p')[0].text
公司规模 = driver.find_elements_by_xpath('//div[@class = "com_tag"]/p')[1].text
公司领域 = driver.find_elements_by_xpath('//div[@class = "com_tag"]/p')[2].text
公司名称 = driver.find_element_by_xpath('//div[@class = "com_msg"]/a/p').text
工作地点 = driver.find_elements_by_xpath('//div[@class = "cn"]//p[@class = "msg ltype"]')[0].text.split("|")[0]
工作经验 = driver.find_elements_by_xpath('//div[@class = "cn"]//p[@class = "msg ltype"]')[0].text.split("|")[1]
学历要求 = driver.find_elements_by_xpath('//div[@class = "cn"]//p[@class = "msg ltype"]')[0].text.split("|")[2]
招聘人数 = driver.find_elements_by_xpath('//div[@class = "cn"]//p[@class = "msg ltype"]')[0].text.split("|")[3]
发布时间 = driver.find_elements_by_xpath('//div[@class = "cn"]//p[@class = "msg ltype"]')[0].text.split("|")[4]
except:
福利待遇 = "nan"
岗位名称 = "nan"
薪资水平 = "nan"
职位信息 = "nan"
公司类型 = "nan"
公司规模 = "nan"
公司领域 = "nan"
公司名称 = "nan"
工作地点 = "nan"
工作经验 = "nan"
学历要求 = "nan"
招聘人数 = "nan"
发布时间 = "nan"
print("信息提取异常,弃用")
finally:
info = {
"岗位名称" : 岗位名称,
"公司名称" : 公司名称,
"薪资水平" : 薪资水平,
"工作经验" : 工作经验,
"学历要求" : 学历要求,
"工作地点" : 工作地点,
"招聘人数" : 招聘人数,
"发布时间" : 发布时间,
"公司类型" : 公司类型,
"公司规模" : 公司规模,
"公司领域" : 公司领域,
"福利待遇" : 福利待遇,
"职位信息" : 职位信息
}
jobs_info.append(info)
df = pd.DataFrame(jobs_info)
df.to_excel(r"E:\python爬虫\前程无忧招聘信息.xlsx")
在爬取并剔除异常数据之后,最终得到了90000多条完整的数据做分析,但经过观察发现,所爬取的数据并非全都与“数据”岗位相关联。实际上,前程无忧招聘网站上与“数据”有关的只有几百页,而我们爬取了2000页的所有数据,因此在后面进行数据处理时需要把无关的数据剔除掉。在爬取前根据对代码的测试发现,有些岗位字段在进行爬取时会出现错位,从而导致数据存储失败,为了不影响后面代码的执行,这里设置了“try-except”进行异常处理,同时使用while循环语句在服务器出现请求失败时关闭模拟浏览器并进行重新请求。