天天看點

【Python開發】【神經網絡與深度學習】網絡爬蟲之python實作

一、網絡爬蟲的定義

網絡爬蟲,即Web Spider,是一個很形象的名字。

把網際網路比喻成一個蜘蛛網,那麼Spider就是在網上爬來爬去的蜘蛛。

網絡蜘蛛是通過網頁的連結位址來尋找網頁的。

從網站某一個頁面(通常是首頁)開始,讀取網頁的内容,找到在網頁中的其它連結位址,

然後通過這些連結位址尋找下一個網頁,這樣一直循環下去,直到把這個網站所有的網頁都抓取完為止。

如果把整個網際網路當成一個網站,那麼網絡蜘蛛就可以用這個原理把網際網路上所有的網頁都抓取下來。

這樣看來,網絡爬蟲就是一個爬行程式,一個抓取網頁的程式。

網絡爬蟲的基本操作是抓取網頁。

那麼如何才能随心所欲地獲得自己想要的頁面?

我們先從URL開始。

二、浏覽網頁的過程

抓取網頁的過程其實和讀者平時使用IE浏覽器浏覽網頁的道理是一樣的。

比如說你在浏覽器的位址欄中輸入    www.baidu.com    這個位址。

打開網頁的過程其實就是浏覽器作為一個浏覽的“用戶端”,向伺服器端發送了 一次請求,把伺服器端的檔案“抓”到本地,再進行解釋、展現。

HTML是一種标記語言,用标簽标記内容并加以解析和區分。

浏覽器的功能是将擷取到的HTML代碼進行解析,然後将原始的代碼轉變成我們直接看到的網站頁面。

三、URI的概念和舉例

簡單的來講,URL就是在浏覽器端輸入的    www.baidu.com    這個字元串。

在了解URL之前,首先要了解URI的概念。

什麼是URI?

Web上每種可用的資源,如 HTML文檔、圖像、視訊片段、程式等都由一個通用資源标志符(Universal Resource Identifier, URI)進行定位。 

URI通常由三部分組成:

①通路資源的命名機制;

②存放資源的主機名;

③資源自身 的名稱,由路徑表示。

如下面的URI:

http://www.why.com.cn/myhtml/html1223/

我們可以這樣解釋它:

①這是一個可以通過HTTP協定通路的資源,

②位于主機 www.webmonkey.com.cn上,

③通過路徑“/html/html40”通路。 

四、URL的了解和舉例

URL是URI的一個子集。它是Uniform Resource Locator的縮寫,譯為“統一資源定位 符”。

通俗地說,URL是Internet上描述資訊資源的字元串,主要用在各種WWW客戶程式和伺服器程式上。

采用URL可以用一種統一的格式來描述各種資訊資源,包括檔案、伺服器的位址和目錄等。

URL的格式由三部分組成: 

①第一部分是協定(或稱為服務方式)。

②第二部分是存有該資源的主機IP位址(有時也包括端口号)。

③第三部分是主機資源的具體位址,如目錄和檔案名等。

第一部分和第二部分用“://”符号隔開,

第二部分和第三部分用“/”符号隔開。

第一部分和第二部分是不可缺少的,第三部分有時可以省略。 

下面來看看兩個URL的小例子。

1.HTTP協定的URL示例:

使用超級文本傳輸協定HTTP,提供超級文本資訊服務的資源。 

例:http://www.peopledaily.com.cn/channel/welcome.htm 

其計算機域名為www.peopledaily.com.cn。

超級文本檔案(檔案類型為.html)是在目錄 /channel下的welcome.htm。

這是中國人民日報的一台計算機。 

例:http://www.rol.cn.net/talk/talk1.htm 

其計算機域名為www.rol.cn.net。

超級文本檔案(檔案類型為.html)是在目錄/talk下的talk1.htm。

這是瑞得聊天室的位址,可由此進入瑞得聊天室的第1室。

2.檔案的URL

用URL表示檔案時,伺服器方式用file表示,後面要有主機IP位址、檔案的存取路 徑(即目錄)和檔案名等資訊。

有時可以省略目錄和檔案名,但“/”符号不能省略。 

例:file://ftp.yoyodyne.com/pub/files/foobar.txt 

上面這個URL代表存放在主機ftp.yoyodyne.com上的pub/files/目錄下的一個檔案,檔案名是foobar.txt。

例:file://ftp.yoyodyne.com/pub 

代表主機ftp.yoyodyne.com上的目錄/pub。 

例:file://ftp.yoyodyne.com/ 

代表主機ftp.yoyodyne.com的根目錄。 

爬蟲最主要的處理對象就是URL,它根據URL位址取得所需要的檔案内容,然後對它 進行進一步的處理。

是以,準确地了解URL對了解網絡爬蟲至關重要

​​[Python]網絡爬蟲(二):利用urllib2通過指定的URL抓取網頁内容​​

分類: ​​爬蟲​​ ​​Python​​2013-05-13 23:45 1628人閱讀 ​

​評論​​(0) ​​收藏​​ ​​舉報​​

所謂網頁抓取,就是把URL位址中指定的網絡資源從網絡流中讀取出來,儲存到本地。 

類似于使用程式模拟IE浏覽器的功能,把URL作為HTTP請求的内容發送到伺服器端, 然後讀取伺服器端的響應資源。

在Python中,我們使用urllib2這個元件來抓取網頁。

urllib2是Python的一個擷取URLs(Uniform Resource Locators)的元件。

它以urlopen函數的形式提供了一個非常簡單的接口。

最簡單的urllib2的應用代碼隻需要四行。

我們建立一個檔案urllib2_test01.py來感受一下urllib2的作用:

[python] ​​view plain​

​​​copy​​

  1. import urllib2  
  2. response = urllib2.urlopen('http://www.baidu.com/')  
  3. html = response.read()  
  4. print html  

按下F5可以看到運作的結果:

【Python開發】【神經網絡與深度學習】網絡爬蟲之python實作

我們可以打開百度首頁,右擊,選擇檢視源代碼(火狐OR谷歌浏覽器均可),會發現也是完全一樣的内容。

也就是說,上面這四行代碼将我們通路百度時浏覽器收到的代碼們全部列印了出來。

這就是一個最簡單的urllib2的例子。

除了"http:",URL同樣可以使用"ftp:","file:"等等來替代。

HTTP是基于請求和應答機制的:

用戶端提出請求,服務端提供應答。

urllib2用一個Request對象來映射你提出的HTTP請求。

在它最簡單的使用形式中你将用你要請求的位址建立一個Request對象,

通過調用urlopen并傳入Request對象,将傳回一個相關請求response對象,

這個應答對象如同一個檔案對象,是以你可以在Response中調用.read()。

我們建立一個檔案urllib2_test02.py來感受一下:

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

可以看到輸出的内容和test01是一樣的。

urllib2使用相同的接口處理所有的URL頭。例如你可以像下面那樣建立一個ftp請求。

  1. req = urllib2.Request('ftp://example.com/')  

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

1.發送data表單資料

這個内容相信做過Web端的都不會陌生,

有時候你希望發送一些資料到URL(通常URL與CGI[通用網關接口]腳本,或其他WEB應用程式挂接)。

在HTTP中,這個經常使用熟知的POST請求發送。

這個通常在你送出一個HTML表單時由你的浏覽器來做。

并不是所有的POSTs都來源于表單,你能夠使用POST送出任意的資料到你自己的程式。

一般的HTML表單,data需要編碼成标準形式。然後做為data參數傳到Request對象。

編碼工作使用urllib的函數而非urllib2。

我們建立一個檔案urllib2_test03.py來感受一下:

  1. import urllib    
  2. url = 'http://www.someserver.com/register.cgi'    
  3. values = {'name' : 'WHY',    
  4.           'location' : 'SDU',    
  5.           'language' : 'Python' }    
  6. data = urllib.urlencode(values) # 編碼工作  
  7. req = urllib2.Request(url, data)  # 發送請求同時傳data表單  
  8. response = urllib2.urlopen(req)  #接受回報的資訊  
  9. the_page = response.read()  #讀取回報的内容  

如果沒有傳送data參數,urllib2使用GET方式的請求。

GET和POST請求的不同之處是POST請求通常有"副作用",

它們會由于某種途徑改變系統狀态(例如送出成堆垃圾到你的門口)。

Data同樣可以通過在Get請求的URL本身上面編碼來傳送。

  1. import urllib  
  2. data = {}  
  3. data['name'] = 'WHY'    
  4. data['location'] = 'SDU'    
  5. data['language'] = 'Python'  
  6. url_values = urllib.urlencode(data)    
  7. print url_values  
  8. name=Somebody+Here&language=Python&location=Northampton    
  9. url = 'http://www.example.com/example.cgi'    
  10. full_url = url + '?' + url_values  
  11. data = urllib2.open(full_url)    

這樣就實作了Data資料的Get傳送。

2.設定Headers到http請求

有一些站點不喜歡被程式(非人為通路)通路,或者發送不同版本的内容到不同的浏覽器。

預設的urllib2把自己作為“Python-urllib/x.y”(x和y是Python主版本和次版本号,例如Python-urllib/2.7),

這個身份可能會讓站點迷惑,或者幹脆不工作。

浏覽器确認自己身份是通過User-Agent頭,當你建立了一個請求對象,你可以給他一個包含頭資料的字典。

下面的例子發送跟上面一樣的内容,但把自身模拟成Internet Explorer。

  1. url = 'http://www.someserver.com/cgi-bin/register.cgi'  
  2. user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'    
  3. headers = { 'User-Agent' : user_agent }    
  4. data = urllib.urlencode(values)    
  5. req = urllib2.Request(url, data, headers)    
  6. the_page = response.read()   

​​[Python]網絡爬蟲(三):異常的處理和HTTP狀态碼的分類​​

分類: ​​Python​​ ​​爬蟲​​2013-05-14 09:51 1358人閱讀 ​

​評論​​(5) ​​收藏​​ ​​舉報​​

先來說一說HTTP的異常處理問題。

當urlopen不能夠處理一個response時,産生urlError。

不過通常的Python APIs異常如ValueError,TypeError等也會同時産生。

HTTPError是urlError的子類,通常在特定HTTP URLs中産生。

1.URLError

通常,URLError在沒有網絡連接配接(沒有路由到特定伺服器),或者伺服器不存在的情況下産生。

這種情況下,異常同樣會帶有"reason"屬性,它是一個tuple(可以了解為不可變的數組),

包含了一個錯誤号和一個錯誤資訊。

我們建一個urllib2_test06.py來感受一下異常的處理:

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

按下F5,可以看到列印出來的内容是:

[Errno 11001] getaddrinfo failed

也就是說,錯誤号是11001,内容是getaddrinfo failed

2.HTTPError伺服器上每一個HTTP 應答對象response包含一個數字"狀态碼"。

有時狀态碼指出伺服器無法完成請求。預設的處理器會為你處理一部分這種應答。

例如:假如response是一個"重定向",需要用戶端從别的位址擷取文檔,urllib2将為你處理。

其他不能處理的,urlopen會産生一個HTTPError。

典型的錯誤包含"404"(頁面無法找到),"403"(請求禁止),和"401"(帶驗證請求)。

HTTP狀态碼表示HTTP協定所傳回的響應的狀态。

比如用戶端向伺服器發送請求,如果成功地獲得請求的資源,則傳回的狀态碼為200,表示響應成功。

如果請求的資源不存在, 則通常傳回404錯誤。 

HTTP狀态碼通常分為5種類型,分别以1~5五個數字開頭,由3位整數組成:

------------------------------------------------------------------------------------------------

200:請求成功      處理方式:獲得響應的内容,進行處理 

201:請求完成,結果是建立了新資源。新建立資源的URI可在響應的實體中得到    處理方式:爬蟲中不會遇到 

202:請求被接受,但處理尚未完成    處理方式:阻塞等待 

204:伺服器端已經實作了請求,但是沒有傳回新的信 息。如果客戶是使用者代理,則無須為此更新自身的文檔視圖。    處理方式:丢棄

300:該狀态碼不被HTTP/1.0的應用程式直接使用, 隻是作為3XX類型回應的預設解釋。存在多個可用的被請求資源。    處理方式:若程式中能夠處理,則進行進一步處理,如果程式中不能處理,則丢棄

301:請求到的資源都會配置設定一個永久的URL,這樣就可以在将來通過該URL來通路此資源    處理方式:重定向到配置設定的URL

302:請求到的資源在一個不同的URL處臨時儲存     處理方式:重定向到臨時的URL 

304 請求的資源未更新     處理方式:丢棄 

400 非法請求     處理方式:丢棄 

401 未授權     處理方式:丢棄 

403 禁止     處理方式:丢棄 

404 沒有找到     處理方式:丢棄 

5XX 回應代碼以“5”開頭的狀态碼表示伺服器端發現自己出現錯誤,不能繼續執行請求    處理方式:丢棄

HTTPError執行個體産生後會有一個整型'code'屬性,是伺服器發送的相關錯誤号。

Error Codes錯誤碼

因為預設的處理器處理了重定向(300以外号碼),并且100-299範圍的号碼訓示成功,是以你隻能看到400-599的錯誤号碼。

BaseHTTPServer.BaseHTTPRequestHandler.response是一個很有用的應答号碼字典,顯示了HTTP協定使用的所有的應答号。

當一個錯誤号産生後,伺服器傳回一個HTTP錯誤号,和一個錯誤頁面。

你可以使用HTTPError執行個體作為頁面傳回的應答對象response。

這表示和錯誤屬性一樣,它同樣包含了read,geturl,和info方法。

我們建一個urllib2_test07.py來感受一下:

  1. req = urllib2.Request('http://bbs.csdn.net/callmewhy')  
  2. try:  
  3.     urllib2.urlopen(req)  
  4. except urllib2.URLError, e:  
  5.     print e.code  
  6.     #print e.read()  

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

3.Wrapping

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

我們建一個urllib2_test08.py來示範一下第一種異常處理的方案:

  1. from urllib2 import Request, urlopen, URLError, HTTPError  
  2. req = Request('http://bbs.csdn.net/callmewhy')  
  3.     response = urlopen(req)  
  4. except HTTPError, e:  
  5.     print 'The server couldn\'t fulfill the request.'  
  6.     print 'Error code: ', e.code  
  7. except URLError, e:  
  8.     print 'We failed to reach a server.'  
  9.     print 'Reason: ', e.reason  
  10. else:  
  11.     print 'No exception was raised.'  
  12.     # everything is fine  

和其他語言相似,try之後捕獲異常并且将其内容列印出來。

這裡要注意的一點,except HTTPError 必須在第一個,否則except URLError将同樣接受到HTTPError 。

