python: 網(wǎng)絡爬蟲

一、網(wǎng)絡爬蟲的定義

網(wǎng)絡爬蟲,即Web Spider,是一個很形象的名字。
把互聯(lián)網(wǎng)比喻成一個蜘蛛網(wǎng),那么Spider就是在網(wǎng)上爬來爬去的蜘蛛。
網(wǎng)絡蜘蛛是通過網(wǎng)頁的鏈接地址來尋找網(wǎng)頁的。
從網(wǎng)站某一個頁面(通常是首頁)開始,讀取網(wǎng)頁的內(nèi)容,找到在網(wǎng)頁中的其它鏈接地址,
然后通過這些鏈接地址尋找下一個網(wǎng)頁,這樣一直循環(huán)下去,直到把這個網(wǎng)站所有的網(wǎng)頁都抓取完為止。
如果把整個互聯(lián)網(wǎng)當成一個網(wǎng)站,那么網(wǎng)絡蜘蛛就可以用這個原理把互聯(lián)網(wǎng)上所有的網(wǎng)頁都抓取下來。
這樣看來,網(wǎng)絡爬蟲就是一個爬行程序,一個抓取網(wǎng)頁的程序。
網(wǎng)絡爬蟲的基本操作是抓取網(wǎng)頁。
那么如何才能隨心所欲地獲得自己想要的頁面?
我們先從URL開始。

二、瀏覽網(wǎng)頁的過程

抓取網(wǎng)頁的過程其實和讀者平時使用IE瀏覽器瀏覽網(wǎng)頁的道理是一樣的。
比如說你在瀏覽器的地址欄中輸入 www.baidu.com 這個地址。
打開網(wǎng)頁的過程其實就是瀏覽器作為一個瀏覽的“客戶端”,向服務器端發(fā)送了 一次請求,把服務器端的文件“抓”到本地,再進行解釋、展現(xiàn)。
HTML是一種標記語言,用標簽標記內(nèi)容并加以解析和區(qū)分。
瀏覽器的功能是將獲取到的HTML代碼進行解析,然后將原始的代碼轉(zhuǎn)變成我們直接看到的網(wǎng)站頁面。

三、URI和URL的概念和舉例

簡單的來講,URL就是在瀏覽器端輸入的 http://www.baidu.com 這個字符串。
在理解URL之前,首先要理解URI的概念。
什么是URI?
Web上每種可用的資源,如 HTML文檔、圖像、視頻片段、程序等都由一個通用資源標志符(Universal Resource Identifier, URI)進行定位。
URI通常由三部分組成:
①訪問資源的命名機制;
②存放資源的主機名;
③資源自身 的名稱,由路徑表示。
如下面的URI:
http://www.why.com.cn/myhtml/html1223/
我們可以這樣解釋它:
①這是一個可以通過HTTP協(xié)議訪問的資源,
②位于主機 www.why.com.cn上,
③通過路徑“/myhtml/html1223”訪問。

四、利用urllib2通過指定的URL抓取網(wǎng)頁內(nèi)容

在Python中,我們使用urllib2這個組件來抓取網(wǎng)頁。urllib2是Python的一個獲取URLs(Uniform Resource Locators)的組件。它以urlopen函數(shù)的形式提供了一個非常簡單的接口。

import urllib2    
req = urllib2.Request('http://www.baidu.com')    
response = urllib2.urlopen(req)    
the_page = response.read()    
print the_page  

在HTTP請求時,允許你做額外的兩件事:

1.發(fā)送data表單數(shù)據(jù)

這個內(nèi)容相信做過Web端的都不會陌生,有時候你希望發(fā)送一些數(shù)據(jù)到URL(通常URL與CGI[通用網(wǎng)關接口]腳本,或其他WEB應用程序掛接)。
在HTTP中,這個經(jīng)常使用熟知的POST請求發(fā)送。
這個通常在你提交一個HTML表單時由你的瀏覽器來做。
并不是所有的POSTs都來源于表單,你能夠使用POST提交任意的數(shù)據(jù)到你自己的程序。
一般的HTML表單,data需要編碼成標準形式。然后做為data參數(shù)傳到Request對象。
編碼工作使用urllib的函數(shù)而非urllib2。

