嚴格意義來說,采集器和爬蟲不是一回事:采集器是對特定結構的數據來源進行解析、結構化,將所需的數據從中提取出來;而爬蟲的主要目標更多的是頁面里的鏈接和頁面的TITLE。
采集器也寫過不少了,隨便寫一點經驗吧,算是給自己的一個備忘。
首先是最簡單的:靜態頁面采集器。即所采集的數據來源頁面是靜態的,至少采集器所關心的那部分數據是靜態的,可以通過直接訪問頁面URL的方式獲取到包含目標數據的全部頁面代碼。這種采集器是最為常用,也是最為基礎的。目前已經有很多成熟的商業化的采集器產品,不過對我來說感覺用着有些過於復雜。一些我自己編寫采集器時會注意的問題在這些產品上似乎沒有,或者名字不是我擬的那個,找不到。用了幾次,干脆不如自己寫了,還更省時間效率更高。
准備知識:HTTP協議基礎、HTML語言基礎、正則表達式和任意支持正則表達式的編程工具(.net、java、php、Python、ruby等等都可以)
首先第一步,是下載目標頁面HTML。
這一步並沒有什么太難的,.net里有HttpWebRequest和HttpWebResponse等類專門處理,其他語言里也有類似的東西。不過需要注意的是,為采集器編寫下載器的時候,參數配置一定要靈活:User-Agent、Refer、Cookie等字段都得做成可配,還得支持使用代理服務器,這是為了突破目標服務器的訪問限制策略或機器人識別策略。有關常見的反機器人以及反“反機器人”等相關技術會在后續文章里專門編寫。
頁面代碼下載到本地之后,就得開始對其進行解析。有兩種解析方法
1、將其當做HTML解析
熟悉HTML的人可以將下載到的HTML頁面直接當做HTML來解析,這樣做也是最快和最有效率的。遍歷HTML元素和屬性之后,直接找到關注部分數據內容,通過訪問其元素、元素屬性和子元素的方式獲取數據。.net里原生沒有HTML解析庫,可以找第三方庫,大多數比較好用,至少當一般拿來解析個頁面提個數據之類的夠用了。唯一需要注意的是需要考慮頁面代碼下載不完全或者目標頁面結構存在錯誤的情況。
2、將其當做字符串,用正則表達式解析
正則表達式的好處就是靈活,在方法1失效或者實現麻煩(比如目標數據的HTML元素路徑可能不固定)的時候可以考慮。使用正則表達式的思路就是尋找目標數據及其上下文的特征或者特征串,然后編寫正則表達式將其匹配提取出來即可。
以下以解析bing的搜索結果頁為例,介紹靜態采集器工作的基本原理。
首先是頁面獲取。點擊兩下就能發現頁面參數的規律,舉例:
http://cn.bing.com/search?q=MOLLE+II&first=31
這個URL代表以“MOLLE”“II”兩個關鍵詞搜索,當前頁面是是第四頁。FIRST參數指的是本頁第一個顯示的搜索結果的索引號,第四頁顯示31-40個搜索結果。
這是用GET方式傳遞參數,大多數情況下都是這樣的。如果目標頁面用POST方式傳遞參數,用瀏覽器的開發者模式抓個包看看參數是啥就OK了。
然后我們就下載到了目標頁面,將其在正則表達式測試器里打開:
恩,這活兒干得多了干脆自己寫了一個趁手的工具。
我們的目標是提取到搜索結果里的鏈接文字和鏈接URL。對於需要從同一個頁面解析得到兩項或者多項相互對應的數量一樣的數據,也有兩種策略:根據這些數據不同的特征直接編寫表達式從頁面里提取目標數據(比如先用一個正則處理頁面,拿到所有的鏈接標題文字,再用一個正則處理頁面,拿到所有的鏈接URL),或者分析頁面結構,找到包含目標數據項的最小頁面結構(比如html表格里的表格行<tr>元素),再進行解析。其中后者更靠譜一點,也可以省去很多干擾,但稍微麻煩一些。以下以后一種方式進行介紹。
用瀏覽器的檢查工具(Chrome里以前叫查看元素,新版改叫檢查了,剛剛還找了半天)分析頁面代碼,我們可以發現所有的搜索內容都包含在一個id屬性為"b_results"的<ol>標簽里。編寫表達式對其進行提取:
對於解析HTML用到的正則,零寬斷言和逆序環視(查找)是經常用到的,用於提取帶有特定前綴和后綴的字符串。有關正則表達式的技術博客園已經有很多相關文章,這里不再贅述。
不過需要注意的是,對於.net的正則表達式庫,有一些開關需要注意。對於解析html的時候,經常需要將SingleLine參數選中,這樣引擎會把字符串里所有的回車當成普通字符,而不是當成一行數據的結尾。不過這也並非絕對,也需要根據實際情況進行靈活配置。
另外還有一個小技巧。在移動端盛行的今天,一些網站會根據用戶瀏覽器請求里的USER-AGENT提供不同的頁面,對手機端發起的請求就會提供手機版的頁面,出於節省客戶流量的考慮,一般手機版的頁面會比PC端的干凈一些,頁面噪音更少。
繼續回到頁面解析,剛剛已經找到包含所有目標元素的頁面結構,實際上如果發現目標數據的最小結構的特征在頁面里也是唯一的,直接提取也無妨:
這樣我們就拿到了所有包含目標數據的<li>標簽的內容。順帶一提,因為截圖里工具使用的NOKIA手機的USER AGENT,所以我拿到的是手機版的頁面,和PC版略有不同,更干凈一點。
下一步我們對每個元素進行解析。由於所有li標簽的格式結構都是一樣的,我們可以用同一套正則解析。
我們的目標是鏈接標題和鏈接URL,說白了,就是<a>標簽的href屬性和標簽內容。
直接寫表達式就好:
然后對於每個li標簽的內容使用同樣的表達式進行處理就OK了。
好了,采集器的基本原理介紹完畢,我自己寫的這個正則工具可以我博客里找到,各位使用愉快,歡迎報BUG和功能建議。
下一篇將會介紹一下動態頁面數據獲取。