前程無憂爬蟲原始碼及分析(一)
一、網頁分析
1.1 關鍵字頁面(url入口)
首先在前程無憂網站上檢索關鍵詞"大資料":
跳轉到如下url: https://search.51job.com/list/000000,000000,0000,00,9,99,%25E5%25A4%25A7%25E6%2595%25B0%25E6%258D%25AE,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=
在這個url中,' %25E5%25A4%25A7%25E6%2595%25B0%25E6%258D%25AE '很明顯為某種編碼格式,我們知道'大資料'的UTF-8為' E5A4A7 E695B0 E68DAE ',加上'%'變為16進位制編碼,即 ' %E5%A4%A7%E6%95%B0%E6%8D%AE ' ,這'25'又是什麼鬼?百度得到到關鍵詞'二次編碼',%25的url編碼即為符號'%',所以' %25E5 '即為' %E5 '。不過我好奇的是,既然'%'已經是URI中的特殊轉義字元,為啥還要多此一舉地進行二次編碼呢。再查,發現'%'在URI中可能會引起程式解析的歧義,百分號本身就用作對不安全字元進行編碼時使用的特殊字元,因此本身需要編碼 。
做一個實驗,我們將該url擷取下來,用'銷售'中文的utf-8編碼代替其中的編碼,看看發生了什麼。
再回過頭去看在網站入口搜尋框進去的'大資料'頁面:
這是二次編碼過的,再在url裡改成中文名:
好吧,二次編碼與否並不影響效果。
接下來來比較不同頁數URL之間的聯絡。提取'大資料'關鍵詞前三頁url:
將前兩頁進行對比:
只有一個符號不同,再比較二、三頁:
也是一個數字的差異,改數字即為頁碼。所以對於頁數url我們可以得出如下結論:
' https://search.51job.com/list/000000,000000,0000,00,9,99 ,關鍵詞 ,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='
因此入口url模組程式碼如下:
-
if __name__ == '__main__' :
-
key = ' 銷售 '
-
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(1,50)]
-
for url in urls:
-
getUrl(url)
1.2 崗位詳情url
由於我們需要的是崗位詳情頁面的資訊,所以我們要找出頁面所有崗位的url。
開啟開發者工具,找到崗位名稱所在標籤,在屬性裡發現了該頁面的url:
又發現每一條招聘資訊都在<div class="el">…</div>裡:
所以通過如下xpath將url提取出來:
-
//*[@id= "resultList" ]/div/p/span/a/@href
1.3 崗位資訊提取
進入某一崗位具體資訊url,開啟開發者選項,在資訊所在標籤上右擊,提取所需資訊的xpath。
-
title = selector.xpath( '/html/body/div[3]/div[2]/div[2]/div/div[1]/h1/text()' )
-
salary = selector.xpath( '/html/body/div[3]/div[2]/div[2]/div/div[1]/strong/text()' )
-
company = selector.xpath( '/html/body/div[3]/div[2]/div[2]/div/div[1]/p[1]/a[1]/text()' )
-
place = selector.xpath( '/html/body/div[3]/div[2]/div[2]/div/div[1]/p[2]/text()[1]' )
-
exp = selector.xpath( '/html/body/div[3]/div[2]/div[2]/div/div[1]/p[2]/text()[2]' )
-
edu = selector.xpath( '/html/body/div[3]/div[2]/div[2]/div/div[1]/p[2]/text()[3]' )
-
num = selector.xpath( '/html/body/div[3]/div[2]/div[2]/div/div[1]/p[2]/text()[4]' )
-
time = selector.xpath( '/html/body/div[3]/div[2]/div[2]/div/div[1]/p[2]/text()[5]' )
-
comment = selector.xpath( '/html/body/div[3]/div[2]/div[3]/div[1]/div/p/text()' )
-
url = res.url
跑一遍看看,
咦,怎麼崗位要求這麼多null?點一個進去看看
嗯,<div>標籤下咋又出現了<div>,真是亂來(笑)。看來得換個法子了,把這個父<div>標籤下的中文全部帶走。這兒需要用到xpath的steing()函式。將上述方法改造,得到新的xpath:
-
string(/html/body/div[3]/div[2]/div[3]/div[1]/div)
又能跑起來了,不過把該<div>裡一些其他資訊也帶進來了,比如崗位分類等。
二、一些細節
2.1 編碼問題
雖然沒有系統的學過編碼,但是在與不同網頁、不同的作業系統打交道的過程中也略知一二了。與前些天爬過的智聯招聘不同,前程無憂網頁用的是GBK編碼,所以需要注意編碼格式。而且還有一個小問題:
對,報錯了,'\ufffd'無法被轉碼。從網上找來的解釋稱:
在通過 GBK 從字串獲取位元組陣列時,由於一個 Unicode 轉換成兩個 byte ,如果此時用 ISO-8859-1 或用 UTF-8 構造字串就會出現兩個問號。
若是通過 ISO-8859-1 構造可以再通過上面所說的錯上加錯恢復(即再通過從 ISO-8859-1 解析,用 GBK 構造);
若是通過 UTF-8 構造則會產生 Unicode 字元" \uFFFD ",不能恢復,若再通過 String - UTF-8 〉 ByteArray - GBK 〉 String ,則會出現雜碼,如 a 錕斤拷錕斤拷
而在Unicode中,\uFFFD為佔 位符, 當從某語言向Unicode轉化時,如果在某語言中沒有該字元,得到的將是Unicode的程式碼"\uffffd"。
針對這種情況,在開啟檔案時可設定一個引數—— errors :設定不同錯誤的處理方案, 預設為 'strict',意為編碼錯誤引起一個UnicodeError。所以我們需要為該引數換一個值:ignore,當遇到編碼問題市直接無視。
-
fp = open( '51job.csv' , 'wt' ,newline= '' ,encoding= 'GBK' ,errors= 'ignore' )
-
writer = csv.writer(fp)
-
'' '''title,salary,company,place,exp,edu,num,time,comment,url'''
-
writer.writerow(( ' 職位 ' , ' 薪水 ' , ' 公司 ' , ' 地區 ' , ' 經驗 ' , ' 學歷 ' , ' 數量 ' , ' 時間 ' , ' 要求 ' , 'url' ))
2.2 網頁結構問題
電腦端的結構太多了…資料抓取率也低,程式碼還得改改,還是移動適配又好看又好爬。
三、原始碼
因為在寫這篇部落格的時候,發現了一些之前沒發現的問題,並且有了優化的想法,所以就不把程式碼貼過來了,就留github 吧,這些天再把這個專案改進一下。
原始碼地址: ofollow,noindex" target="_blank">51job原始碼地址
相關:智聯招聘原始碼講解