values = {'name' : 'WHY',    
          'location' : 'SDU',    
          'language' : 'Python' }    
  
data = urllib.urlencode(values) # 編碼工作  
req = urllib2.Request(url, data)  # 發(fā)送請求同時傳data表單  
response = urllib2.urlopen(req)  #接受反饋的信息  
the_page = response.read()  #讀取反饋的內(nèi)容  

如果沒有傳送data參數(shù),urllib2使用GET方式的請求。
GET和POST請求的不同之處是POST請求通常有"副作用",
它們會由于某種途徑改變系統(tǒng)狀態(tài)(例如提交成堆垃圾到你的門口)。
Data同樣可以通過在Get請求的URL本身上面編碼來傳送。

import urllib2    
import urllib  
  
data = {}  
  
data['name'] = 'WHY'    
data['location'] = 'SDU'    
data['language'] = 'Python'  
  
url_values = urllib.urlencode(data)    
print url_values  
  
name=Somebody+Here&language=Python&location=Northampton    
url = 'http://www.example.com/example.cgi'    
full_url = url + '?' + url_values  
  
data = urllib2.open(full_url)

這樣就實現(xiàn)了Data數(shù)據(jù)的Get傳送。

2.設置Headers到http請求

有一些站點不喜歡被程序(非人為訪問)訪問,或者發(fā)送不同版本的內(nèi)容到不同的瀏覽器。
默認的urllib2把自己作為“Python-urllib/x.y”(x和y是Python主版本和次版本號,例如Python-urllib/2.7),

這個身份可能會讓站點迷惑,或者干脆不工作。
瀏覽器確認自己身份是通過User-Agent頭,當你創(chuàng)建了一個請求對象,你可以給他一個包含頭數(shù)據(jù)的字典。
下面的例子發(fā)送跟上面一樣的內(nèi)容,但把自身模擬成Internet Explorer。

import urllib    
import urllib2    
  
url = 'http://www.someserver.com/cgi-bin/register.cgi'  
  
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'    
values = {'name' : 'WHY',    
          'location' : 'SDU',    
          'language' : 'Python' }    
  
headers = { 'User-Agent' : user_agent }    
data = urllib.urlencode(values)    
req = urllib2.Request(url, data, headers)    
response = urllib2.urlopen(req)    
the_page = response.read() 

五、異常的處理和HTTP狀態(tài)碼的分類

先來說一說HTTP的異常處理問題。
當urlopen不能夠處理一個response時,產(chǎn)生urlError。
不過通常的Python APIs異常如ValueError,TypeError等也會同時產(chǎn)生。
HTTPError是urlError的子類,通常在特定HTTP URLs中產(chǎn)生。

1.URLError

通常,URLError在沒有網(wǎng)絡連接(沒有路由到特定服務器),或者服務器不存在的情況下產(chǎn)生。
這種情況下,異常同樣會帶有"reason"屬性,它是一個tuple(可以理解為不可變的數(shù)組),
包含了一個錯誤號和一個錯誤信息。
我們建一個urllib2_test06.py來感受一下異常的處理:

import urllib2  
  
req = urllib2.Request('http://www.baibai.com')  
  
try: urllib2.urlopen(req)  
  
except urllib2.URLError, e:    
    print e.reason

按下F5,可以看到打印出來的內(nèi)容是:
[Errno 11001] getaddrinfo failed
也就是說,錯誤號是11001,內(nèi)容是getaddrinfo failed

2.HTTPError

