一.需求分析
拉勾网的职位页面详情是由 http://www.lagou.com/jobs/ PositionId.html 组成。
我们打开不同的python求职页面,可以看到url地址的区别(如图不同之处为6284525和6278314):
而 PositionId 可以通过分析 Json 的 XHR 获得。我们要爬取的数据在content—>positionResult----->result下,包含了工作地点、公司名、职位等信息。 我们需要保存这个数据:
知道了数据的源头,接下来就按照常规步骤包装 Headers ,提交 FormData 来获取反馈数据。
注意防止反爬虫:
1.拉勾网反爬虫做的比较严,请求头多添加几个参数才能不被网站识别。
2.我们找到真正的请求网址,发现返回的是一个 JSON 串,解析这个 JSON 串即可,而且注意是 POST
传值,通过改变 Form Data 中 pn 的值来控制翻页。
真实请求的网址:
请求头信息:
Form data:
二.项目代码
1.核心代码,包括了构造headers,构造form data,获取json数据。
import time
import requests
import logging
from config import *
import pprint
import pandas as pd
from concurrent.futures import ThreadPoolExecutor
def getPositionIDPage(url_start, url_parse, page=1, kd='python'):
"""
获取PositionId列表所在页面, 返回的时json数据;
:param url_start: 图形界面拉勾网职位信息的url地址;为了获取随机的session;
:param url_parse: 真实返回json格式的url地址
:param page: 访问的页数
:param kd: 搜索的关键字
:return: json数据格式的文本信息;
"""
# 构造请求头(headers)
headers = {'User-Agent': ua.random,
'Host': Host,
'Origin': Origin,
'Referer': Referer,
'Connection': Connection,
'Accept': Accept,
'proxies':proxies
}
# 构造表单
data = {
'first': False,
'pn': str(page),
'kd': kd
}
try:
# requests库的session对象能够帮我们跨请求保持某些参数,
# 也会在同一个session实例发出的所有请求之间保持cookies。
# 创建一个session对象
session = requests.Session()
# 用session对象发出get请求,设置cookies
session.get(url_start, headers=headers, timeout=3) # 请求首页获取cookies
cookie = session.cookies # 为此次获取的cookies
# 用session对象发出另外一个post请求,获取cookies , 返回响应信息
response = session.post(url=url_parse,
headers=headers,
data=data,
)
time.sleep(1)
# 响应状态码为4xx客户端错误,或者5xx服务器错误响应,来抛出异常:
response.raise_for_status()
response.encoding = response.apparent_encoding
except Exception as e:
logging.error("页面" + url_parse + "爬取失败:", e)
else:
logging.info("页面" + url_parse + "爬取成功" + str(response.status_code))
return response.json()
def analyse_PositionID(html):
def analyse_PositionID(html):
"""
根据获取的页面解析每一个招聘页面详情都有一个所属的ID索引
:param html:
:return:
"""
#tag = 'positionId'
positions = html['content']['positionResult']['result']
df = pd.DataFrame(positions)
return df
#封装爬取任务
def task(page):
# 拉勾网页面
url_start = 'https://www.lagou.com/jobs/list_python'
# 真实的拉勾网url地址
url_parse = 'https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false'
# 获取指定页拉勾网的职位信息, 返回的是json反序列化的结果
html = getPositionIDPage(url_start, url_parse, page=page)
# pprint.pprint(content)
# 解析页面, 返回DataFrame格式的数据;
df = analyse_PositionID(html)
return df
def save_as_csv():
# 开启线程池
with ThreadPoolExecutor(ThreadCount) as pool:
results = pool.map(task, range(1, pages + 1))
total_df = pd.concat(results, axis=0)
total_df.to_csv(csv_filename, sep=',', header=True, index=False)
logging.info("文件%s存储成功" % (csv_filename))
return total_df
if __name__ == '__main__':
# url_start = 'https://www.lagou.com/jobs/list_python'
# url_parse = 'https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult = false'
#
# content = getPositionIDPage(url_start, url_parse)
# pprint.pprint(content)
save_as_csv()
2.配置config文件,把经常修改的信息写在config文件中,便于修改
from fake_useragent import UserAgent
Host = 'www.lagou.com'
Origin = 'https://www.lagou.com'
Referer = 'https://www.lagou.com/jobs/list_python'
Connection = 'keep-alive'
Accept = 'application/json, text/javascript, */*; q=0.01'
ua = UserAgent(verify_ssl=False)
# 爬取数据的页数
pages = 10
# 存储信息的csv文件位置
csv_filename = 'lagou.csv'
# 多线程开启的线程数;
ThreadCount = 100
结果如下图:
三.拉勾网页面分析
1.职位类别统计与公司统计
import pandas as pd
from config import *
import matplotlib.pyplot as plt
import matplotlib#配置中文字体和修改字体大小
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['font.family'] = 'sans-serif'
matplotlib.rcParams['font.size'] = 12
# 用来正常显示负号
plt.rcParams['axes.unicode_minus']=False
df = pd.read_csv(csv_filename, encoding='utf-8')
def show_second_type():
# 获取职位类别分类并分组统计
secondType_Series = df['secondType'].value_counts()
# 设置图形的大小;
plt.figure(figsize=(10,5))
# 绘制条形图;
secondType_Series.plot.bar()
# 展示图形
plt.show()
def show_company():
companyShortName_Series = df['companyShortName'].value_counts()
companyShortName_Series_gt5 =
companyShortName_Series[companyShortName_Series > 5] # 选取招聘Python相关职位个数大
于等于5的公司
plt.figure(figsize=(10, 5))
companyShortName_Series_gt5.plot.bar()
plt.show()
show_company()
show_second_type()
职位统计:
公司统计: