一文看懂網(wǎng)絡爬蟲
從基礎理論入手,詳細講解了爬蟲內(nèi)容,分為六個部分:我們的目的是什么;內(nèi)容從何而來;了解網(wǎng)絡請求;一些常見的限制方式;嘗試解決問題的思路;效率問題的取舍。
一、我們的目的是什么
一般來講對我們而言,需要抓取的是某個網(wǎng)站或者某個應用的內(nèi)容,提取有用的價值,內(nèi)容一般分為兩部分,非結構化的文本,或結構化的文本。
1. 關于非結構化的數(shù)據(jù)
1.1 HTML文本(包含JavaScript代碼)
HTML文本基本上是傳統(tǒng)爬蟲過程中最常見的,也就是大多數(shù)時候會遇到的情況,例如抓取一個網(wǎng)頁,得到的是HTML,然后需要解析一些常見的元素,提取一些關鍵的信息。HTML其實理應屬于結構化的文本組織,但是又因為一般我們需要的關鍵信息并非直接可以得到,需要進行對HTML的解析查找,甚至一些字符串操作才能得到,所以還是歸類于非結構化的數(shù)據(jù)處理中。
常見解析方式如下:
CSS選擇器
現(xiàn)在的網(wǎng)頁樣式比較多,所以一般的網(wǎng)頁都會有一些CSS的定位,例如class,id等等,或者我們根據(jù)常見的節(jié)點路徑進行定位,例如騰訊首頁的財經(jīng)部分。

這里id就為finance,我們用css選擇器,就是”#finance”就得到了財經(jīng)這一塊區(qū)域的html,同理,可以根據(jù)特定的css選擇器可以獲取其他的內(nèi)容。
XPATH
XPATH是一種頁面元素的路徑選擇方法,利用Chrome可以快速得到,如:

copy XPATH 就能得到——//*[@id=”finance”]
正則表達式
正則表達式,用標準正則解析,一般會把HTML當做普通文本,用指定格式匹配當相關文本,適合小片段文本,或者某一串字符,或者HTML包含javascript的代碼,無法用CSS選擇器或者XPATH。
字符串分隔
同正則表達式,更為偷懶的方法,不建議使用。
1.2 一段文本
例如一篇文章,或者一句話,我們的初衷是提取有效信息,所以如果是滯后處理,可以直接存儲,如果是需要實時提取有用信息,常見的處理方式如下:
分詞
根據(jù)抓取的網(wǎng)站類型,使用不同詞庫,進行基本的分詞,然后變成詞頻統(tǒng)計,類似于向量的表示,詞為方向,詞頻為長度。
NLP
自然語言處理,進行語義分析,用結果表示,例如正負面等。
2. 關于結構化的數(shù)據(jù)
結構化的數(shù)據(jù)是***處理,一般都是類似JSON格式的字符串,直接解析JSON數(shù)據(jù)就可以了,提取JSON的關鍵字段即可。
二、內(nèi)容從何而來
過去我們常需要獲取的內(nèi)容主要來源于網(wǎng)頁,一般來講,我們決定進行抓取的時候,都是網(wǎng)頁上可看到的內(nèi)容,但是隨著這幾年移動互聯(lián)網(wǎng)的發(fā)展,我們也發(fā)現(xiàn)越來越多的內(nèi)容會來源于移動App,所以爬蟲就不止局限于一定要抓取解析網(wǎng)頁,還有就是模擬移動app的網(wǎng)絡請求進行抓取,所以這一部分我會分兩部分進行說明。
1 網(wǎng)頁內(nèi)容
網(wǎng)頁內(nèi)容一般就是指我們最終在網(wǎng)頁上看到的內(nèi)容,但是這個過程其實并不是網(wǎng)頁的代碼里面直接包含內(nèi)容這么簡單,所以對于很多新人而言,會遇到很多問題,比如:
明明在頁面用Chrome或者Firefox進行審查元素時能看到某個HTML標簽下包含內(nèi)容,但是抓取的時候為空。
很多內(nèi)容一定要在頁面上點擊某個按鈕或者進行某個交互操作才能顯示出來。
所以對于很多新人的做法是用某個語言別人模擬瀏覽器操作的庫,其實就是調(diào)用本地瀏覽器或者是包含了一些執(zhí)行JavaScript的引擎來進行模擬操作抓取數(shù)據(jù),但是這種做法顯然對于想要大量抓取數(shù)據(jù)的情況下是效率非常低下,并且對于技術人員本身而言也相當于在用一個盒子,那么對于這些內(nèi)容到底是怎么顯示在網(wǎng)頁上的呢?主要分為以下幾種情況:
網(wǎng)頁包含內(nèi)容
這種情況是最容易解決的,一般來講基本上是靜態(tài)網(wǎng)頁已經(jīng)寫死的內(nèi)容,或者動態(tài)網(wǎng)頁,采用模板渲染,瀏覽器獲取到HTML的時候已經(jīng)是包含所有的關鍵信息,所以直接在網(wǎng)頁上看到的內(nèi)容都可以通過特定的HTML標簽得到。
JavaScript代碼加載內(nèi)容
這種情況是由于雖然網(wǎng)頁顯示時,內(nèi)容在HTML標簽里面,但是其實是由于執(zhí)行js代碼加到標簽里面的,所以這個時候內(nèi)容在js代碼里面的,而js的執(zhí)行是在瀏覽器端的操作,所以用程序去請求網(wǎng)頁地址的時候,得到的response是網(wǎng)頁代碼和js的代碼,所以自己在瀏覽器端能看到內(nèi)容,解析時由于js未執(zhí)行,肯定找到指定HTML標簽下內(nèi)容肯定為空,這個時候的處理辦法,一般來講主要是要找到包含內(nèi)容的js代碼串,然后通過正則表達式獲得相應的內(nèi)容,而不是解析HTML標簽。
Ajax異步請求
這種情況是現(xiàn)在很常見的,尤其是在內(nèi)容以分頁形式顯示在網(wǎng)頁上,并且頁面無刷新,或者是對網(wǎng)頁進行某個交互操作后,得到內(nèi)容。那我們該如何分析這些請求呢?這里我以Chrome的操作為例,進行說明:

