招聘数据爬取、数据处理与可视化(v2--解析JS渲染页面)
招聘数据爬取、数据处理与可视化(v2--解析JS渲染页面)
更新说明
上一版本地址。
由于爬取的原网站的变化,当前数据是在JavaScript中,浏览器会对JavaScript代码进行渲染,然后再呈现。然而,当我们通过爬虫程序获取数据时,爬虫程序并不能自动对HTML文件中的JavaScript代码进行渲染。因此,需要获取JS中数据进行解析。
程序说明
通过爬取“51job”获取招聘信息(以计算机软件为例),根据所获取数据分析领域相关工作职位需求,并通过可视化的方式展示分析行业就业情况(例如平均月薪、工作地点等)。
数据爬取
使用requests
库请求网页内容,使用BeautifulSoup4
与正则表达库re
解析网页。
观察网页结构
首先在爬取网页前,使用使用浏览器“开发者工具”,观察网页结构。
页面解析
使用beautifulsoup解析页面,获取JS中所需数据:
soup.find_all('script')[7]
为了能够使用re解析获取内容,需要将内容转换为字符串:
results = str(soup.find_all('script')[7])
例如为了获取工作名,首先构造正则表达式:
pattern = re.compile(r"job_name":"'(.*?)'",re.MULTILINE|re.DOTALL)
然后查找所有的工作名:
job_names = pattern_job_name.findall(results)
为了方便获取所需字段,构造解析函数
def analysis(item,results):
pattern = re.compile(item, re.I|re.M)
result_list = pattern.findall(results)
return result_list
将数据写入csv文件中
观察解析出的数据,例如公司名地址:
https:\/\/jobs.51job.com\/all\/co6371981.html
例如工作名:
Trade Ops(Contractor, Operations)
为了将数据写入csv文件中,首先需要将获得数据中的逗号','
去除,因为其表示单元格的切换,然后还需去掉结果中的转义字符/
。
因此将获取的数据进行预处理:
def precess(item):
return item.replace(',', ' ').replace('\\', '')
通过观察页面链接,爬取所有页面
查看第2页链接为:
https://search.51job.com/list/000000,000000,0000,00,9,99,%25E8%25AE%25A1%25E7%25AE%2597%25E6%259C%25BA,2,2.html?lang=c&postchannel=0000&workyear=99&cotype=99°reefrom=99&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=
第3页链接为:
https://search.51job.com/list/000000,000000,0000,00,9,99,%25E8%25AE%25A1%25E7%25AE%2597%25E6%259C%25BA,2,3.html?lang=c&postchannel=0000&workyear=99&cotype=99°reefrom=99&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=
仅改变了页面数字,因此可以构造如下模式,并使用循环,爬取所有页面:
url_pattern = "https://search.51job.com/list/000000,000000,0000,00,9,99,%25E8%25AE%25A1%25E7%25AE%2597%25E6%259C%25BA,2,{}.html?lang=c&postchannel=0000&workyear=99&cotype=99°reefrom=99&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare="
for i in range(1,2001):
url = url_pattern.format(i)
爬虫程序完整代码
import time
import requests
from bs4 import BeautifulSoup
import os
import csv
import re
def analysis(item,results):
pattern = re.compile(item, re.I|re.M)
result_list = pattern.findall(results)
return result_list
def precess(item):
return item.replace(',', ' ').replace('\\', '')
#构建请求头
headers = {
'User-Agent':'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:86.0) Gecko/20100101 Firefox/86.0'
}
url_pattern = "https://search.51job.com/list/000000,000000,0000,00,9,99,%25E8%25AE%25A1%25E7%25AE%2597%25E6%259C%25BA,2,{}.html?lang=c&postchannel=0000&workyear=99&cotype=99°reefrom=99&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare="
if not os.path.exists("intro_job.csv"):
#创建存储csv文件存储数据
file = open('intro_job.csv', "w", encoding="utf-8-sig",newline='')
csv_head = csv.writer(file)
#表头
header = ['job','company','place','salary','date','detail_url']
csv_head.writerow(header)
file.close()
for i in range(1,2001):
#增加时延防止反爬虫
time.sleep(5)
url = url_pattern.format(i)
response = requests.get(url=url, headers=headers)
#声明网页编码方式,需要根据具体网页响应情况
response.encoding = 'gbk'
response.raise_for_status()
soup = BeautifulSoup(response.text, 'html.parser')
#pattern = re.compile(r"engine_search_result'(.*?)'",re.MULTILINE|re.DOTALL)
results = str(soup.find_all('script')[7])
job_names = analysis(r'"job_name":"(.*?)"', results)
company_names = analysis(r'"company_name":"(.*?)"', results)
workarea_texts = analysis(r'"workarea_text":"(.*?)"', results)
providesalary_texts = analysis(r'"providesalary_text":"(.*?)"', results)
updatedates = analysis(r'"updatedate":"(.*?)"', results)
job_hrefs = analysis(r'"job_href":"(.*?)"', results)
for i in range(len(job_names)):
with open('intro_job.csv', 'a+', encoding='utf-8-sig') as f:
f.write(precess(job_names[i]) + ','
+ precess(company_names[i]) + ','
+ precess(workarea_texts[i]) + ','
+ precess(providesalary_texts[i]) + ','
+ precess(updatedates[i]) +','
+ precess(job_hrefs[i]) + '\n')
爬取数据结果
展示部分爬取结果:
数据及可视化
该部分参考之前博客。
The end
Enjoying coding!