因為HTTPError是URLError的子類,如果URLError在前面它會捕捉到所有的URLError(包括HTTPError )。

我們建一個urllib2_test09.py來示範一下第二種異常處理的方案:

  1. try:    
  2.     response = urlopen(req)    
  3. except URLError, e:    
  4.     if hasattr(e, 'reason'):    
  5.         print 'We failed to reach a server.'    
  6.         print 'Reason: ', e.reason    
  7.     elif hasattr(e, 'code'):    
  8.         print 'The server couldn\'t fulfill the request.'    
  9.         print 'Error code: ', e.code    
  10. else:    
  11.     print 'No exception was raised.'    
  12.     # everything is fine  

​​[Python]網絡爬蟲(四):Opener與Handler的介紹和執行個體應用​​

分類: ​​Python​​ ​​爬蟲​​2013-05-14 15:09 1087人閱讀 ​

在開始後面的内容之前,先來解釋一下urllib2中的兩個個方法:info and geturl 

urlopen傳回的應答對象response(或者HTTPError執行個體)有兩個很有用的方法info()和geturl()

1.geturl():

這個傳回擷取的真實的URL,這個很有用,因為urlopen(或者opener對象使用的)或許會有重定向。擷取的URL或許跟請求URL不同。

以人人中的一個超級連結為例,

我們建一個urllib2_test10.py來比較一下原始URL和重定向的連結:

  1. old_url = 'http://rrurl.cn/b1UZuP'  
  2. req = Request(old_url)  
  3. response = urlopen(req)    
  4. print 'Old url :' + old_url  
  5. print 'Real url :' + response.geturl()  

運作之後可以看到真正的連結指向的網址:

【Python開發】【神經網絡與深度學習】網絡爬蟲之python實作

2.info():

這個傳回對象的字典對象,該字典描述了擷取的頁面情況。通常是伺服器發送的特定頭headers。目前是httplib.HTTPMessage 執行個體。

經典的headers包含"Content-length","Content-type",和其他内容。

我們建一個urllib2_test11.py來測試一下info的應用:

  1. old_url = 'http://www.baidu.com'  
  2. print 'Info():'  
  3. print response.info()  

運作的結果如下,可以看到頁面的相關資訊:

【Python開發】【神經網絡與深度學習】網絡爬蟲之python實作

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

1.Openers:

當你擷取一個URL你使用一個opener(一個urllib2.OpenerDirector的執行個體)。

正常情況下,我們使用預設opener:通過urlopen。

但你能夠建立個性的openers。

2.Handles:

Openers使用處理器handlers,所有的“繁重”工作由handlers處理。

每個handlers知道如何通過特定協定打開URLs,或者如何處理URL打開時的各個方面。

例如HTTP重定向或者HTTP cookies。

如果你希望用特定處理器擷取URLs你會想建立一個openers,例如擷取一個能處理cookie的opener,或者擷取一個不重定向的opener。

要建立一個 opener,可以執行個體化一個OpenerDirector,

然後調用.add_handler(some_handler_instance)。

同樣,可以使用build_opener,這是一個更加友善的函數,用來建立opener對象,他隻需要一次函數調用。

build_opener預設添加幾個處理器,但提供快捷的方法來添加或更新預設處理器。

其他的處理器handlers你或許會希望處理代理,驗證,和其他常用但有點特殊的情況。

install_opener 用來建立(全局)預設opener。這個表示調用urlopen将使用你安裝的opener。

Opener對象有一個open方法。

該方法可以像urlopen函數那樣直接用來擷取urls:通常不必調用install_opener,除了為了友善。

說完了上面兩個内容,下面我們來看一下基本認證的内容,這裡會用到上面提及的Opener和Handler。

Basic Authentication 基本驗證

為了展示建立和安裝一個handler,我們将使用HTTPBasicAuthHandler。

當需要基礎驗證時,伺服器發送一個header(401錯誤碼) 請求驗證。這個指定了scheme 和一個‘realm’,看起來像這樣:Www-authenticate: SCHEME realm="REALM".

例如

Www-authenticate: Basic realm="cPanel Users"

用戶端必須使用新的請求,并在請求頭裡包含正确的姓名和密碼。

這是“基礎驗證”,為了簡化這個過程,我們可以建立一個HTTPBasicAuthHandler的執行個體,并讓opener使用這個handler就可以啦。

HTTPBasicAuthHandler使用一個密碼管理的對象來處理URLs和realms來映射使用者名和密碼。

如果你知道realm(從伺服器發送來的頭裡)是什麼,你就能使用HTTPPasswordMgr。

通常人們不關心realm是什麼。那樣的話,就能用友善的HTTPPasswordMgrWithDefaultRealm。

這個将在你為URL指定一個預設的使用者名和密碼。

這将在你為特定realm提供一個其他組合時得到提供。

我們通過給realm參數指定None提供給add_password來訓示這種情況。

最高層次的URL是第一個要求驗證的URL。你傳給.add_password()更深層次的URLs将同樣合适。

說了這麼多廢話,下面來用一個例子示範一下上面說到的内容。

我們建一個urllib2_test12.py來測試一下info的應用:

  1. # -*- coding: utf-8 -*-  
  2. # 建立一個密碼管理者  
  3. password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()  
  4. # 添加使用者名和密碼  
  5. top_level_url = "http://example.com/foo/"  
  6. # 如果知道 realm, 我們可以使用他代替 ``None``.  
  7. # password_mgr.add_password(None, top_level_url, username, password)  
  8. password_mgr.add_password(None, top_level_url,'why', '1223')  
  9. # 建立了一個新的handler  
  10. handler = urllib2.HTTPBasicAuthHandler(password_mgr)  
  11. # 建立 "opener" (OpenerDirector 執行個體)  
  12. opener = urllib2.build_opener(handler)  
  13. a_url = 'http://www.baidu.com/'  
  14. # 使用 opener 擷取一個URL  
  15. opener.open(a_url)  
  16. # 安裝 opener.  
  17. # 現在所有調用 urllib2.urlopen 将用我們的 opener.  
  18. urllib2.install_opener(opener)  

注意:以上的例子我們僅僅提供我們的HHTPBasicAuthHandler給build_opener。

預設的openers有正常狀況的handlers:ProxyHandler,UnknownHandler,HTTPHandler,HTTPDefaultErrorHandler, HTTPRedirectHandler,FTPHandler, FileHandler, HTTPErrorProcessor。

代碼中的top_level_url 實際上可以是完整URL(包含"http:",以及主機名及可選的端口号)。

例如:http://example.com/。

也可以是一個“authority”(即主機名和可選的包含端口号)。

例如:“example.com” or “example.com:8080”。

後者包含了端口号。

​​[Python]網絡爬蟲(五):urllib2的使用細節與抓站技巧​​

分類: ​​爬蟲​​ ​​Python​​2013-05-14 16:21 1250人閱讀 ​

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

1.Proxy 的設定

urllib2 預設會使用環境變量 http_proxy 來設定 HTTP Proxy。

如果想在程式中明确控制 Proxy 而不受環境變量的影響,可以使用代理。

建立test14來實作一個簡單的代理Demo:

  1. enable_proxy = True  
  2. proxy_handler = urllib2.ProxyHandler({"http" : 'http://some-proxy.com:8080'})  
  3. null_proxy_handler = urllib2.ProxyHandler({})  
  4. if enable_proxy:  
  5.     opener = urllib2.build_opener(proxy_handler)  
  6.     opener = urllib2.build_opener(null_proxy_handler)  

