《用python寫網絡爬蟲》 編寫第一個網絡爬蟲


為了抓取網站,我們首先需要下載包含有感興趣數據的網頁,該過程一般被稱為爬取“crawing”。爬取一個網站有很多種方法,而選用哪種方法更加合適,則取決於目標網站的結構。本章中,首先會探討如何安全地下載網頁,然后會介紹如下3種爬取網站的常見方法:

  • 爬取網站地圖
  • 遍歷每個網頁的數據庫ID
  • 跟蹤網頁鏈接

 

下載網頁

  想要爬取網頁,我們首先需要將其下載下來。下面的示例腳本使用python的urllib2模塊下載URL。

import  urllib2
def  download(url):
  return  urllib2.urlopen(url).read()

當傳入URL參數時,該函數將會下載網頁並返回其HTML。不過,這個代碼存在一個問題,即當下在網頁時,我們可能會遇到一些無法控制的錯誤,比如請求的頁面可能不存在。此時,urllib2會拋出異常,然后退出腳本。安全起見,下面再給出一個更健壯的版本,可以捕獲這些異常。

import urllib2

def download(url)
    print("Download: ",url)
    try:
        html = urllib2.urlopen(url).read()
    except urllib2.URLError as e:
        print("Download error: ",e.reason)
        html = None
    return html

現在,當出現下載錯誤時,該函數能夠捕獲到異常,然后返回None。

重新下載

  下載時遇到的錯誤經常是臨時性的,比如服務器過載時返回的 503 service unavailable 錯誤。對於此類錯誤,我們可以嘗試重新下載,因為這個服務器問題現在可能已解決。不過,我們不需要對所有錯誤都嘗試重新下載。如果服務器返回的是 404 not found 這種錯誤,則說明該網頁目前並不存在,再次嘗試同樣的請求一般也不會出現不同的結果。

  互聯網工程任務組(Inter Engineering Task Force)定義了HTTP錯誤的完整列表,詳情可參考 http://tools.ietf.org/html/rfc7231#section-6  從該文檔中,我們可以了解到 4XX 錯誤發生在請求存在問題時,而 5XX 錯誤發生在服務端存在問題時。所以,我們只需要確保 download 函數在發生 5XX 錯誤時重試下載即可。下面是支持重試下載功能的新版本代碼。

#python2
import urllib2

def download(url, num_retries=2):
    print 'Download:',url
    try:
        html = urllib2.urlopen(url).read()
    except urllib2.URLError as e:
        print('Download error: ',e.reason)
        html = None
        if num_retries > 0:
            if hasattr(e, 'code') and 500 <= e.code <600:    #hasattr() 函數用於判斷對象是否包含對應的屬性。
                # recursively retry 5xx HTTP errors
                return download(url, num_retries-1)
    return html


#maybe made in Richard lawson
#python3
import os
import urllib.request
import urllib.error

def download(url, num_retries=2):
    print ("Download:",url)
    try:
        html = urllib.request.urlopen(url).read()
    except urllib.error.URLError as e:
        print("Download error: ",e.reason)
        html = None
        if num_retries > 0 and 500 <= e.code <600:
            # recursively retry 5xx HTTP errors
            return download(url, num_retries-1)
    return html

    


print(download("http://httpstat.us/500"))

os.system("pause")
#made in China

 

現在,當download函數遇到 5XX 錯誤碼時,將會遞歸調用函數自身進行嘗試。此外,該函數還增加了一個參數,用於設定重試下載的次數,其默認值為兩次。我們在這里限制網頁下載的嘗試次數,是因為服務器錯誤可能暫時還沒有解決。想要測試該函數,可以嘗試下載 http://httpstat.us/500 ,該網站會始終返回500錯誤碼。

將會顯示類似如下的文本

Download: http://httpstat.us/500
Download error:  Internal Server Error
Download: http://httpstat.us/500
Download error:  Internal Server Error
Download: http://httpstat.us/500
Download error:  Internal Server Error
None
請按任意鍵繼續. . .

從上面的返回結果可以看出,download 函數的行為和預期一致,先嘗試下載網頁,在接收到500錯誤后,又進行了兩次重試才放棄。

 

設置用戶代理

  默認情況下,urllib2 使用Python-urllib/2.7 作為用戶代理下載網頁內容,其中2.7是Python的版本號。如果能使用可辨識的用戶代理則更好,這樣可以避免我們的網絡爬蟲碰到一些問題。此外,也許是因為曾經經歷過質量不佳的Python網絡爬蟲造成的服務器過載,一些網站還會封禁這個默認的用戶代理。比如,在使用python默認用戶代理的情況下,訪問 http://meetup.com/ ,目前會返回如下訪問拒絕提示。

Access denied

The owner of this website(www.meetup.com) has banned your access based on your brower's signature (1754134676cf0ac4-ua48).

Ray ID: 1754134676cf0ac4
Timestamp:Mon,06-Oct-14 18:55:48 GMT
Your IP address:
Requested URL:www.meetup.com/
Error reference number: 1010
Server ID: FL_33F7
User-Agent: Python-urllib/2.7

 

  因此,為了下載更加可靠,我們需要控制用戶代理的設定。下面的代碼對 download 函數進行了修改,設定了一個默認的用戶代理“wswp”(即web scraping with python 的首字母縮寫)。

def download(url, user_agent='wswp', num_retries=2):
    print 'Download:', url 
    headers = {'User-agent':user_agent}
    request = urllib2.Request{url, headers=headers}
    try:
        html = urllib2.urlopen(request).read()
    except urllib.URLError as e:
        print 'Download error:', e.reason
        html = None
        if num_retries > 0:
            if hasattr(e, 'code') and 500 <= e.code <600:
            # recursively retry 5xx HTTP errors
            return download(url, num_retries-1)
    return html

現在,我們擁有了一個靈活的下載函數,可以在后續示例中得到復用。該函數能夠捕獲異常、重試下載並設置用戶代理。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM