招聘数据爬取、数据处理与可视化(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!