這裡要注意的一個細節,使用 urllib2.install_opener() 會設定 urllib2 的全局 opener 。

這樣後面的使用會很友善,但不能做更細緻的控制,比如想在程式中使用兩個不同的 Proxy 設定等。

比較好的做法是不使用 install_opener 去更改全局的設定,而隻是直接調用 opener 的 open 方法代替全局的 urlopen 方法。

2.Timeout 設定在老版 Python 中(Python2.6前),urllib2 的 API 并沒有暴露 Timeout 的設定,要設定 Timeout 值,隻能更改 Socket 的全局 Timeout 值。

  1. import socket  
  2. socket.setdefaulttimeout(10) # 10 秒鐘後逾時  
  3. urllib2.socket.setdefaulttimeout(10) # 另一種方式  

在 Python 2.6 以後,逾時可以通過 urllib2.urlopen() 的 timeout 參數直接設定。

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

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

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

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

對有些 header 要特别留意,伺服器會針對這些 header 做檢查

User-Agent : 有些伺服器或 Proxy 會通過該值來判斷是否是浏覽器發出的請求

Content-Type : 在使用 REST 接口時,伺服器會檢查該值,用來确定 HTTP Body 中的内容該怎樣解析。常見的取值有:

application/xml : 在 XML RPC,如 RESTful/SOAP 調用時使用

application/json : 在 JSON RPC 調用時使用

application/x-www-form-urlencoded : 浏覽器送出 Web 表單時使用

在使用伺服器提供的 RESTful 或 SOAP 服務時, Content-Type 設定錯誤會導緻伺服器拒絕服務

4.Redirecturllib2 預設情況下會針對 HTTP 3XX 傳回碼自動進行 redirect 動作,無需人工配置。要檢測是否發生了 redirect 動作,隻要檢查一下 Response 的 URL 和 Request 的 URL 是否一緻就可以了。

  1. my_url = 'http://www.google.cn'  
  2. response = urllib2.urlopen(my_url)  
  3. redirected = response.geturl() == my_url  
  4. print redirected  
  5. my_url = 'http://rrurl.cn/b1UZuP'  

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

  1. class RedirectHandler(urllib2.HTTPRedirectHandler):  
  2.     def http_error_301(self, req, fp, code, msg, headers):  
  3.         print "301"  
  4.         pass  
  5.     def http_error_302(self, req, fp, code, msg, headers):  
  6.         print "303"  
  7. opener = urllib2.build_opener(RedirectHandler)  
  8. opener.open('http://rrurl.cn/b1UZuP')  

5.Cookie

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

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

運作之後就會輸出通路百度的Cookie值:

【Python開發】【神經網絡與深度學習】網絡爬蟲之python實作

6.使用 HTTP 的 PUT 和 DELETE 方法

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

  1. request = urllib2.Request(uri, data=data)  
  2. request.get_method = lambda: 'PUT' # or 'DELETE'  

7.得到 HTTP 的傳回碼

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

  1.     response = urllib2.urlopen('http://bbs.csdn.net/why')  
  2. except urllib2.HTTPError, e:  

8.Debug Log

使用 urllib2 時,可以通過下面的方法把 debug Log 打開,這樣收發包的内容就會在螢幕上列印出來,友善調試,有時可以省去抓包的工作

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

這樣就可以看到傳輸的資料包内容了:

【Python開發】【神經網絡與深度學習】網絡爬蟲之python實作

9.表單的處理

登入必要填表,表單怎麼填?

首先利用工具截取所要填表的内容。

比如我一般用firefox+httpfox插件來看看自己到底發送了些什麼包。

以verycd為例,先找到自己發的POST請求,以及POST表單項。

可以看到verycd的話需要填username,password,continueURI,fk,login_submit這幾項,其中fk是随機生成的(其實不太随機,看上去像是把epoch時間經過簡單的編碼生成的),需要從網頁擷取,也就是說得先通路一次網頁,用正規表達式等工具截取傳回資料中的fk項。continueURI顧名思義可以随便寫,login_submit是固定的,這從源碼可以看出。還有username,password那就很顯然了:

  1. postdata=urllib.urlencode({  
  2.     'username':'汪小光',  
  3.     'password':'why888',  
  4.     'continueURI':'http://www.verycd.com/',  
  5.     'fk':'',  
  6.     'login_submit':'登入'  
  7. })  
  8. req = urllib2.Request(  
  9.     url = 'http://secure.verycd.com/signin',  
  10.     data = postdata  
  11. )  
  12. result = urllib2.urlopen(req)  
  13. print result.read()   

10.僞裝成浏覽器通路

某些網站反感爬蟲的到訪,于是對爬蟲一律拒絕請求

這時候我們需要僞裝成浏覽器,這可以通過修改http包中的header來實作

  1. #…  
  2. headers = {  
  3.     'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'  
  4. }  
  5.     url = 'http://secure.verycd.com/signin/*/http://www.verycd.com/',  
  6.     data = postdata,  
  7.     headers = headers  
  8. #...  

11.對付"反盜鍊"

某些站點有所謂的反盜鍊設定,其實說穿了很簡單,

就是檢查你發送請求的header裡面,referer站點是不是他自己,

是以我們隻需要像把headers的referer改成該網站即可,以cnbeta為例:

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

headers是一個dict資料結構,你可以放入任何想要的header,來做一些僞裝。

例如,有些網站喜歡讀取header中的X-Forwarded-For來看看人家的真實IP,可以直接把X-Forwarde-For改了。

​​[Python]網絡爬蟲(六):一個簡單的百度貼吧的小爬蟲​​

分類: ​​爬蟲​​ ​​Python​​2013-05-14 21:36 1185人閱讀 ​

​評論​​(10) ​​收藏​​ ​​舉報​​

  1. #---------------------------------------  
  2. #   程式:百度貼吧爬蟲  
  3. #   版本:0.1  
  4. #   作者:why  
  5. #   日期:2013-05-14  
  6. #   語言:Python 2.7  
  7. #   操作:輸入帶分頁的位址,去掉最後面的數字,設定一下起始頁數和終點頁數。  
  8. #   功能:下載下傳對應頁碼内的所有頁面并存儲為html檔案。  
  9. import string, urllib2  
  10. #定義百度函數  
  11. def baidu_tieba(url,begin_page,end_page):     
  12.     for i in range(begin_page, end_page+1):  
  13.         sName = string.zfill(i,5) + '.html'#自動填充成六位的檔案名  
  14.         print '正在下載下傳第' + str(i) + '個網頁,并将其存儲為' + sName + '......'  
  15.         f = open(sName,'w+')  
  16.         m = urllib2.urlopen(url + str(i)).read()  
  17.         f.write(m)  
  18.         f.close()  
  19. #-------- 在這裡輸入參數 ------------------  
  20. # 這個是山東大學的百度貼吧中某一個文章的位址  
  21. #bdurl = 'http://tieba.baidu.com/p/2296017831?pn='  
  22. #iPostBegin = 1  
  23. #iPostEnd = 10  
  24. bdurl = str(raw_input(u'請輸入貼吧的位址,去掉pn=後面的數字:\n'))  
  25. begin_page = int(raw_input(u'請輸入開始的頁數:\n'))  
  26. end_page = int(raw_input(u'請輸入終點的頁數:\n'))  
  27. #調用  
  28. baidu_tieba(bdurl,begin_page,end_page)  