服務器上每一個HTTP 應答對象response包含一個數(shù)字"狀態(tài)碼"。
有時狀態(tài)碼指出服務器無法完成請求。默認的處理器會為你處理一部分這種應答。
例如:假如response是一個"重定向",需要客戶端從別的地址獲取文檔,urllib2將為你處理。
其他不能處理的,urlopen會產(chǎn)生一個HTTPError。
典型的錯誤包含"404"(頁面無法找到),"403"(請求禁止),和"401"(帶驗證請求)。
HTTP狀態(tài)碼表示HTTP協(xié)議所返回的響應的狀態(tài)。
比如客戶端向服務器發(fā)送請求,如果成功地獲得請求的資源,則返回的狀態(tài)碼為200,表示響應成功。
如果請求的資源不存在, 則通常返回404錯誤。
HTTP狀態(tài)碼通常分為5種類型,分別以1~5五個數(shù)字開頭,由3位整數(shù)組成:


200:請求成功 處理方式:獲得響應的內(nèi)容,進行處理
201:請求完成,結(jié)果是創(chuàng)建了新資源。新創(chuàng)建資源的URI可在響應的實體中得到 處理方式:爬蟲中不會遇到
202:請求被接受,但處理尚未完成 處理方式:阻塞等待
204:服務器端已經(jīng)實現(xiàn)了請求,但是沒有返回新的信 息。如果客戶是用戶代理,則無須為此更新自身的文檔視圖。 處理方式:丟棄
300:該狀態(tài)碼不被HTTP/1.0的應用程序直接使用, 只是作為3XX類型回應的默認解釋。存在多個可用的被請求資源。 處理方式:若程序中能夠處理,則進行進一步處理,如果程序中不能處理,則丟棄
301:請求到的資源都會分配一個永久的URL,這樣就可以在將來通過該URL來訪問此資源 處理方式:重定向到分配的URL
302:請求到的資源在一個不同的URL處臨時保存 處理方式:重定向到臨時的URL
304 請求的資源未更新 處理方式:丟棄
400 非法請求 處理方式:丟棄
401 未授權(quán) 處理方式:丟棄
403 禁止 處理方式:丟棄
404 沒有找到 處理方式:丟棄
5XX 回應代碼以“5”開頭的狀態(tài)碼表示服務器端發(fā)現(xiàn)自己出現(xiàn)錯誤,不能繼續(xù)執(zhí)行請求 處理方式:丟棄


HTTPError實例產(chǎn)生后會有一個整型'code'屬性,是服務器發(fā)送的相關錯誤號。
Error Codes錯誤碼
因為默認的處理器處理了重定向(300以外號碼),并且100-299范圍的號碼指示成功,所以你只能看到400-599的錯誤號碼。
BaseHTTPServer.BaseHTTPRequestHandler.response是一個很有用的應答號碼字典,顯示了HTTP協(xié)議使用的所有的應答號。
當一個錯誤號產(chǎn)生后,服務器返回一個HTTP錯誤號,和一個錯誤頁面。
你可以使用HTTPError實例作為頁面返回的應答對象response。
這表示和錯誤屬性一樣,它同樣包含了read,geturl,和info方法。

import urllib2  
req = urllib2.Request('http://bbs.csdn.net/callmewhy')  
  
try:  
    urllib2.urlopen(req)  
  
except urllib2.URLError, e:  
  
    print e.code  
    #print e.read() 

按下F5可以看見輸出了404的錯誤碼,也就說沒有找到這個頁面。

3.Wrapping

所以如果你想為HTTPError或URLError做準備,將有兩個基本的辦法。推薦使用第二種。

from urllib2 import Request, urlopen, URLError, HTTPError  
  
req = Request('http://bbs.csdn.net/callmewhy')  
  
try:  
  
    response = urlopen(req)  
  
except HTTPError, e:  
  
    print 'The server couldn\'t fulfill the request.'  
  
    print 'Error code: ', e.code  
  
except URLError, e:  
  
    print 'We failed to reach a server.'  
  
    print 'Reason: ', e.reason  
  
else:  
    print 'No exception was raised.'  
    # everything is fine  

和其他語言相似,try之后捕獲異常并且將其內(nèi)容打印出來。
這里要注意的一點,except HTTPError 必須在第一個,否則except URLError將同樣接受到HTTPError 。
因為HTTPError是URLError的子類,如果URLError在前面它會捕捉到所有的URLError(包括HTTPError )。

