1、網絡爬蟲的定義html
網絡爬蟲,即Web Spider,是一個很形象的名字。把互聯網比喻成一個蜘蛛網,那麼Spider就是在網上爬來爬去的蜘蛛。網絡蜘蛛是經過網頁的連接地址來尋找網頁的。python
從網站某一個頁面(一般是首頁)開始,讀取網頁的內容,找到在網頁中的其它連接地址,而後經過這些連接地址尋找下一個網頁,這樣一直循環下去,直到把這個網站全部的網頁git
都抓取完爲止。若是把整個互聯網當成一個網站,那麼網絡蜘蛛就能夠用這個原理把互聯網上全部的網頁都抓取下來。web
這樣看來,網絡爬蟲就是一個爬行程序,一個抓取網頁的程序。正則表達式
網絡爬蟲的基本操做是抓取網頁。編程
那麼如何才能爲所欲爲地得到本身想要的頁面?json
咱們先從URL開始。數組
2、瀏覽網頁的過程瀏覽器
抓取網頁的過程其實和讀者平時使用IE瀏覽器瀏覽網頁的道理是同樣的。服務器
好比說你在瀏覽器的地址欄中輸入 www.baidu.com 這個地址。
打開網頁的過程其實就是瀏覽器做爲一個瀏覽的「客戶端」,向服務器端發送了一次請求,把服務器端的文件「抓」到本地,再進行解釋、展示。
HTML是一種標記語言,用標籤標記內容並加以解析和區分。
瀏覽器的功能是將獲取到的HTML代碼進行解析,而後將原始的代碼轉變成咱們直接看到的網站頁面。
3、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」訪問。
4、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對理解網絡爬蟲相當重要
分類: 爬蟲 Python2013-05-13 23:45 1628人閱讀
所謂網頁抓取,就是把URL地址中指定的網絡資源從網絡流中讀取出來,保存到本地。
相似於使用程序模擬IE瀏覽器的功能,把URL做爲HTTP請求的內容發送到服務器端, 而後讀取服務器端的響應資源。
在Python中,咱們使用urllib2這個組件來抓取網頁。
urllib2是Python的一個獲取URLs(Uniform Resource Locators)的組件。
它以urlopen函數的形式提供了一個很是簡單的接口。
最簡單的urllib2的應用代碼只須要四行。
在pythono3以上的版本,urllib2已經被整合到標準庫中,因此須要import urllib.request
咱們新建一個文件urllib2_test01.py來感覺一下urllib2的做用:
- import urllib.request
- response = urllib.request.urlopen('http://www.baidu.com/')
- html = response.read()
- print(html)
按下F5能夠看到運行的結果:
咱們能夠打開百度主頁,右擊,選擇查看源代碼(火狐OR谷歌瀏覽器都可),會發現也是徹底同樣的內容。
也就是說,上面這四行代碼將咱們訪問百度時瀏覽器收到的代碼們所有打印了出來。
這就是一個最簡單的urllib2的例子。
除了"http:",URL一樣可使用"ftp:","file:"等等來替代。
HTTP是基於請求和應答機制的:
客戶端提出請求,服務端提供應答。
urllib2用一個Request對象來映射你提出的HTTP請求。
在它最簡單的使用形式中你將用你要請求的地址建立一個Request對象,
經過調用urlopen並傳入Request對象,將返回一個相關請求response對象,
這個應答對象如同一個文件對象,因此你能夠在Response中調用.read()。
咱們新建一個文件urllib2_test02.py來感覺一下:
- import urllib2
- req = urllib2.Request('http://www.baidu.com')
- response = urllib2.urlopen(req)
- the_page = response.read()
- print the_page
能夠看到輸出的內容和test01是同樣的。
urllib2使用相同的接口處理全部的URL頭。例如你能夠像下面那樣建立一個ftp請求。
- 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來感覺一下:
- import urllib
- import urllib.request
-
- url = 'http://www.someserver.com/register.cgi'
-
- values = {'name' : 'WHY',
- 'location' : 'SDU',
- 'language' : 'Python' }
-
- data = urllib.urlencode(values)
- req = urllib.request(url, data)
- response = urllib.request.urlopen(req)
- the_page = response.read()
若是沒有傳送data參數,urllib2使用GET方式的請求。
GET和POST請求的不一樣之處是POST請求一般有"反作用",
它們會因爲某種途徑改變系統狀態(例如提交成堆垃圾到你的門口)。
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)
這樣就實現了Data數據的Get傳送。
2.設置Headers到http請求
有一些站點不喜歡被程序(非人爲訪問)訪問,或者發送不一樣版本的內容到不一樣的瀏覽器。
默認的urllib2把本身做爲「Python-urllib/x.y」(x和y是Python主版本和次版本號,例如Python-urllib/2.7),
這個身份可能會讓站點迷惑,或者乾脆不工做。
瀏覽器確認本身身份是經過User-Agent頭,當你建立了一個請求對象,你能夠給他一個包含頭數據的字典。
下面的例子發送跟上面同樣的內容,但把自身模擬成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()
分類: Python 爬蟲2013-05-14 09:51 1358人閱讀
先來講一說HTTP的異常處理問題。
當urlopen不可以處理一個response時,產生urlError。
不過一般的Python APIs異常如ValueError,TypeError等也會同時產生。
HTTPError是urlError的子類,一般在特定HTTP URLs中產生。
1.URLError
一般,URLError在沒有網絡鏈接(沒有路由到特定服務器),或者服務器不存在的狀況下產生。
這種狀況下,異常一樣會帶有"reason"屬性,它是一個tuple(能夠理解爲不可變的數組),
包含了一個錯誤號和一個錯誤信息。
咱們建一個urllib2_test06.py來感覺一下異常的處理:
- import urllib2
-
- req = urllib2.Request('http://www.baibai.com')
-
- try: urllib2.urlopen(req)
-
- except urllib2.URLError, e:
- 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來感覺一下:
- import urllib2
- req = urllib2.Request('http://bbs.csdn.net/callmewhy')
-
- try:
- urllib2.urlopen(req)
-
- except urllib2.URLError, e:
-
- print e.code
-
按下F5能夠看見輸出了404的錯誤碼,也就說沒有找到這個頁面。
3.Wrapping
因此若是你想爲HTTPError或URLError作準備,將有兩個基本的辦法。推薦使用第二種。
咱們建一個urllib2_test08.py來示範一下第一種異常處理的方案:
- 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.'
-
和其餘語言類似,try以後捕獲異常而且將其內容打印出來。
這裏要注意的一點,except HTTPError 必須在第一個,不然except URLError將一樣接受到HTTPError 。
由於HTTPError是URLError的子類,若是URLError在前面它會捕捉到全部的URLError(包括HTTPError )。
咱們建一個urllib2_test09.py來示範一下第二種異常處理的方案:
- 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.'
-
分類: 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和重定向的連接:
- 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()
運行以後能夠看到真正的連接指向的網址:
2.info():
這個返回對象的字典對象,該字典描述了獲取的頁面狀況。一般是服務器發送的特定頭headers。目前是httplib.HTTPMessage 實例。
經典的headers包含"Content-length","Content-type",和其餘內容。
咱們建一個urllib2_test11.py來測試一下info的應用:
- 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()
運行的結果以下,能夠看到頁面的相關信息:
下面來講一說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的應用:
- import urllib2
-
- password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
-
-
- top_level_url = "http://example.com/foo/"
-
- password_mgr.add_password(None, top_level_url,'why', '1223')
-
- handler = urllib2.HTTPBasicAuthHandler(password_mgr)
-
- opener = urllib2.build_opener(handler)
-
- a_url = 'http://www.baidu.com/'
-
- opener.open(a_url)
-
- 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 爬蟲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和重定向的連接:
- 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()
運行以後能夠看到真正的連接指向的網址:
2.info():
這個返回對象的字典對象,該字典描述了獲取的頁面狀況。一般是服務器發送的特定頭headers。目前是httplib.HTTPMessage 實例。
經典的headers包含"Content-length","Content-type",和其餘內容。
咱們建一個urllib2_test11.py來測試一下info的應用:
- 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()
運行的結果以下,能夠看到頁面的相關信息:
下面來講一說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的應用:
- import urllib2
-
- password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
-
-
- top_level_url = "http://example.com/foo/"
-
- password_mgr.add_password(None, top_level_url,'why', '1223')
-
- handler = urllib2.HTTPBasicAuthHandler(password_mgr)
-
- opener = urllib2.build_opener(handler)
-
- a_url = 'http://www.baidu.com/'
-
- opener.open(a_url)
-
- 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」。
後者包含了端口號。
分類: 爬蟲 Python2013-05-14 16:21 1250人閱讀
前面說到了urllib2的簡單入門,下面整理了一部分urllib2的使用細節。
1.Proxy 的設置
urllib2 默認會使用環境變量 http_proxy 來設置 HTTP Proxy。
若是想在程序中明確控制 Proxy 而不受環境變量的影響,可使用代理。
新建test14來實現一個簡單的代理Demo:
- 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)
這裏要注意的一個細節,使用 urllib2.install_opener() 會設置 urllib2 的全局 opener 。
這樣後面的使用會很方便,但不能作更細緻的控制,好比想在程序中使用兩個不一樣的 Proxy 設置等。
比較好的作法是不使用 install_opener 去更改全局的設置,而只是直接調用 opener 的 open 方法代替全局的 urlopen 方法。
2.Timeout 設置
在老版 Python 中(Python2.6前),urllib2 的 API 並無暴露 Timeout 的設置,要設置 Timeout 值,只能更改 Socket 的全局 Timeout 值。
- import urllib2
- import socket
- socket.setdefaulttimeout(10)
- urllib2.socket.setdefaulttimeout(10)
在 Python 2.6 之後,超時能夠經過 urllib2.urlopen() 的 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 會經過該值來判斷是不是瀏覽器發出的請求
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.Redirect
urllib2 默認狀況下會針對 HTTP 3XX 返回碼自動進行 redirect 動做,無需人工配置。要檢測是否發生了 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 可以發出 PUT 或DELETE 的請求:
- import urllib2
- request = urllib2.Request(uri, data=data)
- request.get_method = lambda: 'PUT'
- 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 打開,這樣收發包的內容就會在屏幕上打印出來,方便調試,有時能夠省去抓包的工做
- 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')
這樣就能夠看到傳輸的數據包內容了:
9.表單的處理
登陸必要填表,表單怎麼填?
首先利用工具截取所要填表的內容。
好比我通常用firefox+httpfox插件來看看本身到底發送了些什麼包。
以verycd爲例,先找到本身發的POST請求,以及POST表單項。
能夠看到verycd的話須要填username,password,continueURI,fk,login_submit這幾項,其中fk是隨機生成的(其實不太隨機,看上去像是把epoch時間通過簡單的編碼生成的),須要從網頁獲取,也就是說得先訪問一次網頁,用正則表達式等工具截取返回數據中的fk項。continueURI顧名思義能夠隨便寫,login_submit是固定的,這從源碼能夠看出。還有username,password那就很顯然了:
- 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.假裝成瀏覽器訪問
某些網站反感爬蟲的到訪,因而對爬蟲一概拒絕請求
這時候咱們須要假裝成瀏覽器,這能夠經過修改http包中的header來實現
-
- 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.對付"反盜鏈"
某些站點有所謂的反盜鏈設置,其實說穿了很簡單,
就是檢查你發送請求的header裏面,referer站點是否是他本身,
因此咱們只須要像把headers的referer改爲該網站便可,以cnbeta爲例:
#...
headers = {
'Referer':'http://www.cnbeta.com/articles'
}
#...
headers是一個dict數據結構,你能夠放入任何想要的header,來作一些假裝。
例如,有些網站喜歡讀取header中的X-Forwarded-For來看看人家的真實IP,能夠直接把X-Forwarde-For改了。
分類: 爬蟲 Python2013-05-14 21:36 1185人閱讀
-
- import string, urllib2
-
- 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) + '個網頁,並將其存儲爲' + sName + '......'
- f = open(sName,'w+')
- m = urllib2.urlopen(url + str(i)).read()
- f.write(m)
- f.close()
-
-
-
-
- bdurl = str(raw_input(u'請輸入貼吧的地址,去掉pn=後面的數字:\n'))
- begin_page = int(raw_input(u'請輸入開始的頁數:\n'))
- end_page = int(raw_input(u'請輸入終點的頁數:\n'))
-
-
- baidu_tieba(bdurl,begin_page,end_page)
分類: 爬蟲 Python2013-05-15 13:29 1212人閱讀
接下來準備用糗百作一個爬蟲的小例子。
可是在這以前,先詳細的整理一下Python中的正則表達式的相關內容。
正則表達式在Python爬蟲中的做用就像是老師點名時用的花名冊同樣,是必不可少的神兵利器。
1、 正則表達式基礎
1.1.概念介紹
正則表達式是用於處理字符串的強大工具,它並非Python的一部分。
其餘編程語言中也有正則表達式的概念,區別只在於不一樣的編程語言實現支持的語法數量不一樣。
它擁有本身獨特的語法以及一個獨立的處理引擎,在提供了正則表達式的語言裏,正則表達式的語法都是同樣的。
下圖展現了使用正則表達式進行匹配的流程:
正則表達式的大體匹配過程是:
1.依次拿出表達式和文本中的字符比較,
2.若是每個字符都能匹配,則匹配成功;一旦有匹配不成功的字符則匹配失敗。
3.若是表達式中有量詞或邊界,這個過程會稍微有一些不一樣。
下圖列出了Python支持的正則表達式元字符和語法:
1.2. 數量詞的貪婪模式與非貪婪模式
正則表達式一般用於在文本中查找匹配的字符串。
貪婪模式,老是嘗試匹配儘量多的字符;
非貪婪模式則相反,老是嘗試匹配儘量少的字符。
Python裏數量詞默認是貪婪的。
例如:正則表達式"ab*"若是用於查找"abbbc",將找到"abbb"。
而若是使用非貪婪的數量詞"ab*?",將找到"a"。
1.3. 反斜槓的問題
與大多數編程語言相同,正則表達式裏使用"\"做爲轉義字符,這就可能形成反斜槓困擾。
假如你須要匹配文本中的字符"\",那麼使用編程語言表示的正則表達式裏將須要4個反斜槓"\\\\":
第一個和第三個用於在編程語言裏將第二個和第四個轉義成反斜槓,
轉換成兩個反斜槓\\後再在正則表達式裏轉義成一個反斜槓用來匹配反斜槓\。
這樣顯然是很是麻煩的。
Python裏的原生字符串很好地解決了這個問題,這個例子中的正則表達式可使用r"\\"表示。
一樣,匹配一個數字的"\\d"能夠寫成r"\d"。
有了原生字符串,媽媽不再用擔憂個人反斜槓問題~
2、 介紹re模塊
2.1. Compile
Python經過re模塊提供對正則表達式的支持。
使用re的通常步驟是:
Step1:先將正則表達式的字符串形式編譯爲Pattern實例。
Step2:而後使用Pattern實例處理文本並得到匹配結果(一個Match實例)。
Step3:最後使用Match實例得到信息,進行其餘的操做。
咱們新建一個re01.py來試驗一下re的應用:
-
- import re
-
- pattern = re.compile(r'hello')
-
- match1 = pattern.match('hello world!')
- match2 = pattern.match('helloo world!')
- match3 = pattern.match('helllo world!')
-
- if match1:
-
- print match1.group()
- else:
- print 'match1匹配失敗!'
-
-
- if match2:
-
- print match2.group()
- else:
- print 'match2匹配失敗!'
-
-
- if match3:
-
- print match3.group()
- else:
- print 'match3匹配失敗!'
能夠看到控制檯輸出了匹配的三個結果:
下面來具體看看代碼中的關鍵方法。
★ 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): 詳細模式。這個模式下正則表達式能夠是多行,忽略空白字符,並能夠加入註釋。
如下兩個正則表達式是等價的:
- import re
-
- a = re.compile(r
-
- b = re.compile(r"\d+\.\d*")
-
- match11 = a.match('3.1415')
- match12 = a.match('33')
- match21 = b.match('3.1415')
- match22 = b.match('33')
-
- if match11:
-
- print match11.group()
- else:
- print u'match11不是小數'
-
- if match12:
-
- print match12.group()
- else:
- print u'match12不是小數'
-
- if match21:
-
- print match21.group()
- else:
- print u'match21不是小數'
-
- if match22:
-
- print match22.group()
- else:
- print u'match22不是小數'
re提供了衆多模塊方法用於完成正則表達式的功能。
這些方法可使用Pattern實例的相應方法替代,惟一的好處是少寫一行re.compile()代碼,
但同時也沒法複用編譯後的Pattern對象。
這些方法將在Pattern類的實例方法部分一塊兒介紹。
如一開始的hello實例能夠簡寫爲:
- # -*- coding: utf-8 -*-
- #一個簡單的re實例,匹配字符串中的hello字符串
- import re
-
- m = re.match(r'hello', 'hello world!')
- print m.group()
re模塊還提供了一個方法escape(string),用於將string中的正則表達式元字符如*/+/?等以前加上轉義符再返回
2.2. Match
Match對象是一次匹配的結果,包含了不少關於這次匹配的信息,可使用Match提供的可讀屬性或方法來獲取這些信息。
屬性:
- string: 匹配時使用的文本。
- re: 匹配時使用的Pattern對象。
- pos: 文本中正則表達式開始搜索的索引。值與Pattern.match()和Pattern.seach()方法的同名參數相同。
- endpos: 文本中正則表達式結束搜索的索引。值與Pattern.match()和Pattern.seach()方法的同名參數相同。
- lastindex: 最後一個被捕獲的分組在文本中的索引。若是沒有被捕獲的分組,將爲None。
- lastgroup: 最後一個被捕獲的分組的別名。若是這個分組沒有別名或者沒有被捕獲的分組,將爲None。
方法:
- group([group1, …]):
得到一個或多個分組截獲的字符串;指定多個參數時將以元組形式返回。group1可使用編號也可使用別名;編號0表明整個匹配的子串;不填寫參數時,返回group(0);沒有截獲字符串的組返回None;截獲了屢次的組返回最後一次截獲的子串。
- groups([default]):
以元組形式返回所有分組截獲的字符串。至關於調用group(1,2,…last)。default表示沒有截獲字符串的組以這個值替代,默認爲None。
- groupdict([default]):
返回以有別名的組的別名爲鍵、以該組截獲的子串爲值的字典,沒有別名的組不包含在內。default含義同上。
- start([group]):
返回指定的組截獲的子串在string中的起始索引(子串第一個字符的索引)。group默認值爲0。
- end([group]):
返回指定的組截獲的子串在string中的結束索引(子串最後一個字符的索引+1)。group默認值爲0。
- span([group]):
返回(start(group), end(group))。
- expand(template):
將匹配到的分組代入template中而後返回。template中可使用\id或\g<id>、\g<name>引用分組,但不能使用編號0。\id與\g<id>是等價的;但\10將被認爲是第10個分組,若是你想表達\1以後是字符'0',只能使用\g<1>0。
下面來用一個py實例輸出全部的內容加深理解:
-
- import re
- m = re.match(r'(\w+) (\w+)(?P<sign>.*)', 'hello world!')
-
- print "m.string:", m.string
- print "m.re:", m.re
- print "m.pos:", m.pos
- print "m.endpos:", m.endpos
- print "m.lastindex:", m.lastindex
- print "m.lastgroup:", m.lastgroup
-
- print "m.group():", m.group()
- print "m.group(1,2):", m.group(1, 2)
- print "m.groups():", m.groups()
- print "m.groupdict():", m.groupdict()
- print "m.start(2):", m.start(2)
- print "m.end(2):", m.end(2)
- print "m.span(2):", m.span(2)
- print r"m.expand(r'\g<2> \g<1>\g<3>'):", m.expand(r'\2 \1\3')
-
2.3. Pattern
Pattern對象是一個編譯好的正則表達式,經過Pattern提供的一系列方法能夠對文本進行匹配查找。
Pattern不能直接實例化,必須使用re.compile()進行構造,也就是re.compile()返回的對象。
Pattern提供了幾個可讀屬性用於獲取表達式的相關信息:
- pattern: 編譯時用的表達式字符串。
- flags: 編譯時用的匹配模式。數字形式。
- groups: 表達式中分組的數量。
- groupindex: 以表達式中有別名的組的別名爲鍵、以該組對應的編號爲值的字典,沒有別名的組不包含在內。
能夠用下面這個例子查看pattern的屬性:
-
- import re
- p = re.compile(r'(\w+) (\w+)(?P<sign>.*)', re.DOTALL)
-
- print "p.pattern:", p.pattern
- print "p.flags:", p.flags
- print "p.groups:", p.groups
- print "p.groupindex:", p.groupindex
-
下面重點介紹一下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的簡單案例:
- import re
-
- pattern = re.compile(r'hello')
-
- match = pattern.match('hello world!')
-
- if match:
-
- print match.group()
-
2.search
search(string[, pos[, endpos]]) | re.search(pattern, string[, flags]):
這個方法用於查找字符串中能夠匹配成功的子串。
從string的pos下標處起嘗試匹配pattern,
若是pattern結束時仍可匹配,則返回一個Match對象;
若沒法匹配,則將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的實例:
-
- import re
-
- pattern = re.compile(r'world')
-
- match = pattern.search('hello world!')
-
- if match:
-
- print match.group()
-
3.split
split(string[, maxsplit]) | re.split(pattern, string[, maxsplit]):
按照可以匹配的子串將string分割後返回列表。
maxsplit用於指定最大分割次數,不指定將所有分割。
- import re
-
- p = re.compile(r'\d+')
- print p.split('one1two2three3four4')
-
4.findall
findall(string[, pos[, endpos]]) | re.findall(pattern, string[, flags]):
搜索string,以列表形式返回所有能匹配的子串。
- import re
-
- p = re.compile(r'\d+')
- print p.findall('one1two2three3four4')
-
5.finditer
finditer(string[, pos[, endpos]]) | re.finditer(pattern, string[, flags]):
搜索string,返回一個順序訪問每個匹配結果(Match對象)的迭代器。
- import re
-
- p = re.compile(r'\d+')
- for m in p.finditer('one1two2three3four4'):
- print m.group(),
-
- ### output ###
- # 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用於指定最多替換次數,不指定時所有替換。
- import re
-
- p = re.compile(r'(\w+) (\w+)')
- s = 'i say, hello world!'
-
- print p.sub(r'\2 \1', s)
-
- def func(m):
- return m.group(1).title() + ' ' + m.group(2).title()
-
- print p.sub(func, s)
-
7.subn
subn(repl, string[, count]) |re.sub(pattern, repl, string[, count]):
返回 (sub(repl, string[, count]), 替換次數)。
- import re
-
- p = re.compile(r'(\w+) (\w+)')
- s = 'i say, hello world!'
-
- print p.subn(r'\2 \1', s)
-
- def func(m):
- return m.group(1).title() + ' ' + m.group(2).title()
-
- print p.subn(func, s)
-
至此,Python的正則表達式基本介紹就算是完成了^_^
分類: Python 爬蟲2013-05-15 20:59 1208人閱讀
項目內容:
用Python寫的糗事百科的網絡爬蟲。
使用方法:
新建一個Bug.py文件,而後將代碼複製到裏面後,雙擊運行。
程序功能:
在命令提示行中瀏覽糗事百科。
原理解釋:
首先,先瀏覽一下糗事百科的主頁:http://www.qiushibaike.com/hot/page/1
能夠看出來,連接中page/後面的數字就是對應的頁碼,記住這一點爲之後的編寫作準備。
而後,右擊查看頁面源碼:
觀察發現,每個段子都用div標記,其中class必爲content,title是發帖時間,咱們只須要用正則表達式將其「扣」出來就能夠了。
明白了原理以後,剩下的就是正則表達式的內容了,能夠參照這篇博文:
http://blog.csdn.net/wxg694175346/article/details/8929576
運行效果:
-
- import urllib2
- import urllib
- import re
- import thread
- import time
-
- class HTML_Tool:
-
- BgnCharToNoneRex = re.compile("(\t|\n| |<a.*?>|<img.*?>)")
-
-
- EndCharToNoneRex = re.compile("<.*?>")
-
-
- BgnPartRex = re.compile("<p.*?>")
- CharToNewLineRex = re.compile("(<br/>|</p>|<tr>|<div>|</div>)")
- CharToNextTabRex = re.compile("<td>")
-
-
- replaceTab = [("<","<"),(">",">"),("&","&"),("&","\""),(" "," ")]
-
- def Replace_Char(self,x):
- x = self.BgnCharToNoneRex.sub("",x)
- x = self.BgnPartRex.sub("\n ",x)
- x = self.CharToNewLineRex.sub("\n",x)
- x = self.CharToNextTabRex.sub("\t",x)
- x = self.EndCharToNoneRex.sub("",x)
-
- for t in self.replaceTab:
- x = x.replace(t[0],t[1])
- return x
-
-
- class HTML_Model:
-
- def __init__(self):
- self.page = 1
- self.pages = []
- self.myTool = HTML_Tool()
- self.enable = False
-
-
- def GetPage(self,page):
- myUrl = "http://m.qiushibaike.com/hot/page/" + page
- myResponse = urllib2.urlopen(myUrl)
- myPage = myResponse.read()
-
-
- unicodePage = myPage.decode("utf-8")
-
-
-
- myItems = re.findall('<div.*?class="content".*?title="(.*?)">(.*?)</div>',unicodePage,re.S)
- items = []
- for item in myItems:
-
-
- items.append([item[0].replace("\n",""),item[1].replace("\n","")])
- return items
-
-
- def LoadPage(self):
-
- while self.enable:
-
- if len(self.pages) < 2:
- try:
-
- myPage = self.GetPage(str(self.page))
- self.page += 1
- self.pages.append(myPage)
- except:
- print '沒法連接糗事百科!'
- else:
- time.sleep(1)
-
- def ShowPage(self,q,page):
- for items in q:
- print u'第%d頁' % page , items[0]
- print self.myTool.Replace_Char(items[1])
- myInput = raw_input()
- if myInput == "quit":
- self.enable = False
- break
-
- def Start(self):
- self.enable = True
- page = self.page
-
- print u'正在加載中請稍候......'
-
-
- thread.start_new_thread(self.LoadPage,())
-
-
- while self.enable:
-
- if self.pages:
- nowPage = self.pages[0]
- del self.pages[0]
- self.ShowPage(nowPage,page)
- page += 1
-
-
- print u
-
-
- print u'請按下回車瀏覽今日的糗百內容:'
- raw_input(' ')
- myModel = HTML_Model()
- myModel.Start()
分類: 爬蟲 Python2013-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標記:
- <h1 class="core_title_txt" title="【原創】時尚首席(關於時尚,名利,事業,愛情,勵志)">【原創】時尚首席(關於時尚,名利,事業,愛情,勵志)</h1>
一樣,正文部分用div和class綜合標記,接下來要作的只是用正則表達式來匹配便可。
運行截圖:
生成的txt文件:
-
- import string
- import urllib2
- import re
-
- class HTML_Tool:
-
- BgnCharToNoneRex = re.compile("(\t|\n| |<a.*?>|<img.*?>)")
-
-
- EndCharToNoneRex = re.compile("<.*?>")
-
-
- BgnPartRex = re.compile("<p.*?>")
- CharToNewLineRex = re.compile("(<br/>|</p>|<tr>|<div>|</div>)")
- CharToNextTabRex = re.compile("<td>")
-
-
- replaceTab = [("<","<"),(">",">"),("&","&"),("&","\""),(" "," ")]
-
- def Replace_Char(self,x):
- x = self.BgnCharToNoneRex.sub("",x)
- x = self.BgnPartRex.sub("\n ",x)
- x = self.CharToNewLineRex.sub("\n",x)
- x = self.CharToNextTabRex.sub("\t",x)
- x = self.EndCharToNoneRex.sub("",x)
-
- for t in self.replaceTab:
- x = x.replace(t[0],t[1])
- return x
-
- class Baidu_Spider:
-
- def __init__(self,url):
- self.myUrl = url + '?see_lz=1'
- self.datas = []
- self.myTool = HTML_Tool()
- print u'已經啓動百度貼吧爬蟲,咔嚓咔嚓'
-
-
- def baidu_tieba(self):
-
- myPage = urllib2.urlopen(self.myUrl).read().decode("gbk")
-
- endPage = self.page_counter(myPage)
-
- title = self.find_title(myPage)
- print u'文章名稱:' + title
-
- self.save_data(self.myUrl,title,endPage)
-
-
- def page_counter(self,myPage):
-
- myMatch = re.search(r'class="red">(\d+?)</span>', myPage, re.S)
- if myMatch:
- endPage = int(myMatch.group(1))
- print u'爬蟲報告:發現樓主共有%d頁的原創內容' % endPage
- else:
- endPage = 0
- print u'爬蟲報告:沒法計算樓主發佈內容有多少頁!'
- return endPage
-
-
- def find_title(self,myPage):
-
- myMatch = re.search(r'<h1.*?>(.*?)</h1>', myPage, re.S)
- title = u'暫無標題'
- if myMatch:
- title = myMatch.group(1)
- else:
- print u'爬蟲報告:沒法加載文章標題!'
-
- title = title.replace('\\','').replace('/','').replace(':','').replace('*','').replace('?','').replace('"','').replace('>','').replace('<','').replace('|','')
- return title
-
-
-
- def save_data(self,url,title,endPage):
-
- self.get_data(url,endPage)
-
- f = open(title+'.txt','w+')
- f.writelines(self.datas)
- f.close()
- print u'爬蟲報告:文件已下載到本地並打包成txt文件'
- print u'請按任意鍵退出...'
- raw_input();
-
-
- def get_data(self,url,endPage):
- url = url + '&pn='
- for i in range(1,endPage+1):
- print u'爬蟲報告:爬蟲%d號正在加載中...' % i
- myPage = urllib2.urlopen(url + str(i)).read()
-
- self.deal_data(myPage.decode('gbk'))
-
-
-
- def deal_data(self,myPage):
- myItems = re.findall('id="post_content.*?>(.*?)</div>',myPage,re.S)
- for item in myItems:
- data = self.myTool.Replace_Char(item.replace("\n","").encode('gbk'))
- self.datas.append(data+'\n')
-
-
-
- print u
-
-
- print u'請輸入貼吧的地址最後的數字串:'
- bdurl = 'http://tieba.baidu.com/p/' + str(raw_input(u'http://tieba.baidu.com/p/'))
-
- mySpider = Baidu_Spider(bdurl)
- mySpider.baidu_tieba()