​​[Python]網絡爬蟲(七):Python中的正規表達式教程​​

分類: ​​爬蟲​​ ​​Python​​2013-05-15 13:29 1212人閱讀 ​

目錄​​(?)​​​​[+]​​

接下來準備用糗百做一個爬蟲的小例子。

但是在這之前,先詳細的整理一下Python中的正規表達式的相關内容。

正規表達式在Python爬蟲中的作用就像是老師點名時用的花名冊一樣,是必不可少的神兵利器。

一、 正規表達式基礎

1.1.概念介紹

正規表達式是用于處理字元串的強大工具,它并不是Python的一部分。

其他程式設計語言中也有正規表達式的概念,差別隻在于不同的程式設計語言實作支援的文法數量不同。

它擁有自己獨特的文法以及一個獨立的處理引擎,在提供了正規表達式的語言裡,正規表達式的文法都是一樣的。

下圖展示了使用正規表達式進行比對的流程:

【Python開發】【神經網絡與深度學習】網絡爬蟲之python實作

正規表達式的大緻比對過程是:

1.依次拿出表達式和文本中的字元比較,

2.如果每一個字元都能比對,則比對成功;一旦有比對不成功的字元則比對失敗。

3.如果表達式中有量詞或邊界,這個過程會稍微有一些不同。

下圖列出了Python支援的正規表達式元字元和文法:   

【Python開發】【神經網絡與深度學習】網絡爬蟲之python實作

1.2. 數量詞的貪婪模式與非貪婪模式

正規表達式通常用于在文本中查找比對的字元串。

貪婪模式,總是嘗試比對盡可能多的字元;

非貪婪模式則相反,總是嘗試比對盡可能少的字元。

Python裡數量詞預設是貪婪的。

例如:正規表達式"ab*"如果用于查找"abbbc",将找到"abbb"。

而如果使用非貪婪的數量詞"ab*?",将找到"a"。

1.3. 反斜杠的問題

與大多數程式設計語言相同,正規表達式裡使用"\"作為轉義字元,這就可能造成反斜杠困擾。

假如你需要比對文本中的字元"\",那麼使用程式設計語言表示的正規表達式裡将需要4個反斜杠"\\\\":

第一個和第三個用于在程式設計語言裡将第二個和第四個轉義成反斜杠,

轉換成兩個反斜杠\\後再在正規表達式裡轉義成一個反斜杠用來比對反斜杠\。

這樣顯然是非常麻煩的。

Python裡的原生字元串很好地解決了這個問題,這個例子中的正規表達式可以使用r"\\"表示。

同樣,比對一個數字的"\\d"可以寫成r"\d"。

有了原生字元串,媽媽再也不用擔心我的反斜杠問題~

二、 介紹re子產品

2.1.  Compile

Python通過re子產品提供對正規表達式的支援。

使用re的一般步驟是:

Step1:先将正規表達式的字元串形式編譯為Pattern執行個體。

Step2:然後使用Pattern執行個體處理文本并獲得比對結果(一個Match執行個體)。

Step3:最後使用Match執行個體獲得資訊,進行其他的操作。

我們建立一個re01.py來試驗一下re的應用:

  1. #一個簡單的re執行個體,比對字元串中的hello字元串  
  2. #導入re子產品  
  3. import re  
  4. # 将正規表達式編譯成Pattern對象,注意hello前面的r的意思是“原生字元串”  
  5. pattern = re.compile(r'hello')  
  6. # 使用Pattern比對文本,獲得比對結果,無法比對時将傳回None  
  7. match1 = pattern.match('hello world!')  
  8. match2 = pattern.match('helloo world!')  
  9. match3 = pattern.match('helllo world!')  
  10. #如果match1比對成功  
  11. if match1:  
  12.     # 使用Match獲得分組資訊  
  13.     print match1.group()  
  14.     print 'match1比對失敗!'  
  15. #如果match2比對成功  
  16. if match2:  
  17.     print match2.group()  
  18.     print 'match2比對失敗!'  
  19. #如果match3比對成功  
  20. if match3:  
  21.     print match3.group()  
  22.     print 'match3比對失敗!'  

可以看到控制台輸出了比對的三個結果:

【Python開發】【神經網絡與深度學習】網絡爬蟲之python實作

下面來具體看看代碼中的關鍵方法。

★ re.compile(strPattern[, flag]):

這個方法是Pattern類的工廠方法,用于将字元串形式的正規表達式編譯為Pattern對象。

第二個參數flag是比對模式,取值可以使用按位或運算符'|'表示同時生效,比如re.I | re.M。

另外,你也可以在regex字元串中指定模式,

比如re.compile('pattern', re.I | re.M)與re.compile('(?im)pattern')是等價的。

可選值有:

  •     re.I(全拼:IGNORECASE): 忽略大小寫(括号内是完整寫法,下同)
  •    re.M(全拼:MULTILINE): 多行模式,改變'^'和'$'的行為(參見上圖)
  •     re.S(全拼:DOTALL): 點任意比對模式,改變'.'的行為
  •     re.L(全拼:LOCALE): 使預定字元類 \w \W \b \B \s \S 取決于目前區域設定
  •     re.U(全拼:UNICODE): 使預定字元類 \w \W \b \B \s \S \d \D 取決于unicode定義的字元屬性
  •     re.X(全拼:VERBOSE): 詳細模式。這個模式下正規表達式可以是多行,忽略空白字元,并可以加入注釋。

以下兩個正規表達式是等價的:

  1. #兩個等價的re比對,比對一個小數  
  2. a = re.compile(r"""\d +  # the integral part 
  3.                    \.    # the decimal point 
  4.                    \d *  # some fractional digits""", re.X)  
  5. b = re.compile(r"\d+\.\d*")  
  6. match11 = a.match('3.1415')  
  7. match12 = a.match('33')  
  8. match21 = b.match('3.1415')  
  9. match22 = b.match('33')   
  10. if match11:  
  11.     print match11.group()  
  12.     print u'match11不是小數'  
  13. if match12:  
  14.     print match12.group()  
  15.     print u'match12不是小數'  
  16. if match21:  
  17.     print match21.group()  
  18.     print u'match21不是小數'  
  19. if match22:  
  20.     print match22.group()  
  21.     print u'match22不是小數'  

re提供了衆多子產品方法用于完成正規表達式的功能。

這些方法可以使用Pattern執行個體的相應方法替代,唯一的好處是少寫一行re.compile()代碼,

但同時也無法複用編譯後的Pattern對象。

這些方法将在Pattern類的執行個體方法部分一起介紹。

如一開始的hello執行個體可以簡寫為:

[html] ​​view plain​

  1. m = re.match(r'hello', 'hello world!')  
  2. print m.group()  

re子產品還提供了一個方法escape(string),用于将string中的正規表達式元字元如*/+/?等之前加上轉義符再傳回

2.2. Match

Match對象是一次比對的結果,包含了很多關于此次比對的資訊,可以使用Match提供的可讀屬性或方法來擷取這些資訊。

