網路資料抓取-拉勾網職位列表和詳情-requests案例
ofollow,noindex">智慧決策上手系列教程索引
這次我們來比較完整的抓取拉勾網上面“人工智慧”相關招聘資訊以及招聘要求詳情。
分析頁面,尋找資料來源
開啟拉勾網,搜尋“人工智慧”得到下面這個頁面。
共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']
儲存資料,把每個職位資訊存為單獨檔案
因為資料很多,我們也說不準以後會用到哪個,所以我們把它整個 data
都儲存為一個檔案,以後只要讀取就可以了,避免因為少存了資料還要重新抓取的麻煩。
改進上面的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!')
程式碼的幾個說明:
- 頂部
params
的pn
恢復為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