网络数据抓取-拉勾网职位列表和详情-requests案例

智能决策上手系列教程索引

这次我们来比较完整的抓取拉勾网上面“人工智能”相关招聘信息以及招聘要求详情。

分析页面,寻找数据来源

打开拉勾网,搜索“人工智能”得到下面这个页面。
共30页,每页展示15个职位,应该最多共计450个职位,但不知道为什么页面上写[职位(500+)]。

image.png

【右键-查看网页源代码】,然后【Ctrl+F】搜索任意一个职位中比较独特的单词,比如“骏嘉财通”,搜不到,这说明数据肯定不在html源代码里面。

image.png

只能从网络面板中查找了,【右键-检查元素】,切换到【Networks】面板。为了清晰一些,我们先点清除,然后点底部的页面分页按钮【2】,得到如下图情况,注意type类型为xhr的两个请求,它们很可能包含我们所需要的数据:


image.png

点击请求查看详细,如下图,在预览【preview】面板中看到,positionAjax.json?needAddtionalResult=false就是我们需要的请求。

image.png

如上图,职位列表数据存在于这个请求结果的.content.positionResult.result下面。

设置参数,复制url、header和params

点击请求可以直接【右键-Copy-Copy ...】复制到链接地址link address,复制到请求头request headers
在右侧Headers面板底部有【FormData】可以看到params的列表,其中first看不出意思,pn应该就是页数,大概是page number的意思,kd不知什么意思,但肯定就是我们的搜索词。

image.png

按照以前我们的方法,在Notebook中新建Python 3文件,复制粘贴两个cell单元,代码如下(headers需要替换成你自己复制的内容)。

注意一定要去掉Content-Length: ...一行

#cell-1
url = 'https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false'
params = {
    'first': 'false',
    'pn': '2',
    'kd': '人工智能',
}
headers='''
POST /jobs/positionAjax.json?needAddtionalResult=false HTTP/1.1
Host: www.lagou.com
Connection: keep-alive
Origin: https://www.lagou.com
X-Anit-Forge-Code: 0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
X-Anit-Forge-Token: None
Referer: https://www.lagou.com/jobs/list_%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD?labelWords=&fromSearch=true&suginput=
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7
Cookie: JSESSIONID=ABAAABAAAGFA......f756e6=1538875676
'''
#cell-2 转换headers为字典
def str2obj(s, s1=';', s2='='):
    li = s.split(s1)
    res = {}
    for kv in li:
        li2 = kv.split(s2)
        if len(li2) > 1:
            res[li2[0]] = li2[1]
    return res


headers = str2obj(headers, '\n', ': ')

发起请求,获取职位信息列表

先测试一个页面,只获取职位标题:

#cell-3
import requests
import pandas as pd

jsonData = requests.get(url, params=params, headers=headers)
data = pd.read_json(jsonData.text, typ='series')

jobs = data['content']['positionResult']['result']
print(json.dumps(jobs[0], indent=2, ensure_ascii=False))

代码的一些说明:

  • 这次我们没有使用import json模块,而是使用了更为强大的pandas模块,它是最常用的数据处理模块之一。
  • 这里的jobs = data['content']['positionResult']['result']请参照上面Network面板请求的preview预览对照。
  • json.dumps(jobs[0], indent=2,ensure_ascii=False)jobs[0]是指职位列表的第一个,json.dumps(...)是把它按正常格式显示出来,否则直接print(jobs[0])也可以,但就会混乱不换行。

运行全部代码,正常应该输出第二页第一个职位的全部信息:


image.png

这样我们就可以知道:

  • jobs[0]['positionId']就是职位索引,是唯一性的数字
  • jobs[0]['positionName']就是职位名
  • jobs[0]['workYear']就是要求工作经验
  • jobs[0]['education']就是要求学历
  • jobs[0]['city']就是工作地点
  • jobs[0]['salary']就是薪资
  • jobs[0]['companyFullName']就是公司名(图中未显示到)
  • jobs[0]['industryField']就是公司行业,“金融”
  • jobs[0]['companySize']就是公司员工数量
  • jobs[0]['firstType']就是这个职位的类别,“产品|需求|项目类”,(图中未显示到)
  • jobs[0]['secondType']就是这个职位的二级分类,“数据分析”,(图中未显示到)
  • ...

存储数据,把每个职位信息存为单独文件

因为数据很多,我们也说不准以后会用到哪个,所以我们把每个job都存储为一个文件,以后只要读取就可以了,避免因为少存了数据还要重新抓取的麻烦。

改进上面的cell-3代码(为确保可以运行,必须在Notebook代码文件所在文件夹下创建data文件夹,进入data文件夹依次创建lagou_ai文件夹、jobs文件夹,否则会报错):

#cell-3
import json
import requests

jsonData = requests.get(url, params=params, headers=headers)
json.loads(jsonData.text)
jobs = data['content']['positionResult']['result']
for job in jobs:
    fname='./data/lagou_ai/jobs/'+str(job['positionId'])+'.json'   
    with open(fname,'w') as f:
        f.write(json.dumps(job))
        f.close()

代码说明:

  • 这个代码将自动存储15个.json文件在./data/lagou_ai/jobs/文件夹下。
  • 这次没有使用pandas模块,仍然使用了json模块,因为pandas不方便把json变为可以写入文件的字符串,而json.dumps(...)就很好用。
  • fname='./data/lagou_ai/jobs/'+str(job['positionId'])+'.json',这是利用每个职位索引positionId都是唯一不重复的特性,创建不重复的文件名。