屬性:

  1. string: 比對時使用的文本。
  2. re: 比對時使用的Pattern對象。
  3. pos: 文本中正規表達式開始搜尋的索引。值與Pattern.match()和Pattern.seach()方法的同名參數相同。
  4. endpos: 文本中正規表達式結束搜尋的索引。值與Pattern.match()和Pattern.seach()方法的同名參數相同。
  5. lastindex: 最後一個被捕獲的分組在文本中的索引。如果沒有被捕獲的分組,将為None。
  6. lastgroup: 最後一個被捕獲的分組的别名。如果這個分組沒有别名或者沒有被捕獲的分組,将為None。

方法:

  1. group([group1, …]):

    獲得一個或多個分組截獲的字元串;指定多個參數時将以元組形式傳回。group1可以使用編号也可以使用别名;編号0代表整個比對的子串;不填寫參數時,傳回group(0);沒有截獲字元串的組傳回None;截獲了多次的組傳回最後一次截獲的子串。

  2. groups([default]): 

    以元組形式傳回全部分組截獲的字元串。相當于調用group(1,2,…last)。default表示沒有截獲字元串的組以這個值替代,預設為None。

  3. groupdict([default]):

    傳回以有别名的組的别名為鍵、以該組截獲的子串為值的字典,沒有别名的組不包含在内。default含義同上。

  4. start([group]): 

    傳回指定的組截獲的子串在string中的起始索引(子串第一個字元的索引)。group預設值為0。

  5. end([group]):

    傳回指定的組截獲的子串在string中的結束索引(子串最後一個字元的索引+1)。group預設值為0。

  6. span([group]):

    傳回(start(group), end(group))。

  7. expand(template): 

    将比對到的分組代入template中然後傳回。template中可以使用\id或\g<id>、\g<name>引用分組,但不能使用編号0。\id與\g<id>是等價的;但\10将被認為是第10個分組,如果你想表達\1之後是字元'0',隻能使用\g<1>0。

下面來用一個py執行個體輸出所有的内容加深了解:

  1. #一個簡單的match執行個體  
  2. # 比對如下内容:單詞+空格+單詞+任意字元  
  3. m = re.match(r'(\w+) (\w+)(?P<sign>.*)', 'hello world!')  
  4. print "m.string:", m.string  
  5. print "m.re:", m.re  
  6. print "m.pos:", m.pos  
  7. print "m.endpos:", m.endpos  
  8. print "m.lastindex:", m.lastindex  
  9. print "m.lastgroup:", m.lastgroup  
  10. print "m.group():", m.group()  
  11. print "m.group(1,2):", m.group(1, 2)  
  12. print "m.groups():", m.groups()  
  13. print "m.groupdict():", m.groupdict()  
  14. print "m.start(2):", m.start(2)  
  15. print "m.end(2):", m.end(2)  
  16. print "m.span(2):", m.span(2)  
  17. print r"m.expand(r'\g<2> \g<1>\g<3>'):", m.expand(r'\2 \1\3')  
  18. ### output ###  
  19. # m.string: hello world!  
  20. # m.re: <_sre.SRE_Pattern object at 0x016E1A38>  
  21. # m.pos: 0  
  22. # m.endpos: 12  
  23. # m.lastindex: 3  
  24. # m.lastgroup: sign  
  25. # m.group(1,2): ('hello', 'world')  
  26. # m.groups(): ('hello', 'world', '!')  
  27. # m.groupdict(): {'sign': '!'}  
  28. # m.start(2): 6  
  29. # m.end(2): 11  
  30. # m.span(2): (6, 11)  
  31. # m.expand(r'\2 \1\3'): world hello!  

2.3. Pattern

Pattern對象是一個編譯好的正規表達式,通過Pattern提供的一系列方法可以對文本進行比對查找。

Pattern不能直接執行個體化,必須使用re.compile()進行構造,也就是re.compile()傳回的對象。

Pattern提供了幾個可讀屬性用于擷取表達式的相關資訊:

  1. pattern: 編譯時用的表達式字元串。
  2. flags: 編譯時用的比對模式。數字形式。
  3. groups: 表達式中分組的數量。
  4. groupindex: 以表達式中有别名的組的别名為鍵、以該組對應的編号為值的字典,沒有别名的組不包含在内。

可以用下面這個例子檢視pattern的屬性:

  1. #一個簡單的pattern執行個體  
  2. p = re.compile(r'(\w+) (\w+)(?P<sign>.*)', re.DOTALL)  
  3. print "p.pattern:", p.pattern  
  4. print "p.flags:", p.flags  
  5. print "p.groups:", p.groups  
  6. print "p.groupindex:", p.groupindex  
  7. # p.pattern: (\w+) (\w+)(?P<sign>.*)  
  8. # p.flags: 16  
  9. # p.groups: 3  
  10. # p.groupindex: {'sign': 3}  

下面重點介紹一下pattern的執行個體方法及其使用。

1.match

match(string[, pos[, endpos]]) | re.match(pattern, string[, flags]):

這個方法将從string的pos下标處起嘗試比對pattern;

如果pattern結束時仍可比對,則傳回一個Match對象;

如果比對過程中pattern無法比對,或者比對未結束就已到達endpos,則傳回None。

pos和endpos的預設值分别為0和len(string);

re.match()無法指定這兩個參數,參數flags用于編譯pattern時指定比對模式。

注意:這個方法并不是完全比對。

當pattern結束時若string還有剩餘字元,仍然視為成功。

想要完全比對,可以在表達式末尾加上邊界比對符'$'。

下面來看一個Match的簡單案例:

  1. # encoding: UTF-8  
  2. # 将正規表達式編譯成Pattern對象  
  3. match = pattern.match('hello world!')  
  4. if match:  
  5.     print match.group()  
  6. ### 輸出 ###  
  7. # hello  

2.search

search(string[, pos[, endpos]]) | re.search(pattern, string[, flags]): 

這個方法用于查找字元串中可以比對成功的子串。

從string的pos下标處起嘗試比對pattern,

若無法比對,則将pos加1後重新嘗試比對;

直到pos=endpos時仍無法比對則傳回None。

pos和endpos的預設值分别為0和len(string));

re.search()無法指定這兩個參數,參數flags用于編譯pattern時指定比對模式。

那麼它和match有什麼差別呢?

match()函數隻檢測re是不是在string的開始位置比對,

search()會掃描整個string查找比對,

match()隻有在0位置比對成功的話才有傳回,如果不是開始位置比對成功的話,match()就傳回none

例如:

print(re.match(‘super’, ‘superstition’).span())

會傳回(0, 5)

print(re.match(‘super’, ‘insuperable’))

則傳回None

search()會掃描整個字元串并傳回第一個成功的比對

print(re.search(‘super’, ‘superstition’).span())

傳回(0, 5)

print(re.search(‘super’, ‘insuperable’).span())

傳回(2, 7)

看一個search的執行個體:

  1. #一個簡單的search執行個體  
  2. pattern = re.compile(r'world')  
  3. # 使用search()查找比對的子串,不存在能比對的子串時将傳回None  
  4. # 這個例子中使用match()無法成功比對  
  5. match = pattern.search('hello world!')  
  6. # world  

3.split

split(string[, maxsplit]) | re.split(pattern, string[, maxsplit]):

按照能夠比對的子串将string分割後傳回清單。

maxsplit用于指定最大分割次數,不指定将全部分割。

  1. p = re.compile(r'\d+')  
  2. print p.split('one1two2three3four4')  
  3. # ['one', 'two', 'three', 'four', '']  

4.findall

findall(string[, pos[, endpos]]) | re.findall(pattern, string[, flags]):

