前程無憂爬蟲原始碼及分析(一)
一、網頁分析
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 吧,這些天再把這個專案改進一下。
原始碼地址:51job原始碼地址
相關:智聯招聘原始碼講解