from urllib2 import Request, urlopen, URLError, HTTPError  
  
req = Request('http://bbs.csdn.net/callmewhy')  
    
try:    
    
    response = urlopen(req)    
    
except URLError, e:    
    
    if hasattr(e, 'reason'):    
    
        print 'We failed to reach a server.'    
    
        print 'Reason: ', e.reason    
    
    elif hasattr(e, 'code'):    
    
        print 'The server couldn\'t fulfill the request.'    
    
        print 'Error code: ', e.code    
    
else:    
    print 'No exception was raised.'    
    # everything is fine  

六、Opener與Handler的介紹和實例應用

在開始后面的內(nèi)容之前,先來解釋一下urllib2中的兩個個方法:info and geturl
urlopen返回的應答對象response(或者HTTPError實例)有兩個很有用的方法info()和geturl()

1.geturl():

這個返回獲取的真實的URL,這個很有用,因為urlopen(或者opener對象使用的)或許會有重定向。獲取的URL或許跟請求URL不同。
以人人中的一個超級鏈接為例

from urllib2 import Request, urlopen, URLError, HTTPError  
  
  
old_url = 'http://rrurl.cn/b1UZuP'  
req = Request(old_url)  
response = urlopen(req)    
print 'Old url :' + old_url  
print 'Real url :' + response.geturl()  

運行結(jié)果如下:


2.info():

這個返回對象的字典對象,該字典描述了獲取的頁面情況。通常是服務器發(fā)送的特定頭headers。目前是httplib.HTTPMessage 實例。
經(jīng)典的headers包含"Content-length","Content-type",和其他內(nèi)容。

from urllib2 import Request, urlopen, URLError, HTTPError  
  
old_url = 'http://www.baidu.com'  
req = Request(old_url)  
response = urlopen(req)    
print 'Info():'  
print response.info()  

運行結(jié)果如下:


下面來說一說urllib2中的兩個重要概念:Openers和Handlers。

1.Openers:

當你獲取一個URL你使用一個opener(一個urllib2.OpenerDirector的實例)。
正常情況下,我們使用默認opener:通過urlopen。
但你能夠創(chuàng)建個性的openers。

2.Handles:

Openers使用處理器handlers,所有的“繁重”工作由handlers處理。
每個handlers知道如何通過特定協(xié)議打開URLs,或者如何處理URL打開時的各個方面。
例如HTTP重定向或者HTTP cookies。

如果你希望用特定處理器獲取URLs你會想創(chuàng)建一個openers,例如獲取一個能處理cookie的opener,或者獲取一個不重定向的opener。

要創(chuàng)建一個 opener,可以實例化一個OpenerDirector,
然后調(diào)用.add_handler(some_handler_instance)。
同樣,可以使用build_opener,這是一個更加方便的函數(shù),用來創(chuàng)建opener對象,他只需要一次函數(shù)調(diào)用。
build_opener默認添加幾個處理器,但提供快捷的方法來添加或更新默認處理器。
其他的處理器handlers你或許會希望處理代理,驗證,和其他常用但有點特殊的情況。

install_opener 用來創(chuàng)建(全局)默認opener。這個表示調(diào)用urlopen將使用你安裝的opener。
Opener對象有一個open方法。
該方法可以像urlopen函數(shù)那樣直接用來獲取urls:通常不必調(diào)用install_opener,除了為了方便。

說完了上面兩個內(nèi)容,下面我們來看一下基本認證的內(nèi)容,這里會用到上面提及的Opener和Handler。
Basic Authentication 基本驗證
為了展示創(chuàng)建和安裝一個handler,我們將使用HTTPBasicAuthHandler。
當需要基礎驗證時,服務器發(fā)送一個header(401錯誤碼) 請求驗證。這個指定了scheme 和一個‘realm’,看起來像這樣:Www-authenticate: SCHEME realm="REALM".
例如
Www-authenticate: Basic realm="cPanel Users"
客戶端必須使用新的請求,并在請求頭里包含正確的姓名和密碼。
這是“基礎驗證”,為了簡化這個過程,我們可以創(chuàng)建一個HTTPBasicAuthHandler的實例,并讓opener使用這個handler就可以啦。

