新手初试python爬取51job(前程无忧)的职位信息
程序员文章站
2024-03-20 21:45:10
...
目标:
搜索关键字:数据分析
地点: 北上广深
想要获取的信息为:
以及点击职位名进入详细信息中的:
这些数据并写入csv文件中。
分析:
1、url
中‘%25E6%2595%25B0%25E6%258D%25AE%25E5%2588%2586%25E6%259E%2590’为关键字转码后的编码。
‘010000%252C020000%252C030200%252C040000’应为‘北上广深’地理位置的转码。
‘1.html’为页数。
2、请求request
3、查看目标数据的html结构为:
码代码
1、导包:
from bs4 import BeautifulSoup # 解析html
import urllib.parse # 调整关键字编码
import requests # 请求所用的包
import csv # 内置csv包,读写csv文件时使用
import time # time延迟操作,防止爬取数据过快导致限制ID访问——不知道是否有用!!!
2、url头部处理和关键字编码:
# 关键字
key = '数据分析'
# 编码调整,如将“数据分析师”编码成%25E6%2595%25B0%25E6%258D%25AE%25E5%2588%2586%25E6%259E%2590%25E5%25B8%2588
key = urllib.parse.quote(urllib.parse.quote(key))
# 代理
User_Agent = r'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'
headers = {'User-Agent': User_Agent,
'Host': 'search.51job.com',
'Upgrade-Insecure-Requests': '1'}
3、构造获取html的方法:
# 获取jd列表页面的html
def getListHtml(page):
print('开始获取html')
url = 'https://search.51job.com/list/010000%252C020000%252C030200%252C040000,000000,0000,01,9,99,' + \
key + ',2,' + str(page) + '.html' # 构造url
r = requests.get(url, headers, timeout=10) # 使用requests发送请求
r.encoding = 'gbk'
time.sleep(1) # 延迟1秒
return r.text # 返回html
# 获取jd详情页面的html
def getDetailsHtml(url):
print('开始岗位详情html')
r = requests.get(url, headers, timeout=10) # 使用requests发送请求
r.encoding = 'gbk'
time.sleep(0.5) # 延迟0.5秒
return r.text # 返回html
4、解析html
(1)解析职位列表
# 获取html
html = getListHtml(page) # 此代码中page未定义
# 使用BeautifulSoup解析html
soup = BeautifulSoup(html, 'html.parser')
# 根据网页html结构 查找出div的class包含‘el’的所有节点
all_link = soup.find_all('div', class_='el')
# 遍历所有节点
for link in all_link:
# 判断节点下是否有p标签
if link.p:
# 判断p标签下是否有a标签
if link.p.a:
# 以上判断是基于网页的dom结构分析得出
# 获取职位名称,get_text()的参数可以不带,具体说明查看BeautifulSoup文档!
jd_name = link.p.a.get_text("|", strip=True) # 职位名
# 获取职位详情的url,为获取第二部分数据做准备
jd_url = link.p.a['href'] # 职位详情url
company_name = link.find('span', class_='t2').string # 公司名
workplace = link.find('span', class_='t3').string # 工作地点
salary = link.find('span', class_='t4').string # 薪资
issue_time = link.find('span', class_='t5').string # 发布时间
以上代码是根据网页结构(如下)自行分析得出的(因为本人第一次写爬虫,仅以自己经验得出,望见谅):
<div class="el">
<p class="t1 ">
<em class="check" name="delivery_em" οnclick="checkboxClick(this)"></em>
<input class="checkbox" type="checkbox" name="delivery_jobid" value="116715922" jt="0" style="display:none">
<span>
<a target="_blank" title="市场大数据分析" href="https://jobs.51job.com/shanghai-ptq/116715922.html?s=01&t=0" οnmοusedοwn="">
市场大数据分析 </a>
</span>
</p>
<span class="t2"><a target="_blank" title="上海柏迦科技有限公司" href="https://jobs.51job.com/all/co5450398.html">上海柏迦科技有限公司</a></span>
<span class="t3">上海-普陀区</span>
<span class="t4">0.8-1万/月</span>
<span class="t5">11-28</span>
</div>
(2)解析职位详情
# 获取html
html = getDetailsHtml(jd_url)
# 解析html
soup = BeautifulSoup(html, 'html.parser')
# 判断div的class中是否含有‘cn’的节点,为什么要判断呢?因为有少数职位详情页面的url并非51job的详情页面,而是某些企业官网的招聘url,所有页面结构不一样,这种数据我自动舍弃了。
if soup.find('div', class_='cn'):
# 找到包含数据信息的节点,并获取信息。但这些信息是糅杂在一起的,需要二次解析。
result = soup.find('div', class_='cn').find('p', class_='msg').get_text(" ", strip=True)
# 拆分糅杂数据,并不完全准确,有些企业的招聘信息没有经验要求等导致
experience = result.split('|')[1] # 经验要求
education = result.split('|')[2] # 学历要求
people = result.split('|')[3] # 招聘人数
# 将岗位描述、要求板块的所有文字拿下来了。
describe = soup.find('div', attrs='bmsg job_msg inbox').get_text(' ', strip=True) # 岗位职责要求
以上代码是根据网页结构(如下)自行分析得出的:
<p class="msg ltype" title="北京-海淀区 | 无工作经验 | 招1人 | 11-28发布">
北京-海淀区 <span>|</span> 无工作经验 <span>|</span> 招1人 <span>|</span> 11-28发布
</p>
<div class="bmsg job_msg inbox">
<p>1、负责RWS项目的实施;</p>
<p>2、负责药物经济学项目的实施;</p>
<p>任职要求:</p>
<p>1、统招硕士以上学历,流行病学或卫生统计相关专业;</p>
<p>2、可以熟练运用相关分析模型和方法进行项目设计并推动实施;</p>
<p>3、具备与团队共同完成项目的经验,擅长、有意愿与团队成员合作完成工作任务。</p>
<div class="mt10">
<p class="fp">
<span class="label">职能类别:</span>
<a class="el tdn" href="https://jobs.51job.com/beijing-hdq/linchuangshuju/">临床数据分析员</a>
</p>
</div>
<div class="share">
<a track-type="jobsButtonClick" event-type="6" class="a" href="javascript:void(0);" οnclick="weixinMa();">微信分享</a>
<div id="weixinMa_fx" style="display:none;">
<img width="198" height="198" alt="二维码" org="https://jobs.51job.com/wx_qrcode.php?url=https%3A%2F%2Fm.51job.com%2Fsearch%2Fjobdetail.php%3Fjobid%3D118510840">
</div>
</div>
<div class="clear"></div>
</div>
5、写入csv文件:
# 打开csv文件 ——'a+'-'追加'
csvFile = open("51job数据分析.csv", 'a+', newline='')
writer = csv.writer(csvFile)
# 写表头
writer.writerow(('职位名',
'职位详情url',
'公司名',
'工作地点',
'薪资',
'发布时间',
'经验要求',
'学历要求',
'招聘人数',
'职位信息'))
# 写入数据——值的注意的一点就是岗位描述中可能会出现编码错误,要忽略才可以不然会报错。
writer.writerow((jd_name, jd_url, company_name, workplace, salary, issue_time, experience, education, people, describe.encode("gbk", 'ignore').decode("gbk", "ignore")))
# 关闭写入文件
csvFile.close()
最终代码:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
File Name: 51job
Description :
Author : Mr.Zhang
date: 2019/11/27 0027
-------------------------------------------------
Change Activity:
2019/11/27 0027:
-------------------------------------------------
"""
__author__ = 'Mr.Zhang'
from bs4 import BeautifulSoup # 解析html
import urllib.parse # 调整关键字编码
import requests # 请求所用的包
import csv # 内置csv包,读写csv文件时使用
import time # time延迟操作,防止爬取数据过快导致限制ID访问——不知道是否有用!!!
# 关键字
key = '数据分析'
# 编码调整,如将“数据分析师”编码成%25E6%2595%25B0%25E6%258D%25AE%25E5%2588%2586%25E6%259E%2590%25E5%25B8%2588
key = urllib.parse.quote(urllib.parse.quote(key))
# 代理
User_Agent = r'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'
headers = {'User-Agent': User_Agent,
'Host': 'search.51job.com',
'Upgrade-Insecure-Requests': '1'}
# 获取jd列表页面的html
def getListHtml(page):
print('获取jd列表')
url = 'https://search.51job.com/list/010000%252C020000%252C030200%252C040000,000000,0000,01,9,99,' + \
key + ',2,' + str(page) + '.html' # 构造url
r = requests.get(url, headers, timeout=10) # 使用requests发送请求
r.encoding = 'gbk'
time.sleep(1)
return r.text # 返回html
# 获取jd详情页面的html
def getDetailsHtml(url):
print('获取jd详情')
r = requests.get(url, headers, timeout=10)
r.encoding = 'gbk'
time.sleep(0.5)
return r.text # 返回html
# 打开csv文件
csvFile = open("51job数据分析.csv", 'a+', newline='')
writer = csv.writer(csvFile)
writer.writerow(('职位名',
'职位详情url',
'公司名',
'工作地点',
'薪资',
'发布时间',
'经验要求',
'学历要求',
'招聘人数',
'职位信息'))
try:
# 51job每页有50个岗位,一共有220页
for page in range(1, 220):
# 获取html
html = getListHtml(page)
# 使用BeautifulSoup解析html
soup = BeautifulSoup(html, 'html.parser')
# 根据网页html结构 查找出div的class包含‘el’的所有节点
all_link = soup.find_all('div', class_='el')
# 遍历所有节点
for link in all_link:
# 判断节点下是否有p标签
if link.p:
# 判断p标签下是否有a标签
if link.p.a:
jd_name = link.p.a.get_text("|", strip=True) # 职位名
jd_url = link.p.a['href'] # 职位详情url
company_name = link.find('span', class_='t2').string # 公司名
workplace = link.find('span', class_='t3').string # 工作地点
salary = link.find('span', class_='t4').string # 薪资
issue_time = link.find('span', class_='t5').string # 发布时间
html = getDetailsHtml(jd_url)
soup = BeautifulSoup(html, 'html.parser')
if soup.find('div', class_='cn'):
result = soup.find('div', class_='cn').find('p', class_='msg').get_text(" ", strip=True)
experience = result.split('|')[1] # 经验要求
education = result.split('|')[2] # 学历要求
people = result.split('|')[3] # 招聘人数
describe = soup.find('div', attrs='bmsg job_msg inbox').get_text(' ', strip=True) # 岗位职责要求
print('写入数据:{}'.format(describe))
writer.writerow(
(jd_name, jd_url, company_name, workplace, salary, issue_time, experience, education, people, describe.encode("gbk", 'ignore').decode("gbk", "ignore")))
except Exception as e:
print(e)
finally:
# 关闭写入文件
csvFile.close()
最后提醒一下:
程序中每个岗位爬取延迟了0.5秒,所以1W数据跑了将近2小时。