搜尋string,以清單形式傳回全部能比對的子串。

  1. print p.findall('one1two2three3four4')  
  2. # ['1', '2', '3', '4']  

5.finditer

finditer(string[, pos[, endpos]]) | re.finditer(pattern, string[, flags]):

搜尋string,傳回一個順序通路每一個比對結果(Match對象)的疊代器。

  1. for m in p.finditer('one1two2three3four4'):  
  2.     print m.group(),  
  3. # 1 2 3 4  

6.sub

sub(repl, string[, count]) | re.sub(pattern, repl, string[, count]):

使用repl替換string中每一個比對的子串後傳回替換後的字元串。 

當repl是一個字元串時,可以使用\id或\g<id>、\g<name>引用分組,但不能使用編号0。 

當repl是一個方法時,這個方法應當隻接受一個參數(Match對象),并傳回一個字元串用于替換(傳回的字元串中不能再引用分組)。 

count用于指定最多替換次數,不指定時全部替換。

  1. p = re.compile(r'(\w+) (\w+)')  
  2. s = 'i say, hello world!'  
  3. print p.sub(r'\2 \1', s)  
  4. def func(m):  
  5.     return m.group(1).title() + ' ' + m.group(2).title()  
  6. print p.sub(func, s)  
  7. # say i, world hello!  
  8. # I Say, Hello World!  

7.subn

subn(repl, string[, count]) |re.sub(pattern, repl, string[, count]):

傳回 (sub(repl, string[, count]), 替換次數)。

  1. print p.subn(r'\2 \1', s)  
  2. print p.subn(func, s)  
  3. # ('say i, world hello!', 2)  
  4. # ('I Say, Hello World!', 2)  

至此,Python的正規表達式基本介紹就算是完成了^_^

​​[Python]網絡爬蟲(八):糗事百科的網絡爬蟲(v0.2)源碼及解析​​

分類: ​​Python​​ ​​爬蟲​​2013-05-15 20:59 1208人閱讀 ​

​評論​​(7) ​​收藏​​ ​​舉報​​

項目内容:

用Python寫的糗事百科的網絡爬蟲。

使用方法:

建立一個Bug.py檔案,然後将代碼複制到裡面後,輕按兩下運作。

程式功能:

在指令提示行中浏覽糗事百科。

原了解釋:

首先,先浏覽一下糗事百科的首頁:​​http://www.qiushibaike.com/hot/page/1​​

可以看出來,連結中page/後面的數字就是對應的頁碼,記住這一點為以後的編寫做準備。

然後,右擊檢視頁面源碼:

【Python開發】【神經網絡與深度學習】網絡爬蟲之python實作

觀察發現,每一個段子都用div标記,其中class必為content,title是發帖時間,我們隻需要用正規表達式将其“扣”出來就可以了。

明白了原理之後,剩下的就是正規表達式的内容了,可以參照這篇博文:

​​javascript:void(0)​​

運作效果:

【Python開發】【神經網絡與深度學習】網絡爬蟲之python實作

​​

  1. #   程式:糗百爬蟲  
  2. #   版本:0.2  
  3. #   日期:2013-05-15  
  4. #   操作:輸入quit退出閱讀糗事百科  
  5. #   功能:按下回車依次浏覽今日的糗百熱點  
  6. #   更新:解決了指令提示行下亂碼的問題  
  7. import thread  
  8. import time  
  9. #----------- 處理頁面上的各種标簽 -----------  
  10. class HTML_Tool:  
  11.     # 用非 貪婪模式 比對 \t 或者 \n 或者 空格 或者 超連結 或者 圖檔  
  12.     BgnCharToNoneRex = re.compile("(\t|\n| |<a.*?>|<img.*?>)")  
  13.     # 用非 貪婪模式 比對 任意<>标簽  
  14.     EndCharToNoneRex = re.compile("<.*?>")  
  15.     # 用非 貪婪模式 比對 任意<p>标簽  
  16.     BgnPartRex = re.compile("<p.*?>")  
  17.     CharToNewLineRex = re.compile("(<br/>|</p>|<tr>|<div>|</div>)")  
  18.     CharToNextTabRex = re.compile("<td>")  
  19.     # 将一些html的符号實體轉變為原始符号  
  20.     replaceTab = [("<","<"),(">",">"),("&","&"),("&","\""),(" "," ")]  
  21.     def Replace_Char(self,x):  
  22.         x = self.BgnCharToNoneRex.sub("",x)  
  23.         x = self.BgnPartRex.sub("\n    ",x)  
  24.         x = self.CharToNewLineRex.sub("\n",x)  
  25.         x = self.CharToNextTabRex.sub("\t",x)  
  26.         x = self.EndCharToNoneRex.sub("",x)  
  27.         for t in self.replaceTab:  
  28.             x = x.replace(t[0],t[1])  
  29.         return x  
  30. #----------- 加載處理糗事百科 -----------  
  31. class HTML_Model:  
  32.     def __init__(self):  
  33.         self.page = 1  
  34.         self.pages = []  
  35.         self.myTool = HTML_Tool()  
  36.         self.enable = False  
  37.     # 将所有的段子都扣出來,添加到清單中并且傳回清單  
  38.     def GetPage(self,page):  
  39.         myUrl = "http://m.qiushibaike.com/hot/page/" + page  
  40.         myResponse  = urllib2.urlopen(myUrl)  
  41.         myPage = myResponse.read()  
  42.         #encode的作用是将unicode編碼轉換成其他編碼的字元串  
  43.         #decode的作用是将其他編碼的字元串轉換成unicode編碼  
  44.         unicodePage = myPage.decode("utf-8")  
  45.         # 找出所有class="content"的div标記  
  46.         #re.S是任意比對模式,也就是.可以比對換行符  
  47.         myItems = re.findall('<div.*?class="content".*?title="(.*?)">(.*?)</div>',unicodePage,re.S)  
  48.         items = []  
  49.         for item in myItems:  
  50.             # item 中第一個是div的标題,也就是時間  
  51.             # item 中第二個是div的内容,也就是内容  
  52.             items.append([item[0].replace("\n",""),item[1].replace("\n","")])  
  53.         return items  
  54.     # 用于加載新的段子  
  55.     def LoadPage(self):  
  56.         # 如果使用者未輸入quit則一直運作  
  57.         while self.enable:  
  58.             # 如果pages數組中的内容小于2個  
  59.             if len(self.pages) < 2:  
  60.                 try:  
  61.                     # 擷取新的頁面中的段子們  
  62.                     myPage = self.GetPage(str(self.page))  
  63.                     self.page += 1  
  64.                     self.pages.append(myPage)  
  65.                 except:  
  66.                     print '無法連結糗事百科!'  
  67.             else:  
  68.                 time.sleep(1)  
  69.     def ShowPage(self,q,page):  
  70.         for items in q:  
  71.             print u'第%d頁' % page , items[0]  
  72.             print self.myTool.Replace_Char(items[1])  
  73.             myInput = raw_input()  
  74.             if myInput == "quit":  
  75.                 self.enable = False  
  76.                 break  
  77.     def Start(self):  
  78.         self.enable = True  
  79.         page = self.page  
  80.         print u'正在加載中請稍候......'  
  81.         # 建立一個線程在背景加載段子并存儲  
  82.         thread.start_new_thread(self.LoadPage,())  
  83.         #----------- 加載處理糗事百科 -----------  
  84.             # 如果self的page數組中存有元素  
  85.             if self.pages:  
  86.                 nowPage = self.pages[0]  
  87.                 del self.pages[0]  
  88.                 self.ShowPage(nowPage,page)  
  89.                 page += 1  
  90. #----------- 程式的入口處 -----------  
  91. print u""" 
  92. --------------------------------------- 
  93.    程式:糗百爬蟲 
  94.    版本:0.1 
  95.    作者:why 
  96.    日期:2013-05-15 
  97.    語言:Python 2.7 
  98.    操作:輸入quit退出閱讀糗事百科 
  99.    功能:按下回車依次浏覽今日的糗百熱點 
  100. """  
  101. print u'請按下回車浏覽今日的糗百内容:'  
  102. raw_input(' ')  
  103. myModel = HTML_Model()  
  104. myModel.Start() 