HTTPBasicAuthHandler使用一個密碼管理的對象來處理URLs和realms來映射用戶名和密碼。
如果你知道realm(從服務器發(fā)送來的頭里)是什么,你就能使用HTTPPasswordMgr。

通常人們不關心realm是什么。那樣的話,就能用方便的HTTPPasswordMgrWithDefaultRealm。
這個將在你為URL指定一個默認的用戶名和密碼。
這將在你為特定realm提供一個其他組合時得到提供。
我們通過給realm參數(shù)指定None提供給add_password來指示這種情況。

最高層次的URL是第一個要求驗證的URL。你傳給.add_password()更深層次的URLs將同樣合適。
說了這么多廢話,下面來用一個例子演示一下上面說到的內(nèi)容。

# -*- coding: utf-8 -*-  
import urllib2  
  
# 創(chuàng)建一個密碼管理者  
password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()  
  
# 添加用戶名和密碼  
  
top_level_url = "http://example.com/foo/"  
  
# 如果知道 realm, 我們可以使用他代替 ``None``.  
# password_mgr.add_password(None, top_level_url, username, password)  
password_mgr.add_password(None, top_level_url,'why', '1223')  
  
# 創(chuàng)建了一個新的handler  
handler = urllib2.HTTPBasicAuthHandler(password_mgr)  
  
# 創(chuàng)建 "opener" (OpenerDirector 實例)  
opener = urllib2.build_opener(handler)  
  
a_url = 'http://www.baidu.com/'  
  
# 使用 opener 獲取一個URL  
opener.open(a_url)  
  
# 安裝 opener.  
# 現(xiàn)在所有調(diào)用 urllib2.urlopen 將用我們的 opener.  
urllib2.install_opener(opener)

注意:以上的例子我們僅僅提供我們的HHTPBasicAuthHandler給build_opener。
默認的openers有正常狀況的handlers:ProxyHandler,UnknownHandler,HTTPHandler,HTTPDefaultErrorHandler, HTTPRedirectHandler,F(xiàn)TPHandler, FileHandler, HTTPErrorProcessor。
代碼中的top_level_url 實際上可以是完整URL(包含"http:",以及主機名及可選的端口號)。
例如:http://example.com/
也可以是一個“authority”(即主機名和可選的包含端口號)。
例如:“example.com” or “example.com:8080”。
后者包含了端口號。

七、urllib2的使用細節(jié)與抓站技巧

前面說到了urllib2的簡單入門,下面整理了一部分urllib2的使用細節(jié)。

1.Proxy 的設置

urllib2 默認會使用環(huán)境變量 http_proxy 來設置 HTTP Proxy。
如果想在程序中明確控制 Proxy 而不受環(huán)境變量的影響,可以使用代理。

import urllib2  
enable_proxy = True  
proxy_handler = urllib2.ProxyHandler({"http" : 'http://some-proxy.com:8080'})  
null_proxy_handler = urllib2.ProxyHandler({})  
if enable_proxy:  
    opener = urllib2.build_opener(proxy_handler)  
else:  
    opener = urllib2.build_opener(null_proxy_handler)  
urllib2.install_opener(opener)  

這里要注意的一個細節(jié),使用 urllib2.install_opener() 會設置 urllib2 的全局 opener 。
這樣后面的使用會很方便,但不能做更細致的控制,比如想在程序中使用兩個不同的 Proxy 設置等。
比較好的做法是不使用 install_opener 去更改全局的設置,而只是直接調(diào)用 opener 的 open 方法代替全局的 urlopen 方法。

2.Timeout 設置

import urllib2  
response = urllib2.urlopen('http://www.google.com', timeout=10) 

3.在 HTTP Request 中加入特定的 Header

要加入 header,需要使用 Request 對象:

import urllib2  
request = urllib2.Request('http://www.baidu.com/')  
request.add_header('User-Agent', 'fake-client')  
response = urllib2.urlopen(request)  
print response.read()  

