爬蟲實戰(二) 51job移動端資料採集
在上一篇 51job職位資訊的爬取 中,對崗位資訊div下各式各樣雜亂的標籤,簡單的Xpath效果不佳,加上string()函式後,也不盡如人意。因此這次我們跳過桌面web端,選擇移動端進行爬取。
一、程式碼結構
按照下圖所示的爬蟲基本框架結構,我將此份程式碼分為四個模組——URL管理、HTML下載、HTML解析以及資料儲存。
二、URL管理模組
這個模組負責搜尋框關鍵詞與對應頁面URL的生成,以及搜尋結果不同頁數的管理。首先觀察某欄位(大資料, UTF-8為' E5A4A7 E695B0 E68DAE ') 全國範圍內的結果,前三頁結果的URL如下:
URL前半部分:
這部分中我們可以看到兩處處不同,第一處為編碼後'2,?.html'中間的數字,這是頁數。另一處為引數stype的值,除第一頁為空之外,其餘都為1。另外,URL中有一連串的數字,這些是搜尋條件,如地區、行業等,在這兒我沒有用上。後面的一連串字元則為搜尋關鍵詞的字元編碼。值得注意的是,有些符號在URL中是不能直接傳輸的,如果需要傳輸的話,就需要對它們進行編碼。編碼的格式為'%'加上該字元的ASCII碼。因此在該URL中,%25即為符號'%'。
URL後半部分:
後半部分很明顯的就能出首頁與後面頁面的URL引數相差很大,非首頁的URL後半部分相同。
因此我們需要對某關鍵字的搜尋結果頁面分兩次處理,第一次處理首頁,第二次可使用迴圈處理後續的頁面。
-
if __name__ == '__main__' :
-
key = ' 資料開發 '
-
# 第一頁
-
url = 'https://search.51job.com/list/000000,000000,0000,00,9,99,' +key+ ',2,1.html?lang=c&stype=&postchannel=0000&workyear=99&cotype=99°reefrom=99&jobterm=99&companysize=99&providesalary=99&lonlat=0%2C0&radius=-1&ord_field=0&confirmdate=9&fromType=&dibiaoid=0&address=&line=&specialarea=00&from=&welfare='
-
getUrl(url)
-
# 後頁 [2,100)
-
urls = [ 'https://search.51job.com/list/000000,000000,0000,00,9,99,' +key+ ',2,{}.html?lang=c&stype=1&postchannel=0000&workyear=99&cotype=99°reefrom=99&jobterm=99&companysize=99&lonlat=0%2C0&radius=-1&ord_field=0&confirmdate=9&fromType=&dibiaoid=0&address=&line=&specialarea=00&from=&welfare=' .format(i) for i in range(2,30)]
-
for url in urls:
-
getUrl(url)
三、HTML下載模組
下載HTMl頁面分為兩個部分,其一為下載搜尋結果某一頁的HTML頁面,另一部分為下載某一崗位具體頁面。由於頁面中具體崗位URL需要從搜尋結果頁面中獲取,所以將下載搜尋結果頁面及獲取具體崗位URL放入一個函式中,在提取到具體崗位URL後,將其傳入至另一函式中。
3.1搜尋結果頁面下載與解析
下載頁面使用的是requests庫的get()方法,得到頁面文字後,通過lxml庫的etree將其解析為樹狀結構,再通過Xpath提取我們想要的資訊。在搜尋結果頁面中,我們需要的是具體崗位的URL,開啟開發者選項,找到崗位名稱。
我們需要的是<a>標籤裡的href屬性。右鍵,複製——Xpath,得到該屬性的路徑。
-
//*[@id= "resultList" ]/div/p/span/a/@href
由於xpath返回值為一個列表,所以通過一個迴圈,將列表內URL依次傳入下一函式。
-
def getUrl(url):
-
print ( 'New page' )
-
res = requests.get(url)
-
res.encoding = 'GBK'
-
if res.status_code == requests.codes.ok:
-
selector = etree.HTML(res.text)
-
urls = selector.xpath( '//*[@id="resultList"]/div/p/span/a/@href' )
-
# //*[@id="resultList"]/div/p/span/a
-
for url in urls:
-
parseInfo(url)
-
time.sleep(random.randrange(1, 4))
3.2具體崗位資訊頁面下載
該函式接收一個具體崗位資訊的引數。由於我們需要對移動端網頁進行處理,所以在傳送請求時需要進行一定的偽裝。通過設定headers,使用手機瀏覽器的使用者代理,再呼叫get()方法。
-
def parseInfo(url):
-
headers = {
-
'User-Agent' : 'Opera/9.80 (Android 2.3.4; Linux; Opera Mobi/ADR-1301071546) Presto/2.11.355 Version/12.10'
-
}
-
res = requests.get(url, headers=headers)
四、HTML解析模組
在3.2中,我們已經得到了崗位資訊的移動端網頁原始碼,因此再將其轉為etree樹結構,呼叫Xpath即可得到我們想要的資訊。
需要注意的是頁面裡崗位職責div裡,所有相關資訊都在一個<article>標籤下,而不同頁面的<article>下層標籤並不相同,所以需要將該標籤下所有文字都取出,此處用上了string()函式。
-
selector = etree.HTML(res.text)
-
title = selector.xpath( '//*[@id="pageContent"]/div[1]/div[1]/p/text()' )
-
salary = selector.xpath( '//*[@id="pageContent"]/div[1]/p/text()' )
-
company = selector.xpath( '//*[@id="pageContent"]/div[2]/a[1]/p/text()' )
-
companyinfo = selector.xpath( '//*[@id="pageContent"]/div[2]/a[1]/div/text()' )
-
companyplace = selector.xpath( '//*[@id="pageContent"]/div[2]/a[2]/span/text()' )
-
place = selector.xpath( '//*[@id="pageContent"]/div[1]/div[1]/em/text()' )
-
exp = selector.xpath( '//*[@id="pageContent"]/div[1]/div[2]/span[2]/text()' )
-
edu = selector.xpath( '//*[@id="pageContent"]/div[1]/div[2]/span[3]/text()' )
-
num = selector.xpath( '//*[@id="pageContent"]/div[1]/div[2]/span[1]/text()' )
-
time = selector.xpath( '//*[@id="pageContent"]/div[1]/div[1]/span/text()' )
-
info = selector.xpath( 'string(//*[@id="pageContent"]/div[3]/div[2]/article)' )
-
info = str(info).strip()
五、資料儲存模組
首先建立.csv檔案,將不同列名稱寫入首行。
-
fp = open( '51job.csv' , 'wt' ,newline= '' ,encoding= 'GBK' ,errors= 'ignore' )
-
writer = csv.writer(fp)
-
writer.writerow(( ' 職位 ' , ' 薪資 ' , ' 公司 ' , ' 公司資訊 ' , ' 公司地址 ' , ' 地區 ' , ' 工作經驗 ' , ' 學歷 ' , ' 人數 ' , ' 時間 ' , ' 崗位資訊 ' ))
再在解析某一頁面資料後,將資料按行寫入.csv檔案。
-
writer.writerow((title,salary,company,companyinfo,companyplace,place,exp,edu,num,time,info))
原始碼: ofollow,noindex" target="_blank">爬取51job移動端原始碼(12月)
相關:智聯招聘原始碼分析