完整运行代码,可以在./data/lagou_ai/jobs/文件夹下生成15个数字文件名的文件,你可以尝试用下面的代码打开其中一个,试试看pandas是否可以正常使用这些数据,注意这里的file:后面内容应该是完整路径(苹果系统的Command+Alt+C):

import pandas as pd
data = pd.read_json('file:/Users/zhyuzh/Desktop/Jupyter/spiders/data/lagou_ai/jobs/3128720.json', typ='series')
print(data['positionName'])

读取二层页面,获取单个职位要求的详情

在网页里面点击任意一个职位进入查看详情,例如https://www.lagou.com/jobs/4263258.html

image.png

参照我们最开始的方法可以发现,我们需要的信息就在右键html网页源代码里面,就在一个class='job_bt'的dd标签里面:

image.png

我们需要使用beautifulsoup来处理html内容:

#cell-2.5
from bs4 import BeautifulSoup
def readJobDetails(pid):
    html = requests.get('https://www.lagou.com/jobs/'+str(pid)+'.html', headers=headers)
    soup = BeautifulSoup(html.text, 'html.parser')
    res=soup.find('dd','job_bt').text.replace('\n','')
    return res

print(readJobDetails(4263258))    

这里.text.replace('\n','')去掉了回车换行,最后一行print(readJobDetails(4263258))执行了测试,成功了就可以把它删除。
成功的话会输出一大堆字符串,和上面html代码截图差不多。

完善代码,读取更多职位列表页面

我们来把代码整合一下,刚才我们只读取过一页15个职位基本信息,或者单个职位的详细信息,我们把它整合到一起,最终代码如下(注意替换headers并去除content-length):


# coding: utf-8

# ## 拉钩搜索‘人工智能’职位列表,包含二级页面详情

# ### 设置

# In[1]:


url = 'https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false'
params = {
    'first': 'false',
    'pn': '1',
    'kd': '人工智能',
}
savePath='./data/lagou_ai'
headers='''
!!!必须替换POST /jobs/positionAjax.json?needAddtionalResult=false HTTP/1.1
Host: www.lagou.com
Connection: keep-alive
Origin: https://www.lagou.com
X-Anit-Forge-Code: 0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
X-Anit-Forge-Token: None
Referer: https://www.lagou.com/jobs/list_%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD?labelWords=&fromSearch=true&suginput=
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7
Cookie: JSESSIONID=ABAAABA...538875676
'''


# In[2]:


#转换headers为字典
def str2obj(s, s1=';', s2='='):
    li = s.split(s1)
    res = {}
    for kv in li:
        li2 = kv.split(s2)
        if len(li2) > 1:
            res[li2[0]] = li2[1]
    return res


headers = str2obj(headers, '\n', ': ')


# ### 读取单个职位详情的函数

# In[3]:


from bs4 import BeautifulSoup


def readJobDetails(pid):
    html = requests.get(
        'https://www.lagou.com/jobs/' + str(pid) + '.html', headers=headers)
    soup = BeautifulSoup(html.text, 'html.parser')
    res = soup.find('dd', 'job_bt').text.replace('\n', '')
    return res


# ### 发起请求

# In[4]:


import json
import requests
import time

for n in range(1, 30):
    params['pn'] = n
    jsonData = requests.get(url, params=params, headers=headers)
    data = json.loads(jsonData.text)
    jobs = data['content']['positionResult']['result']
    for job in jobs:
        pid = str(job['positionId'])
        fname = savePath + '/jobs/' + pid + '.json'
        job['details'] = readJobDetails(job['positionId'])
        with open(fname, 'w') as f:
            f.write(json.dumps(job))
            f.close()
        print('>Got job:', pid)
        time.sleep(1)
    time.sleep(1)
print('>Finished!')

代码的几个说明:

  • 顶部paramspn恢复为1,之前我们一直使用的是2...
  • 将存储文件的目录放到开始savePath设置,方便以后修改
  • def readJobDetails...必须要放在for n in range...前面,先def然后才能使用。
  • 因为过程超过400秒也就是有八九分钟,所以用print('>Got job:', pid)只是让过程能够有点反应,看上去不像死机。
  • 每读取一页列表,time.sleep(1)休息1秒,每读取一个详情页面也要time.sleep(1)休息一秒。这样比较低调不容易被封禁。

运行整个代码,可以在./data/lagou_ai/jobs下生成450个xxxxxxx.json文件,你可以在资源管理器(win)或者访达(mac)中查看文件一个个变多以确保程序顺路进行中。

最后别忘了测试一下存储的数据是否正确:

import pandas as pd
data = pd.read_json('file:/Users/zhyuzh/Desktop/Jupyter/spiders/data/lagou_ai/jobs/5177921.json', typ='series')
print(data['positionName'])
print(data['details'])

总结

  • 先分析页面,知道数据从哪里来、什么格式?(网页源代码html?Network面板的xhr请求到的json数据?)
  • 然后找到对应的headers和params(如果有的话,详情页就没有)
  • 根据数据来源格式确定使用BeautifulSoup还是json模块来解析,具体解析方法要多看教程多查资料多学习
  • 发起Request请求get数据,然后解析到我们需要的内容
  • 利用def定义不同的函数处理单独的请求可以让代码更清晰
  • 把获取的内容存储到文件(简单横竖列表数据存.csv,复杂结构存.json),注意要转为字符串存储,注意文件名要唯一。
  • 尽可能在必要的位置合理的使用time.sleep(1)延缓一下
  • 代码要一点点测试,如果要for n in range(1,30)那就先for n in range(1,2)试一下能不能成功。

智能决策上手系列教程索引

每个人的智能决策新时代

如果您发现文章错误,请不吝留言指正;
如果您觉得有用,请点喜欢;
如果您觉得很有用,欢迎转载~


END

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容