對有些 header 要特別留意,服務器會針對這些 header 做檢查
User-Agent : 有些服務器或 Proxy 會通過該值來判斷是否是瀏覽器發(fā)出的請求
Content-Type : 在使用 REST 接口時,服務器會檢查該值,用來確定 HTTP Body 中的內(nèi)容該怎樣解析。常見的取值有:
application/xml : 在 XML RPC,如 RESTful/SOAP 調(diào)用時使用
application/json : 在 JSON RPC 調(diào)用時使用
application/x-www-form-urlencoded : 瀏覽器提交 Web 表單時使用
在使用服務器提供的 RESTful 或 SOAP 服務時, Content-Type 設置錯誤會導致服務器拒絕服務

4.Redirect

urllib2 默認情況下會針對 HTTP 3XX 返回碼自動進行 redirect 動作,無需人工配置。要檢測是否發(fā)生了 redirect 動作,只要檢查一下 Response 的 URL 和 Request 的 URL 是否一致就可以了。

import urllib2  
my_url = 'http://www.google.cn'  
response = urllib2.urlopen(my_url)  
redirected = response.geturl() == my_url  
print redirected  
  
my_url = 'http://rrurl.cn/b1UZuP'  
response = urllib2.urlopen(my_url)  
redirected = response.geturl() == my_url  
print redirected  

如果不想自動 redirect,除了使用更低層次的 httplib 庫之外,還可以自定義HTTPRedirectHandler 類。

import urllib2  
class RedirectHandler(urllib2.HTTPRedirectHandler):  
    def http_error_301(self, req, fp, code, msg, headers):  
        print "301"  
        pass  
    def http_error_302(self, req, fp, code, msg, headers):  
        print "303"  
        pass  
  
opener = urllib2.build_opener(RedirectHandler)  
opener.open('http://rrurl.cn/b1UZuP')  

5.Cookie

urllib2 對 Cookie 的處理也是自動的。如果需要得到某個 Cookie 項的值,可以這么做:

import urllib2  
import cookielib  
cookie = cookielib.CookieJar()  
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))  
response = opener.open('http://www.baidu.com')  
for item in cookie:  
    print 'Name = '+item.name  
    print 'Value = '+item.value 

運行之后就會輸出訪問百度的Cookie值:


6.使用 HTTP 的 PUT 和 DELETE 方法

urllib2 只支持 HTTP 的 GET 和 POST 方法,如果要使用 HTTP PUT 和 DELETE ,只能使用比較低層的 httplib 庫。雖然如此,我們還是能通過下面的方式,使 urllib2 能夠發(fā)出 PUT 或DELETE 的請求:

import urllib2  
request = urllib2.Request(uri, data=data)  
request.get_method = lambda: 'PUT' # or 'DELETE'  
response = urllib2.urlopen(request)  

7.得到 HTTP 的返回碼

對于 200 OK 來說,只要使用 urlopen 返回的 response 對象的 getcode() 方法就可以得到 HTTP 的返回碼。但對其它返回碼來說,urlopen 會拋出異常。這時候,就要檢查異常對象的 code 屬性了:

import urllib2  
try:  
    response = urllib2.urlopen('http://bbs.csdn.net/why')  
except urllib2.HTTPError, e:  
    print e.code 

8.Debug Log

使用 urllib2 時,可以通過下面的方法把 debug Log 打開,這樣收發(fā)包的內(nèi)容就會在屏幕上打印出來,方便調(diào)試,有時可以省去抓包的工作

import urllib2  
httpHandler = urllib2.HTTPHandler(debuglevel=1)  
httpsHandler = urllib2.HTTPSHandler(debuglevel=1)  
opener = urllib2.build_opener(httpHandler, httpsHandler)  
urllib2.install_opener(opener)  
response = urllib2.urlopen('http://www.google.com') 

這樣就可以看到傳輸?shù)臄?shù)據(jù)包內(nèi)容了:


9.表單的處理

