智能決策上手系列教程索引
這次我們來比較完整的抓取拉勾網(wǎng)上面“人工智能”相關(guān)招聘信息以及招聘要求詳情。
分析頁面,尋找數(shù)據(jù)來源
打開拉勾網(wǎng),搜索“人工智能”得到下面這個頁面。
共30頁,每頁展示15個職位,應(yīng)該最多共計450個職位,但不知道為什么頁面上寫[職位(500+)]。

【右鍵-查看網(wǎng)頁源代碼】,然后【Ctrl+F】搜索任意一個職位中比較獨特的單詞,比如“駿嘉財通”,搜不到,這說明數(shù)據(jù)肯定不在html源代碼里面。

只能從網(wǎng)絡(luò)面板中查找了,【右鍵-檢查元素】,切換到【Networks】面板。為了清晰一些,我們先點清除,然后點底部的頁面分頁按鈕【2】,得到如下圖情況,注意type類型為xhr的兩個請求,它們很可能包含我們所需要的數(shù)據(jù):

點擊請求查看詳細(xì),如下圖,在預(yù)覽【preview】面板中看到,positionAjax.json?needAddtionalResult=false就是我們需要的請求。

如上圖,職位列表數(shù)據(jù)存在于這個請求結(jié)果的.content.positionResult.result下面。
設(shè)置參數(shù),復(fù)制url、header和params
點擊請求可以直接【右鍵-Copy-Copy ...】復(fù)制到鏈接地址link address,復(fù)制到請求頭request headers。
在右側(cè)Headers面板底部有【FormData】可以看到params的列表,其中first看不出意思,pn應(yīng)該就是頁數(shù),大概是page number的意思,kd不知什么意思,但肯定就是我們的搜索詞。

按照以前我們的方法,在Notebook中新建Python 3文件,復(fù)制粘貼兩個cell單元,代碼如下(headers需要替換成你自己復(fù)制的內(nèi)容)。
注意一定要去掉
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 轉(zhuǎn)換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', ': ')
發(fā)起請求,獲取職位信息列表
先測試一個頁面,只獲取職位標(biāo)題:
#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模塊,它是最常用的數(shù)據(jù)處理模塊之一。 - 這里的
jobs = data['content']['positionResult']['result']請參照上面Network面板請求的preview預(yù)覽對照。 -
json.dumps(jobs[0], indent=2,ensure_ascii=False)中jobs[0]是指職位列表的第一個,json.dumps(...)是把它按正常格式顯示出來,否則直接print(jobs[0])也可以,但就會混亂不換行。
運行全部代碼,正常應(yīng)該輸出第二頁第一個職位的全部信息:

這樣我們就可以知道:
-
jobs[0]['positionId']就是職位索引,是唯一性的數(shù)字 -
jobs[0]['positionName']就是職位名 -
jobs[0]['workYear']就是要求工作經(jīng)驗 -
jobs[0]['education']就是要求學(xué)歷 -
jobs[0]['city']就是工作地點 -
jobs[0]['salary']就是薪資 -
jobs[0]['companyFullName']就是公司名(圖中未顯示到) -
jobs[0]['industryField']就是公司行業(yè),“金融” -
jobs[0]['companySize']就是公司員工數(shù)量 -
jobs[0]['firstType']就是這個職位的類別,“產(chǎn)品|需求|項目類”,(圖中未顯示到) -
jobs[0]['secondType']就是這個職位的二級分類,“數(shù)據(jù)分析”,(圖中未顯示到) - ...
存儲數(shù)據(jù),把每個職位信息存為單獨文件
因為數(shù)據(jù)很多,我們也說不準(zhǔn)以后會用到哪個,所以我們把每個job都存儲為一個文件,以后只要讀取就可以了,避免因為少存了數(shù)據(jù)還要重新抓取的麻煩。
改進(jìn)上面的cell-3代碼(為確??梢赃\行,必須在Notebook代碼文件所在文件夾下創(chuàng)建data文件夾,進(jìn)入data文件夾依次創(chuàng)建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變?yōu)榭梢詫懭胛募淖址?,?code>json.dumps(...)就很好用。 -
fname='./data/lagou_ai/jobs/'+str(job['positionId'])+'.json',這是利用每個職位索引positionId都是唯一不重復(fù)的特性,創(chuàng)建不重復(fù)的文件名。
完整運行代碼,可以在./data/lagou_ai/jobs/文件夾下生成15個數(shù)字文件名的文件,你可以嘗試用下面的代碼打開其中一個,試試看pandas是否可以正常使用這些數(shù)據(jù),注意這里的file:后面內(nèi)容應(yīng)該是完整路徑(蘋果系統(tǒng)的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'])
讀取二層頁面,獲取單個職位要求的詳情
在網(wǎng)頁里面點擊任意一個職位進(jìn)入查看詳情,例如https://www.lagou.com/jobs/4263258.html:

參照我們最開始的方法可以發(fā)現(xiàn),我們需要的信息就在右鍵html網(wǎng)頁源代碼里面,就在一個class='job_bt'的dd標(biāo)簽里面:

我們需要使用beautifulsoup來處理html內(nèi)容:
#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))執(zhí)行了測試,成功了就可以把它刪除。
成功的話會輸出一大堆字符串,和上面html代碼截圖差不多。
完善代碼,讀取更多職位列表頁面
我們來把代碼整合一下,剛才我們只讀取過一頁15個職位基本信息,或者單個職位的詳細(xì)信息,我們把它整合到一起,最終代碼如下(注意替換headers并去除content-length):
# coding: utf-8
# ## 拉鉤搜索‘人工智能’職位列表,包含二級頁面詳情
# ### 設(shè)置
# 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]:
#轉(zhuǎn)換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', ': ')
# ### 讀取單個職位詳情的函數(shù)
# 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
# ### 發(fā)起請求
# 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!')
代碼的幾個說明:
- 頂部
params的pn恢復(fù)為1,之前我們一直使用的是2... - 將存儲文件的目錄放到開始
savePath設(shè)置,方便以后修改 -
def readJobDetails...必須要放在for n in range...前面,先def然后才能使用。 - 因為過程超過400秒也就是有八九分鐘,所以用
print('>Got job:', pid)只是讓過程能夠有點反應(yīng),看上去不像死機。 - 每讀取一頁列表,
time.sleep(1)休息1秒,每讀取一個詳情頁面也要time.sleep(1)休息一秒。這樣比較低調(diào)不容易被封禁。
運行整個代碼,可以在./data/lagou_ai/jobs下生成450個xxxxxxx.json文件,你可以在資源管理器(win)或者訪達(dá)(mac)中查看文件一個個變多以確保程序順路進(jìn)行中。
最后別忘了測試一下存儲的數(shù)據(jù)是否正確:
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'])
總結(jié)
- 先分析頁面,知道數(shù)據(jù)從哪里來、什么格式?(網(wǎng)頁源代碼html?Network面板的xhr請求到的json數(shù)據(jù)?)
- 然后找到對應(yīng)的headers和params(如果有的話,詳情頁就沒有)
- 根據(jù)數(shù)據(jù)來源格式確定使用BeautifulSoup還是json模塊來解析,具體解析方法要多看教程多查資料多學(xué)習(xí)
- 發(fā)起Request請求get數(shù)據(jù),然后解析到我們需要的內(nèi)容
- 利用
def定義不同的函數(shù)處理單獨的請求可以讓代碼更清晰 - 把獲取的內(nèi)容存儲到文件(簡單橫豎列表數(shù)據(jù)存.csv,復(fù)雜結(jié)構(gòu)存.json),注意要轉(zhuǎn)為字符串存儲,注意文件名要唯一。
- 盡可能在必要的位置合理的使用
time.sleep(1)延緩一下 - 代碼要一點點測試,如果要
for n in range(1,30)那就先for n in range(1,2)試一下能不能成功。
智能決策上手系列教程索引
每個人的智能決策新時代
如果您發(fā)現(xiàn)文章錯誤,請不吝留言指正;
如果您覺得有用,請點喜歡;
如果您覺得很有用,歡迎轉(zhuǎn)載~
END