写在前面:
拉勾网数据爬取是一个蛮经典的爬虫案例 ,由于被频繁被爬取的原因 ,网站经过不断更新 ,加入了一些反爬技术 。例如:参数的加密 、AJAX异步加载JSON数据 。对于入门爬虫的新手来说 ,还是有一些困难 。这里用到selenium和post请求两种方式解析网页 ,希望可以帮到你们 。
分析网页:
这里我们以 数据分析 该职位为例 : 链接
数据在网页一般会分为两种加载方式 : 在原网页内与不在原网页内 。判断的方法可以复制网页内要爬取的一些数据 ,然后右击网页 ——查看源代码 , 看一看是否可以找到 。:
按ctrl + f 输入复制的的数据发现并没有存在原网页内 。
那么可以推断出 , 我们要爬取的数据是通过AJAX异步加载JSON数据到网页的 。
那我们需要在网页后台找到那个json数据 ,我用的是chrom浏览器 ,右击 ——检查 :
在picture_1中 选择 network --- XHR (如果没有数据的话,重新加载一下网页) ,可以在左边看到四个json链接 。因为我们要爬取的是职位 ,很快我们就可以确定目标url ,picture_3验证了准确性 :
在picture_3中我们可以很清楚的看见这是一个json格式的数据 ,通过分析 ,可以找到数据在result下面 :
通过对比picture_5发现就是我们要爬取的数据 。
编写爬虫:
https://www.lagou.com/jobs/positionAjax.json?px=default&city=%E8%8B%8F%E5%B7%9E&needAddtionalResult=false
我们要解析的url在Headers中可以看出是post请求 。
下面我们构造headers :
my_headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
'Host':'www.lagou.com',
'Referer':'https://www.lagou.com/jobs/list_{}?labelWords=sug&fromSearch=true&suginput='.format(position_id),
'X-Anit-Forge-Code':'0',
'X-Anit-Forge-Token': 'None',
'X-Requested-With':'XMLHttpRequest'
}
其中的referer可以看到 ,我用format函数加入了一个参数 ,便于爬取不同城市的数据 , 如果你只打算爬取一个城市这里可以不加 ,只使用浏览器上带的参数 。
提交的 data:
my_data = {
'first': 'true',
'pn':num,
'kd':position
}
从picture中可以看出需要post数据主要有三个 ,其中 pn:为页码数 ,kd:为职位名称 。因此 ,我将这两个参数的值设置为变量 num和position ,便于接下来的post页码和不同职位的实现 。
def get_json(url,num,position,position_id):
#print(position)
'''''从网页获取JSON,使用POST请求,加上头部信息'''
my_headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
'Host':'www.lagou.com',
'Referer':'https://www.lagou.com/jobs/list_{}?labelWords=sug&fromSearch=true&suginput='.format(position_id),
'X-Anit-Forge-Code':'0',
'X-Anit-Forge-Token': 'None',
'X-Requested-With':'XMLHttpRequest'
}
my_data = {
'first': 'true',
'pn':num,
'kd':position}
res = requests.post(url, headers = my_headers, data = my_data)
res.raise_for_status()
res.encoding = 'utf-8'
# 得到包含职位信息的字典
page = res.json()
return page
这是带headers和data完整的post请求 , 其中函数由四个参数 ,url为刚刚我们找出的那个链接 。num是页码 ,这里我们先设置为起始页码1 。position为职位名称 ,position_id为职位代号 。我们可以从headers的referen中看出 :https://www.lagou.com/jobs/list_%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90?labelWords=sug&fromSearch=true&suginput=shujufenxi 在list_到?中的职位名称被加密出现, 所以我构造了一个字典 :
def getCity():
return [
{"苏州":"%E8%8B%8F%E5%B7%9E"},
{"深圳":"%E6%B7%B1%E5%9C%B3"},
{"上海":"%E4%B8%8A%E6%B5%B7"},
{"杭州":"%E6%9D%AD%E5%B7%9E"},
{"南京":"%E5%8D%97%E4%BA%AC"},
]
def getLanguage():
return [
#"python",
{"数据分析":"%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90"},
{"图像处理":"%E5%9B%BE%E5%83%8F%E5%A4%84%E7%90%86"},
{"":""},
{"":""},
{"":""},
]
将需要爬取的城市 、职位所对应的加密码一一对应 。
get_json函数返回了json数据 ,包括了职位总数 ,这样我们就可以构造一个函数 ,而且拉勾网 一页最多显示15个职位 ,计算出我们爬取职位的总页数了 。
def get_page_num(count):
'''''计算要抓取的页数'''
# 每页15个职位,向上取整
res = math.ceil(count/15)
# 拉勾网最多显示30页结果
if res > 30:
return 30
else:
return res
计算出页数之后 , 我们就可以通过一个循环 ,带着可变参数反复post数据:
for n in range(1,num+1):
# 对每个网页读取JSON, 获取每页数据
page = get_json(url,n,position,position_id)
try:
jobs_list = page['content']['positionResult']['result']
except:
continue
page_info = get_info_to_mongodb(jobs_list)
res_list.extend(page_info)
查看返回的结果 ,这里我通过构造一个含有dict的list便于存入mongodb ,看个人需求 :
通过看原网页的数据 , 我们发现数据已经被爬取先来了 一共19个职位 ,共分两页