登錄必要填表,表單怎么填?
首先利用工具截取所要填表的內(nèi)容。
比如我一般用firefox+httpfox插件來看看自己到底發(fā)送了些什么包。
以verycd為例,先找到自己發(fā)的POST請求,以及POST表單項。
可以看到verycd的話需要填username,password,continueURI,fk,login_submit這幾項,其中fk是隨機生成的(其實不太隨機,看上去像是把epoch時間經(jīng)過簡單的編碼生成的),需要從網(wǎng)頁獲取,也就是說得先訪問一次網(wǎng)頁,用正則表達式等工具截取返回數(shù)據(jù)中的fk項。continueURI顧名思義可以隨便寫,login_submit是固定的,這從源碼可以看出。還有username,password那就很顯然了:

# -*- coding: utf-8 -*-  
import urllib  
import urllib2  
postdata=urllib.urlencode({  
    'username':'汪小光',  
    'password':'why888',  
    'continueURI':'http://www.verycd.com/',  
    'fk':'',  
    'login_submit':'登錄'  
})  
req = urllib2.Request(  
    url = 'http://secure.verycd.com/signin',  
    data = postdata  
)  
result = urllib2.urlopen(req)  
print result.read()   

10.偽裝成瀏覽器訪問

某些網(wǎng)站反感爬蟲的到訪,于是對爬蟲一律拒絕請求
這時候我們需要偽裝成瀏覽器,這可以通過修改http包中的header來實現(xiàn)

#…  
  
headers = {  
    'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'  
}  
req = urllib2.Request(  
    url = 'http://secure.verycd.com/signin/*/http://www.verycd.com/',  
    data = postdata,  
    headers = headers  
)  
#... 

11.對付"反盜鏈"

某些站點有所謂的反盜鏈設置,其實說穿了很簡單,
就是檢查你發(fā)送請求的header里面,referer站點是不是他自己,
所以我們只需要像把headers的referer改成該網(wǎng)站即可,以cnbeta為例:

#...
headers = {
    'Referer':'http://www.cnbeta.com/articles'
}
#...

headers是一個dict數(shù)據(jù)結(jié)構(gòu),你可以放入任何想要的header,來做一些偽裝。
例如,有些網(wǎng)站喜歡讀取header中的X-Forwarded-For來看看人家的真實IP,可以直接把X-Forwarde-For改了。

八、一個簡單的百度貼吧的小爬蟲

# -*- coding: utf-8 -*-  
#---------------------------------------  
#   程序:百度貼吧爬蟲  
#   版本:0.1  
#   作者:why  
#   日期:2013-05-14  
#   語言:Python 2.7  
#   操作:輸入帶分頁的地址,去掉最后面的數(shù)字,設置一下起始頁數(shù)和終點頁數(shù)。  
#   功能:下載對應頁碼內(nèi)的所有頁面并存儲為html文件。  
#---------------------------------------  
   
import string, urllib2  
   
#定義百度函數(shù)  
def baidu_tieba(url,begin_page,end_page):     
    for i in range(begin_page, end_page+1):  
        sName = string.zfill(i,5) + '.html'#自動填充成六位的文件名  
        print '正在下載第' + str(i) + '個網(wǎng)頁,并將其存儲為' + sName + '......'  
        f = open(sName,'w+')  
        m = urllib2.urlopen(url + str(i)).read()  
        f.write(m)  
        f.close()  
   
   
#-------- 在這里輸入?yún)?shù) ------------------  
  
# 這個是山東大學的百度貼吧中某一個帖子的地址  
#bdurl = 'http://tieba.baidu.com/p/2296017831?pn='  
#iPostBegin = 1  
#iPostEnd = 10  
  
bdurl = str(raw_input(u'請輸入貼吧的地址,去掉pn=后面的數(shù)字:\n'))  
begin_page = int(raw_input(u'請輸入開始的頁數(shù):\n'))  
end_page = int(raw_input(u'請輸入終點的頁數(shù):\n'))  
#-------- 在這里輸入?yún)?shù) ------------------  
   
  
#調(diào)用  
baidu_tieba(bdurl,begin_page,end_page)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容