網(wǎng)絡(luò)數(shù)據(jù)抓取-拉勾網(wǎng)職位列表和詳情-requests案例

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

這次我們來比較完整的抓取拉勾網(wǎng)上面“人工智能”相關(guān)招聘信息以及招聘要求詳情。

分析頁面,尋找數(shù)據(jù)來源

打開拉勾網(wǎng),搜索“人工智能”得到下面這個頁面。
共30頁,每頁展示15個職位,應(yīng)該最多共計450個職位,但不知道為什么頁面上寫[職位(500+)]。

image.png

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

image.png

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


image.png

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

image.png

如上圖,職位列表數(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不知什么意思,但肯定就是我們的搜索詞。

image.png

按照以前我們的方法,在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)該輸出第二頁第一個職位的全部信息:


image.png

這樣我們就可以知道:

  • 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

image.png

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

image.png

我們需要使用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!')

代碼的幾個說明:

  • 頂部paramspn恢復(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,632評論 19 139
  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網(wǎng)絡(luò)請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明AI閱讀 16,211評論 3 119
  • 黑盒測試 黑盒測試也稱功能測試或數(shù)據(jù)驅(qū)動測試,它是在已知產(chǎn)品所應(yīng)具有的功能,通過測試來檢測每個功能是否都能正常使用...
    望夜的星空閱讀 706評論 0 2
  • 理查德·泰普勒, 歐美暢銷書作者,被譽為“個人成長”的導(dǎo)師。泰普勒人生軌跡豐富多彩,在其30年的工作生涯中,涉獵諸...
    馬蹄說閱讀 601評論 0 1

友情鏈接更多精彩內(nèi)容