天天看點

如何抓取WEB頁面

文章轉載自: http://blog.binux.me/2013/09/howto-crawl-web/

1. HTTP協定

       WEB内容是通過HTTP協定傳輸的,實際上,任何的抓取行為都是在模拟浏覽器的HTTP請求。那麼,首先通過 http://zh.wikipedia.org/wiki/ 超文本傳輸協定 來對HTTP協定來進行初步的了解:

* HTTP通常通過建立到伺服器80端口的TCP連接配接進行通信

* HTTP協定的内容包括請求方式(method), url,header,body,通常以純文字方式發送

* HTTP傳回内容包括狀态碼,header,body,通常以純文字方式傳回

* header以及body間以CRLF(\r\n)分割

       由于富web應用越來越盛行,單純的HTTP協定已經不能滿足 -人類的欲望- 人們的需求了,websocket, spdy等越來越多的非HTTP協定資訊傳輸手段被使用,但是目前看來,web的主要資訊依舊承載于http協定。

我們通過通路http://www.baidu.com/來檢視一個正常的HTTP請求,浏覽器的HTTP 請求部分如下所示:

GET / HTTP/1.1

Host: www.baidu.com

Connection: keep-alive

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8

User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.66 Safari/537.36

DNT: 1

Accept-Encoding: gzip,deflate,sdch

Accept-Language: zh-CN,zh;q=0.8

請求中的第一行稱為Request-Line,包含了“請求方式 URL HTTP版本”,在上面的例子中,這個請求方式(method)為GET,URL為 /, HTTP版本為 HTTP/1.1 。

注意到這裡的URL并不是我們通路時的 http://www.baidu.com/ 而隻是一個 /,而www.baidu.com的域名在Header Host: www.baidu.com 中展現。這樣表示請求的資源 / 是位于主機(host) www.baidu.com 上的,而 GET http://www.baidu.com/ HTTP/1.1 則表示請求的資源位于别的地方,通常用于http代理請求中。

請求的後續行都是Header,其中比較重要的header有 Host, User-Agent, Cookie, Referer, X-Requested-With (這個請求中未展現)。如果是POST請求,還會有body。

雖然并不需要了解HTTP請求,隻要參照展示的内容模拟請求就可以抓取到内容,但是學習一下各個header的作用有助于了解哪些元素是必須的,哪些可以被忽略或修改。

更多内容可以通過以下連結進行進一步學習: 

http://zh.wikipedia.org/wiki/URL 

http://en.wikipedia.org/wiki/Query_string 

http://en.wikipedia.org/wiki/HTTP_POST 

http://en.wikipedia.org/wiki/List_of_HTTP_header_fields 

好了,這就是一個請求的全部,隻要正确模拟了method,url,header,body 這四要素,任何内容都能抓下來,而所有的四個要素,隻要打開浏覽器-審查元素-Network就能看到!

2. curl

現在我們就可以通過curl指令來模拟一個請求: 

curl -v -H "User-Agent: Chrome" http://www.baidu.com/ 

其中 -v 用于顯示了請求的内容,-H 指定header,具體curl的使用方式可以 man curl 或者你可以在chrome或者其他平台上找到很多類似的工具。 

如果想看到請求是否正确,可以 curl http://httpbin.org/get 這個位址,它會傳回經過解析的請求内容,來看看你的請求是否符合預期(http://httpbin.org/中有包括POST在内的完整API)

HTTP傳回下面展示了一個http傳回的header部分,body内容被省略:

HTTP/1.1 200 OK

Date: Mon, 30 Sep 2013 06:51:11 GMT

Server: BWS/1.0

Content-Length: 4379

Content-Type: text/html;charset=utf-8

Cache-Control: private

BDPAGETYPE: 1

BDUSERID: 0

BDQID: 0x8e3bf8800bcc3d7e

Set-Cookie: BDSVRTM=2; path=/

Set-Cookie: H_PS_PSSID=3409_3381_1462_2980_3089_3502_3439; path=/; domain=.baidu.com

Set-Cookie: BAIDUID=5DDF70314DF9C307385D1821EC3B9F78:FG=1; expires=Mon, 30-Sep-43 06:51:11 GMT; path=/; domain=.baidu.com

Expires: Mon, 30 Sep 2013 06:51:11 GMT

Content-Encoding: gzip

P3P: CP=" OTI DSP COR IVA OUR IND COM "

Connection: Keep-Alive

其中第一行為 HTTP版本 狀态碼 狀态文字說明 之後的内容都是header,其中比較重要的有:Content-Type, Set-Cookie, Location, Content-Encoding(參見 HTTP_header#Requests)。傳回之後的内容就是我們看到的網頁内容了。

傳回中最重要的是狀态碼和body中的内容,狀态碼決定抓取是否成功(200),是否會有跳轉 (HTTP狀态碼),内容就是我們關心的内容了。

3. 實際應用

其他http庫在實際抓取中,選擇一個友善的HTTP庫會幫你解決很多http的細節問題,比如http庫會幫你:

* 建立http連接配接

* 設定常用header,生成正确的http請求

* get/post參數編碼

* 跳轉重定向

* 自動儲存處理cookie

* 傳回gzip解壓,内容編碼

python中推薦 requests,在指令行中我一般用curl進行調試。

AJAX:現在越來越多的頁面使用了AJAX技術,表現為内容并不在打開的頁面的源碼中,而是通過稱為 AJAX 的技術,在頁面打開後加載的。但實際上,AJAX也是通過HTTP傳送資訊的,隻不過内容來自于頁面發起的另一個http請求,通過檢視chome中的network列出的頁面所有請求,一定可以找到内容,之後隻需要模拟對應的這個請求即可。

HTML内容解析:web頁面大都以HTML編寫,對于簡單的内容提取,使用正則即可。但是對付複雜的内容提取需求正則并不是一個好的選擇(甚至稱不上一個正确的選擇),一款HTML/XML解析器+xpath/css selector是一個更有效的選擇。

富web應用:可能分析AJAX請求,和内容提取的代價太高。這時可能需要上最後手段——浏覽器渲染。通過 phantomjs或類似浏覽器引擎,建構一個真實的浏覽器執行js、渲染頁面。