​​[Python]網絡爬蟲(九):百度貼吧的網絡爬蟲(v0.4)源碼及解析​​

分類: ​​爬蟲​​ ​​Python​​2013-05-16 13:48 1361人閱讀 ​

百度貼吧的爬蟲制作和糗百的爬蟲制作原理基本相同,都是通過檢視源碼扣出關鍵資料,然後将其存儲到本地txt檔案。

用Python寫的百度貼吧的網絡爬蟲。

建立一個BugBaidu.py檔案,然後将代碼複制到裡面後,輕按兩下運作。

将貼吧中樓主釋出的内容打包txt存儲到本地。

首先,先浏覽一下某一條貼吧,點選隻看樓主并點選第二頁之後url發生了一點變化,變成了:

http://tieba.baidu.com/p/2296712428?see_lz=1&pn=1

可以看出來,see_lz=1是隻看樓主,pn=1是對應的頁碼,記住這一點為以後的編寫做準備。

這就是我們需要利用的url。

接下來就是檢視頁面源碼。

首先把題目摳出來存儲檔案的時候會用到。

可以看到百度使用gbk編碼,标題使用h1标記:

  1. <h1 class="core_title_txt" title="【原創】時尚首席(關于時尚,名利,事業,愛情,勵志)">【原創】時尚首席(關于時尚,名利,事業,愛情,勵志)</h1>  

同樣,正文部分用div和class綜合标記,接下來要做的隻是用正規表達式來比對即可。

運作截圖:

【Python開發】【神經網絡與深度學習】網絡爬蟲之python實作

生成的txt檔案:

【Python開發】【神經網絡與深度學習】網絡爬蟲之python實作
  1. #   版本:0.5  
  2. #   日期:2013-05-16  
  3. #   操作:輸入網址後自動隻看樓主并儲存到本地檔案  
  4. #   功能:将樓主釋出的内容打包txt存儲到本地。  
  5. import string  
  6.         for t in self.replaceTab:    
  7.             x = x.replace(t[0],t[1])    
  8.         return x    
  9. class Baidu_Spider:  
  10.     # 申明相關的屬性  
  11.     def __init__(self,url):    
  12.         self.myUrl = url + '?see_lz=1'  
  13.         self.datas = []  
  14.         print u'已經啟動百度貼吧爬蟲,咔嚓咔嚓'  
  15.     # 初始化加載頁面并将其轉碼儲存  
  16.     def baidu_tieba(self):  
  17.         # 讀取頁面的原始資訊并将其從gbk轉碼  
  18.         myPage = urllib2.urlopen(self.myUrl).read().decode("gbk")  
  19.         # 計算樓主釋出内容一共有多少頁  
  20.         endPage = self.page_counter(myPage)  
  21.         # 擷取該帖的标題  
  22.         title = self.find_title(myPage)  
  23.         print u'文章名稱:' + title  
  24.         # 擷取最終的資料  
  25.         self.save_data(self.myUrl,title,endPage)  
  26.     #用來計算一共有多少頁  
  27.     def page_counter(self,myPage):  
  28.         # 比對 "共有<span class="red">12</span>頁" 來擷取一共有多少頁  
  29.         myMatch = re.search(r'class="red">(\d+?)</span>', myPage, re.S)  
  30.         if myMatch:    
  31.             endPage = int(myMatch.group(1))  
  32.             print u'爬蟲報告:發現樓主共有%d頁的原創内容' % endPage  
  33.         else:  
  34.             endPage = 0  
  35.             print u'爬蟲報告:無法計算樓主釋出内容有多少頁!'  
  36.         return endPage  
  37.     # 用來尋找該帖的标題  
  38.     def find_title(self,myPage):  
  39.         # 比對 <h1 class="core_title_txt" title="">xxxxxxxxxx</h1> 找出标題  
  40.         myMatch = re.search(r'<h1.*?>(.*?)</h1>', myPage, re.S)  
  41.         title = u'暫無标題'  
  42.         if myMatch:  
  43.             title  = myMatch.group(1)  
  44.             print u'爬蟲報告:無法加載文章标題!'  
  45.         # 檔案名不能包含以下字元: \ / : * ? " < > |  
  46.         title = title.replace('\\','').replace('/','').replace(':','').replace('*','').replace('?','').replace('"','').replace('>','').replace('<','').replace('|','')  
  47.         return title  
  48.     # 用來存儲樓主釋出的内容  
  49.     def save_data(self,url,title,endPage):  
  50.         # 加載頁面資料到數組中  
  51.         self.get_data(url,endPage)  
  52.         # 打開本地檔案  
  53.         f = open(title+'.txt','w+')  
  54.         f.writelines(self.datas)  
  55.         print u'爬蟲報告:檔案已下載下傳到本地并打包成txt檔案'  
  56.         print u'請按任意鍵退出...'  
  57.         raw_input();  
  58.     # 擷取頁面源碼并将其存儲到數組中  
  59.     def get_data(self,url,endPage):  
  60.         url = url + '&pn='  
  61.         for i in range(1,endPage+1):  
  62.             print u'爬蟲報告:爬蟲%d号正在加載中...' % i  
  63.             myPage = urllib2.urlopen(url + str(i)).read()  
  64.             # 将myPage中的html代碼處理并存儲到datas裡面  
  65.             self.deal_data(myPage.decode('gbk'))  
  66.     # 将内容從頁面代碼中摳出來  
  67.     def deal_data(self,myPage):  
  68.         myItems = re.findall('id="post_content.*?>(.*?)</div>',myPage,re.S)  
  69.             data = self.myTool.Replace_Char(item.replace("\n","").encode('gbk'))  
  70.             self.datas.append(data+'\n')  
  71. #-------- 程式入口處 ------------------  
  72. print u"""#--------------------------------------- 
  73. #   程式:百度貼吧爬蟲 
  74. #   版本:0.5 
  75. #   作者:why 
  76. #   日期:2013-05-16 
  77. #   語言:Python 2.7 
  78. #   操作:輸入網址後自動隻看樓主并儲存到本地檔案 
  79. #   功能:将樓主釋出的内容打包txt存儲到本地。 
  80. #--------------------------------------- 
  81. # 以某小說貼吧為例子  
  82. # bdurl = 'http://tieba.baidu.com/p/2296712428?see_lz=1&pn=1'  
  83. print u'請輸入貼吧的位址最後的數字串:'  
  84. bdurl = 'http://tieba.baidu.com/p/' + str(raw_input(u'http://tieba.baidu.com/p/'))   
  85. mySpider = Baidu_Spider(bdurl)  
  86. mySpider.baidu_tieba()