所以當我們開始刷新頁面的時候就要開始跟蹤所有的請求,觀察數(shù)據(jù)到底是在哪一步加載進來的。然后當我們找到核心的異步請求的時候,就只用抓取這個異步請求就可以了,如果原始網(wǎng)頁沒有任何有用信息,也沒必要去抓取原始網(wǎng)頁了。
2 App內(nèi)容
因為現(xiàn)在移動應用越來越多,很多有用信息都在App里面,另外解析非結構化文本和結構文本對比而言,結構化文本會簡單多了,不同去找內(nèi)容,去過多分析解析,所有既有網(wǎng)站又有App的話,推薦抓取App,大多數(shù)情況下基本上只是一些JSON數(shù)據(jù)的API了。
那么App的數(shù)據(jù)該如何抓取呢?通用的方法就是抓包,基本的做法就是電腦安裝抓包軟件,配置好端口,然后記下ip,手機端和電腦在同一個局域網(wǎng)里面,然后在手機的網(wǎng)絡連接里面設置好代理,這個時候打開App進行一些操作,如果有網(wǎng)絡數(shù)據(jù)請求,則都會被抓包軟件記下,就如上Chrome分析網(wǎng)絡請求一樣,你可以看到所有的請求情況,可以模擬請求操作。這里Mac上我推薦軟件Charles,Windows推薦Fiddler2。
具體如何使用,之后我再做詳述,可能會涉及到HTTPS證書的問題。
三、了解網(wǎng)絡請求
剛剛一直在寬泛的提到一些我們需要找到請求,進行請求,對于請求只是一筆帶過,但請求是很重要的一部分,包括如何繞過限制,如何發(fā)送正確地數(shù)據(jù),都需要對的請求,這里就要詳細的展開說下請求,以及如何模擬請求。
我們常說爬蟲其實就是一堆的HTTP請求,找到待爬取的鏈接,不管是網(wǎng)頁鏈接還是App抓包得到的API鏈接,然后發(fā)送一個請求包,得到一個返回包(也有HTTP長連接,或者Streaming的情況,這里不考慮),所以核心的幾個要素就是:
URL
請求方法(POST, GET)
請求包headers
請求包內(nèi)容
返回包headers
在用Chrome進行網(wǎng)絡請求捕獲或者用抓包工具分析請求時,最重要的是弄清楚URL,請求方法,然后headers里面的字段,大多數(shù)出問題就出在headers里面,最常限制的幾個字段就是User-Agent, Referer, Cookie 另外Base Auth也是在headers里面加了Autheration的字段。
請求內(nèi)容也就是post時需要發(fā)送的數(shù)據(jù),一般都是將Key-Value進行urlencode。返回包headers大多數(shù)會被人忽視,可能只得到內(nèi)容就可以了,但是其實很多時候,很多人會發(fā)現(xiàn)明明url,請求方法還有請求包的內(nèi)容都對了,為什么沒有返回內(nèi)容,或者發(fā)現(xiàn)請求被限制,其實這里大概有兩個原因:
一個是返回包的內(nèi)容是空的,但是在返回包的headers的字段里面有個Location,這個Location字段就是告訴瀏覽器重定向,所以有時候代碼沒有自動跟蹤,自然就沒有內(nèi)容了;
另外一個就是很多人會頭疼的Cookie問題,簡單說就是瀏覽器為什么知道你的請求合法的,例如已登錄等等,其實就是可能你之前某個請求的返回包的headers里面有個字段叫Set-Cookie,Cookie存在本地,一旦設置后,除非過期,一般都會自動加在請求字段上,所以Set-Cookie里面的內(nèi)容就會告訴瀏覽器存多久,存的是什么內(nèi)容,在哪個路徑下有用,Cookie都是在指定域下,一般都不跨域,域就是你請求的鏈接host。
所以分析請求時,一定要注意前四個,在模擬時保持一致,同時觀察第五個返回時是不是有限制或者有重定向。
四、一些常見的限制方式
上述都是講的都是一些的基礎的知識,現(xiàn)在我就列一些比較常見的限制方式,如何突破這些限制抓取數(shù)據(jù)。
Basic Auth
一般會有用戶授權的限制,會在headers的Autheration字段里要求加入;
Referer
通常是在訪問鏈接時,必須要帶上Referer字段,服務器會進行驗證,例如抓取京東的評論;
User-Agent
會要求真是的設備,如果不加會用編程語言包里自有User-Agent,可以被辨別出來;
Cookie
一般在用戶登錄或者某些操作后,服務端會在返回包中包含Cookie信息要求瀏覽器設置Cookie,沒有Cookie會很容易被辨別出來是偽造請求;
也有本地通過JS,根據(jù)服務端返回的某個信息進行處理生成的加密信息,設置在Cookie里面;
Gzip
請求headers里面帶了gzip,返回有時候會是gzip壓縮,需要解壓;
JavaScript加密操作
一般都是在請求的數(shù)據(jù)包內(nèi)容里面會包含一些被javascript進行加密限制的信息,例如新浪微博會進行SHA1和RSA加密,之前是兩次SHA1加密,然后發(fā)送的密碼和用戶名都會被加密;
其他字段
因為http的headers可以自定義地段,所以第三方可能會加入了一些自定義的字段名稱或者字段值,這也是需要注意的。
真實的請求過程中,其實不止上面某一種限制,可能是幾種限制組合在一次,比如如果是類似RSA加密的話,可能先請求服務器得到Cookie,然后再帶著Cookie去請求服務器拿到公鑰,然后再用js進行加密,再發(fā)送數(shù)據(jù)到服務器。所以弄清楚這其中的原理,并且耐心分析很重要。
五、嘗試解決問題的思路
首先大的地方,加入我們想抓取某個數(shù)據(jù)源,我們要知道大概有哪些路徑可以獲取到數(shù)據(jù)源,基本上無外乎三種:
PC端網(wǎng)站;
針對移動設備響應式設計的網(wǎng)站(也就是很多人說的H5, 雖然不一定是H5);
移動App;
原則是能抓移動App的,***抓移動App,如果有針對移動設備優(yōu)化的網(wǎng)站,就抓針對移動設備優(yōu)化的網(wǎng)站,***考慮PC網(wǎng)站。因為移動App基本都是API很簡單,而移動設備訪問優(yōu)化的網(wǎng)站一般來講都是結構簡單清晰的HTML,而PC網(wǎng)站自然是最復雜的了;
針對PC端網(wǎng)站和移動網(wǎng)站的做法一樣,分析思路可以一起講,移動App單獨分析。
1 網(wǎng)站類型的分析
首先是網(wǎng)站類的,使用的工具就是Chrome,建議用Chrome的隱身模式,分析時不用頻繁清楚cookie,直接關閉窗口就可以了。
具體操作步驟如下:
輸入網(wǎng)址后,先不要回車確認,右鍵選擇審查元素,然后點擊網(wǎng)絡,記得要勾上preserve log選項,因為如果出現(xiàn)上面提到過的重定向跳轉(zhuǎn),之前的請求全部都會被清掉,影響分析,尤其是重定向時還加上了Cookie;
接下來觀察網(wǎng)絡請求列表,資源文件,例如css,圖片基本都可以忽略,***個請求肯定就是該鏈接的內(nèi)容本身,所以查看源碼,確認頁面上需要抓取的內(nèi)容是不是在HTML標簽里面,很簡單的方法,找到自己要找的內(nèi)容,看到父節(jié)點,然后再看源代碼里面該父節(jié)點里面有沒有內(nèi)容,如果沒有,那么一定是異步請求,如果是非異步請求,直接抓該鏈接就可以了。
分析異步請求,按照網(wǎng)絡列表,略過資源文件,然后點擊各個請求,觀察是否在返回時包含想要的內(nèi)容,有幾個方法:
內(nèi)容比較有特點,例如人的屬性信息,物品的價格,或者微博列表等內(nèi)容,直接觀察可以判斷是不是該異步請求;
知道異步加載的內(nèi)容節(jié)點或者父節(jié)點的class或者id的名稱,找到js代碼,閱讀代碼得到異步請求;
確認異步請求之后,就是要分析異步請求了,簡單的,直接請求異步請求,能得到數(shù)據(jù),但是有時候異步請求會有限制,所以現(xiàn)在分析限制從何而來。
針對分析對請求的限制,思路是逆序方法。
先找到***一個得到內(nèi)容的請求,然后觀察headers,先看post數(shù)據(jù)或者url的某個參數(shù)是不是都是已知數(shù)據(jù),或者有意義數(shù)據(jù),如果發(fā)現(xiàn)不確定的先帶上,只是更改某個關鍵字段,例如page,count看結果是不是會正常,如果不正常,比如多了個token,或者某個字段明顯被加密,例如用戶名密碼,那么接下來就要看JS的代碼,看到底是哪個函數(shù)進行了加密,一般會是原生JS代碼加密,那么看到代碼,直接加密就行,如果是類似RSA加密,那么就要看公鑰是從何而來,如果是請求得到的,那么就要往上分析請求,另外如果是發(fā)現(xiàn)請求headers里面有陌生字段,或者有Cookie也要往上看請求,Cookie在哪一步設置的;
接下來找到剛剛那個請求未知來源的信息,例如Cookie或者某個加密需要的公鑰等等,看看上面某個請求是不是已經(jīng)包含,依次類推。
2 App的分析
然后是App類的,使用的工具是Charles,手機和電腦在一個局域網(wǎng)內(nèi),先用Charles配置好端口,然后手機設置代理,ip為電腦的ip,端口為設置的端口,然后如果手機上請求網(wǎng)絡內(nèi)容時,Charles會顯示相應地請求,那么就ok了,分析的大體邏輯基本一致,限制會相對少很多,但是也有幾種情況需要注意:
加密,App有時候也有一些加密的字段,這個時候,一般來講都會進行反編譯進行分析,找到對應的代碼片段,逆推出加密方法;
gzip壓縮或者base64編碼,base64編碼的辨別度較高,有時候數(shù)據(jù)被gzip壓縮了,不過Charles都是有自動解密的;
https證書,有的https請求會驗證證書,Charles提供了證書,可以在官網(wǎng)找到,手機訪問,然后信任添加就可以。
六、效率問題的取舍
一般來講在抓取大量數(shù)據(jù),例如全網(wǎng)抓取京東的評論,微博所有人的信息,微博信息,關注關系等等,這種上十億到百億次設置千億次的請求必須考慮效率,否者一天只有86400秒,那么一秒鐘要抓100次,一天也才864w次請求,也需要100多天才能到達十億級別的請求量。
涉及到大規(guī)模的抓取,一定要有良好的爬蟲設計,一般很多開源的爬蟲框架也都是有限制的,因為中間涉及到很多其他的問題,例如數(shù)據(jù)結構,重復抓取過濾的問題,當然最重要的是要把帶寬利用滿,所以分布式抓取很重要,接下來我會有一篇專門講分布式的爬蟲設計,分布式最重要的就是中間消息通信,如果想要抓的越多越快,那么對中間的消息系統(tǒng)的吞吐量要求也越高。
但是對于一些不太大規(guī)模的抓取就沒要用分布式的一套,比較消耗時間,基本只要保證單機器的帶寬能夠利用滿就沒問題,所以做好并發(fā)就可以,另外對于數(shù)據(jù)結構也要有一定的控制,很多人寫程序,內(nèi)存越寫越大,抓取越來越慢,可能存在的原因就包括,一個是用了內(nèi)存存一些數(shù)據(jù)沒有進行釋放,第二個可能有一些hashset的判斷,***判斷的效率越來越低,比如用bloomfilter替換就會優(yōu)化很多。