一. 需求javascript
萬維網上有着無數的網頁,包含着海量的信息,無孔不入、森羅萬象。但不少時候,不管出於數據分析或產品需求,咱們須要從某些網站,提取出咱們感興趣、有價值的內容,可是縱然是進化到21世紀的人類,依然只有兩隻手,一雙眼,不可能去每個網頁去點去看,而後再複製粘貼。因此咱們須要一種能自動獲取網頁內容並能夠按照指定規則提取相應內容的程序,這就是爬蟲。html
二. 原理java
傳統爬蟲從一個或若干初始網頁的URL開始,得到初始網頁上的URL,在抓取網頁的過程當中,不斷從當前頁面上抽取新的URL放入隊列,直到知足系統的必定中止條件。聚焦爬蟲的工做流程較爲複雜,須要根據必定的網頁分析算法過濾與主題無關的連接,保留有用的連接並將其放入等待抓取的URL隊列。而後,它將根據必定的搜索策略從隊列中選擇下一步要抓取的網頁URL,並重覆上述過程,直到達到系統的某一條件時中止。另外,全部被爬蟲抓取的網頁將會被系統存貯,進行必定的分析、過濾,並創建索引,以便以後的查詢和檢索;因此一個完整的爬蟲通常會包含以下三個模塊: node
1. 網絡請求模塊python
2. 爬取流程控制模塊jquery
3. 內容分析提取模塊c++
三. 網絡請求web
咱們常說爬蟲其實就是一堆的http(s)請求,找到待爬取的連接,而後發送一個請求包,獲得一個返回包,固然,也有HTTP長鏈接(keep-alive)或h5中基於stream的websocket協議,這裏暫不考慮,因此核心的幾個要素就是:ajax
1. url正則表達式
2. 請求header、body
3. 響應herder、內容
四. URL
爬蟲開始運行時須要一個初始url,而後會根據爬取到的html文章,解析裏面的連接,而後繼續爬取,這就像一棵多叉樹,從根節點開始,每走一步,就會產生新的節點。爲了使爬蟲可以結束,通常都會指定一個爬取深度(Depth)。
五. Http請求
http請求信息由請求方法(method)、請求頭(headers)、請求正文(body)三部分組成。因爲method通常是header中的第一行,也能夠說請求頭中包含請求方法,下面是chrome訪問請求頭的一部分:
1
2
3
4
5
|
GET / HTTP/1.1
Connection:Keep-Alive
Host:gsw.iguoxue.org
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36
Accept-Encoding:gzip, deflate, sdch, br
|
本文不會解釋各個字段的意思,詳細的解釋請移步w3c Http Header Field Definitions . 對於爬蟲須要注意的是請求方法是post時,須要將請求的參數先進行urlencode後再發送,後臺收到請求信息後可能會作一些校驗,這可能會影響到爬取,相關的header字段以下:
1. Basic Auth
這是一種古老的、不安全的用戶驗證方式,通常會有用戶受權的限制,會在headers的Autheration字段裏要求加入用戶名密碼(明文),若是驗證失敗則請求就會失敗,如今這種認證方式正在被淘汰。
2. Referer
連接的來源,一般在訪問連接時,都要帶上Referer字段,服務器會進行來源驗證,後臺一般會用此字段做爲防盜鏈的依據。
3. User-Agent
後臺一般會經過此字段判斷用戶設備類型、系統以及瀏覽器的型號版本。有些編程語言包裏網絡請求會自定義User-Agent,能夠被辨別出來,爬蟲中能夠設置爲瀏覽器的ua.
4. Cookie
通常在用戶登陸或者某些操做後,服務端會在返回包中包含Cookie信息要求瀏覽器設置Cookie,沒有Cookie會很容易被辨別出來是僞造請求;
也有本地經過JS,根據服務端返回的某個信息進行處理生成的加密信息,設置在Cookie裏面;
5. JavaScript加密操做
在進行敏感數據傳輸時,通常都會經過javascript進行加密,例如qq空間就會對用戶登錄密碼進行RSA加密後再發送給服務器,所以,爬蟲在模擬登錄時須要本身去請求公鑰,而後加密。
6. 自定義字段
由於http的headers能夠自定義地段,因此第三方可能會加入了一些自定義的字段名稱或者字段值,這也是須要注意的。
六. 流程控制
所謂爬取流程,就是按照什麼樣的規則順序去爬。在爬取任務不大的狀況下,爬取的流程控制不會太麻煩,不少爬取框架都已經幫你作了如scrapy,只須要本身實現解析的代碼。但在爬取一些大型網站時,例如全網抓取京東的評論,微博全部人的信息,關注關係等等,這種上十億到百億次設置千億次的請求必須考慮效率,不然一天只有86400秒,那麼一秒鐘要抓100次,一天也才8640w次請求,也須要100多天才能到達十億級別的請求量。涉及到大規模的抓取,必定要有良好的爬蟲設計,通常不少開源的爬蟲框架也都是有限制的,由於中間涉及到不少其餘的問題,例如數據結構,重複抓取過濾的問題,固然最重要的是要把帶寬利用滿,因此分佈式抓取很重要,這時流程控制就會很重要,分佈式最重要的就是多臺機器不一樣線程的調度和配合,一般會共享一個url隊列,而後各個線程經過消息通訊,若是想要抓的越多越快,那麼對中間的消息系統的吞吐量要求也越高。如今也有一些開源的分佈式爬取框架如scrapy-redis就是一個重寫了scrapy的調度模塊、隊列、管道的包,redis數據庫是用來在分佈式中作請求隊列共享,scrapyd是用來部署scrapy的,scrapyd-api用來啓動獲取數據。
七. 內容分析提取
請求headers的Accept-Encoding字段表示瀏覽器告訴服務器本身支持的壓縮算法(目前最多的是gzip),若是服務器開啓了壓縮,返回時會對響應體進行壓縮,爬蟲須要本身解壓;
過去咱們常須要獲取的內容主要來源於網頁html文檔自己,也就是說,咱們決定進行抓取的時候,都是html中包含的內容,可是隨着這幾年web技術飛速的發展,動態網頁愈來愈多,尤爲是移動端,大量的SPA應用,這些網站中大量的使用了ajax技術。咱們在瀏覽器中看到的網頁已不全是html文檔說包含的,不少都是經過javascript動態生成的,通常來講,咱們最終眼裏看到的網頁包括如下三種:
Html文檔自己包含內容
這種狀況是最容易解決的,通常來說基本上是靜態網頁已經寫死的內容,或者動態網頁,採用模板渲染,瀏覽器獲取到HTML的時候已是包含全部的關鍵信息,因此直接在網頁上看到的內容均可以經過特定的HTML標籤獲得。這種狀況解析也是很簡單的,通常的方法有一下幾種:
1. CSS選擇器
2. XPATH(這個值得學習一下)
3. 正則表達式或普通字符串查找
4. JavaScript代碼加載內容
通常來講有兩種狀況:一種狀況是在請求到html文檔時,網頁的數據在js代碼中,而並不是在html標籤中,之因此咱們看到的網頁是正常的,那是由於,實際上是因爲執行js代碼動態添加到標籤裏面的,因此這個時候內容在js代碼裏面的,而js的執行是在瀏覽器端的操做,因此用程序去請求網頁地址的時候,獲得的response是網頁代碼和js的代碼,因此本身在瀏覽器端能看到內容,解析時因爲js未執行,確定找到指定HTML標籤下內容確定爲空,如百度的主頁就是這種,這個時候的處理辦法,通常來說主要是要找到包含內容的js代碼串,而後經過正則表達式得到相應的內容,而不是解析HTML標籤。另外一種狀況是在和用戶交互時,JavaScript可能會動態生成一些dom,如點擊某個按鈕彈了一個對話框等;對於這種狀況,通常這些內容都是一些用戶提示相關的內容,沒什麼價值,若是確實須要,能夠分析一下js執行邏輯,但這樣的狀況不多。
Ajax/Fetch異步請求
這種狀況是如今很常見的,尤爲是在內容以分頁形式顯示在網頁上,而且頁面無刷新,或者是對網頁進行某個交互操做後,獲得內容。對於這種頁面,分析的時候咱們要跟蹤全部的請求,觀察數據究竟是在哪一步加載進來的。而後當咱們找到核心的異步請求的時候,就只需抓取這個異步請求就能夠了,若是原始網頁沒有任何有用信息,也不必去抓取原始網頁了。
八. 爬蟲技術的現狀
1. 語言
理論上來講,任何支持網絡通訊的語言都是能夠寫爬蟲的,爬蟲自己雖然語言關係不大,可是,總有相對順手、簡單的。目前來講,大多數爬蟲是用後臺腳本類語言寫的,其中python無疑是用的最多最廣的,而且頁誕生了不少優秀的庫和框架,如scrapy、BeautifulSoup 、pyquery、Mechanize等。可是通常來講,搜索引擎的爬蟲對爬蟲的效率要求更高,會選用c++、java、go(適合高併發),我在大學時代就用c++實現了一個多線程的框架,可是發現和python實現的爬蟲效率提高並不明顯,緣由是,對於簡單爬蟲,瓶頸在於數據分析及提取,而網絡效率和語言關係並不大。值得一提的是,在近幾年node發展很是快, 使得javascript遍地開花,有些人也開始嘗試用node作爬蟲,可是,這其實和其它後臺腳本語言沒什麼區別,也不如 python簡單, 由於你依舊不能在node 裏發起ajax請求,不能執行原網頁的dom。由於node的javascript 執行環境和瀏覽器的執行環境並不相同。那麼,難道就真的不能像在瀏覽器中同樣用js寫爬蟲,用jquery提取內容嗎?想法很大膽,咱們暫且擱置。
2. 運行環境
爬蟲自己不區分究竟是運行在windows仍是Linux,又或是OSX,但從業務角度講,咱們把運行在服務端(後臺)的,稱之爲後臺爬蟲。而如今,幾乎全部的爬蟲都是後臺爬蟲。
3. 後臺爬蟲的三大問題
問題一:交互問題
有些網頁每每須要和用戶進行一些交互,進而才能走到下一步,好比輸入一個驗證碼,拖動一個滑塊,選幾個漢字。網站之因此這麼作,不少時候都是爲了驗證訪問者究竟是人仍是機器。而爬蟲程序遇到這種狀況很難處理,傳統的簡單圖片驗證碼能夠經過圖形處理算法讀出內容,可是隨着各類各樣,花樣百出,人神共憤的、變態的驗證碼愈來愈多(尤爲是買火車票時,分分鐘都想爆粗口),這個問題就愈來愈嚴重。
問題二:Javascript 解析問題
如前文所述,javascript能夠動態生成dom。目前大多數網頁屬於動態網頁(內容由javascript動態填充),尤爲是在移動端,SPA/PWA應用愈來愈流行,網頁中大多數有用的數據都是經過ajax/fetch動態獲取後而後再由js填充到網頁dom樹中,單純的html靜態頁面中有用的數據不多。目前主要應對的方案就是對於js ajax/fetch請求直接請求ajax/fetch的url ,可是還有一些ajax的請求參數會依賴一段javascript動態生成,好比一個請求籤名,再好比用戶登錄時對密碼的加密等等,若是一昧的去用後臺腳本去幹javascript原本作的事,這就要清楚的理解原網頁代碼邏輯,而這不只很是麻煩,並且會使你的爬取代碼異常龐大臃腫,可是,更致命的是,有些javascript能夠作的事爬蟲程序是很難甚至是不能模仿的,好比有些網站使用拖動滑塊到某個位置的驗證碼機制,這就很難再爬蟲中去模仿。其實,總結一些,這些弊端歸根結底,是由於爬蟲程序並不是是瀏覽器,沒有javascript解析引擎所致。針對這個問題,目前主要的應對策略就是在爬蟲中引入Javascript 引擎,如PhantomJS,可是又有着明顯的弊端,如服務器同時有多個爬取任務時,資源佔用太大。還有就是,這些 無窗口的javascript引擎不少時候使用起來並不能像在瀏覽器環境中同樣,頁面內部發生跳轉時,會致使流程很難控制。
問題三:IP限制
這是目前對後臺爬蟲中最致命的。網站的防火牆會對某個固定ip在某段時間內請求的次數作限制,若是沒有超過上線則正常返回數據,超過了,則拒絕請求,如qq 郵箱。值得說明的是,ip限制有時並不是是專門爲了針對爬蟲的,而大多數時候是出於網站安全緣由針對DOS攻擊的防護措施。後臺爬取時機器和ip有限,很容易達到上線而致使請求被拒絕。目前主要的應對方案是使用代理,這樣一來ip的數量就會多一些,但代理ip依然有限,對於這個問題,根本不可能完全解決。