1. 程式人生 > >Scrapy爬蟲入門教程十一 Request和Response(請求和響應)

Scrapy爬蟲入門教程十一 Request和Response(請求和響應)

開發環境: 
Python 3.6.0 版本 (當前最新) 
Scrapy 1.3.2 版本 (當前最新)

目錄

請求和響應

請求子類

響應物件

響應子類

請求和響應

Scrapy的Request和Response物件用於爬網網站。

通常,Request物件在爬蟲程式中生成並傳遞到系統,直到它們到達下載程式,後者執行請求並返回一個Response物件,該物件返回到發出請求的爬蟲程式。

上面一段話比較拗口,有web經驗的同學,應該都瞭解的,不明白看下面的圖大概理解下。

爬蟲爬蟲RequestRequestResponseResponse建立獲取下載資料資料

兩個類Request和Response類都有一些子類,它們新增基類中不需要的功能。這些在下面的請求子類和 響應子類中描述。

Request objects

class scrapy.http.Request(url[, callback, method='GET', headers, body, cookies, meta, encoding='utf-8', priority=0, dont_filter=False, errback])

一個Request物件表示一個HTTP請求,它通常是在爬蟲生成,並由下載執行,從而生成Response。

  • 引數:

    • url(string)
       - 此請求的網址
    • callback(callable) - 將使用此請求的響應(一旦下載)作為其第一個引數呼叫的函式。有關更多資訊,請參閱下面的將附加資料傳遞給回撥函式。如果請求沒有指定回撥,parse()將使用spider的 方法。請注意,如果在處理期間引發異常,則會呼叫errback。
    • method(string) - 此請求的HTTP方法。預設為’GET’。
    • meta(dict) - 屬性的初始值Request.meta。如果給定,在此引數中傳遞的dict將被淺複製。
    • body(str或unicode) - 請求體。如果unicode傳遞了a,那麼它被編碼為 str使用傳遞的編碼(預設為utf-8)。如果 body沒有給出,則儲存一個空字串。不管這個引數的型別,儲存的最終值將是一個str(不會是unicode或None)。
    • headers(dict) - 這個請求的頭。dict值可以是字串(對於單值標頭)或列表(對於多值標頭)。如果 None作為值傳遞,則不會發送HTTP頭。
    • cookie(dict或list) - 請求cookie。這些可以以兩種形式傳送。

      • 使用dict:
      request_with_cookies = Request(url="http://www.example.com",
                                   cookies={'currency': 'USD', 'country': 'UY'})
      • 1
      • 2
      • 使用列表:
      request_with_cookies = Request(url="http://www.example.com",
                                     cookies=[{'name': 'currency',
                                              'value': 'USD',
                                              'domain': 'example.com',
                                              'path': '/currency'}])

後一種形式允許定製 cookie的屬性domain和path屬性。這隻有在儲存Cookie用於以後的請求時才有用。

當某些網站返回Cookie(在響應中)時,這些Cookie會儲存在該域的Cookie中,並在將來的請求中再次傳送。這是任何常規網路瀏覽器的典型行為。但是,如果由於某種原因,您想要避免與現有Cookie合併,您可以通過將dont_merge_cookies關鍵字設定為True 來指示Scrapy如此操作 Request.meta。

不合並Cookie的請求示例:

request_with_cookies = Request(url="http://www.example.com",
                               cookies={'currency': 'USD', 'country': 'UY'},
                               meta={'dont_merge_cookies': True})
  • encoding(string) - 此請求的編碼(預設為’utf-8’)。此編碼將用於對URL進行百分比編碼,並將正文轉換為str(如果給定unicode)。
  • priority(int) - 此請求的優先順序(預設為0)。排程器使用優先順序來定義用於處理請求的順序。具有較高優先順序值的請求將較早執行。允許負值以指示相對低優先順序。
  • dont_filter(boolean) - 表示此請求不應由排程程式過濾。當您想要多次執行相同的請求時忽略重複過濾器時使用。小心使用它,或者你會進入爬行迴圈。預設為False。
  • errback(callable) - 如果在處理請求時引發任何異常,將呼叫的函式。這包括失敗的404 HTTP錯誤等頁面。它接收一個Twisted Failure例項作為第一個引數。有關更多資訊,請參閱使用errbacks在請求處理捕獲異常

  • url 
    包含此請求的網址的字串。請記住,此屬性包含轉義的網址,因此它可能與建構函式中傳遞的網址不同。

此屬性為只讀。更改請求使用的URL replace()

  • method 
    表示請求中的HTTP方法的字串。這保證是大寫的。例如:"GET","POST","PUT"

  • headers 
    包含請求標頭的類似字典的物件。

  • body 
    包含請求正文的str。

此屬性為只讀。更改請求使用的正文 replace()

  • meta 
    包含此請求的任意元資料的字典。此dict對於新請求為空,通常由不同的Scrapy元件(擴充套件程式,中介軟體等)填充。因此,此dict中包含的資料取決於您啟用的擴充套件。

當使用or 方法克隆請求時,此dict是淺複製的 ,並且也可以在您的爬蟲中從屬性訪問。copy()replace()response.meta

  • copy() 
    返回一個新的請求,它是這個請求的副本。另請參見: 將附加資料傳遞到回撥函式

  • replace([url, method, headers, body, cookies, meta, encoding, dont_filter, callback, errback]) 
    返回具有相同成員的Request物件,但通過指定的任何關鍵字引數賦予新值的成員除外。該屬性Request.meta是預設複製(除非新的值在給定的meta引數)。另請參見 將附加資料傳遞給回撥函式

將附加資料傳遞給回撥函式

請求的回撥是當下載該請求的響應時將被呼叫的函式。將使用下載的Response物件作為其第一個引數來呼叫回撥函式。

例:

def parse_page1(self, response):
    return scrapy.Request("http://www.example.com/some_page.html",
                          callback=self.parse_page2)

def parse_page2(self, response):
    # this would log http://www.example.com/some_page.html
    self.logger.info("Visited %s", response.url)

在某些情況下,您可能有興趣向這些回撥函式傳遞引數,以便稍後在第二個回撥中接收引數。您可以使用該Request.meta屬性。

以下是使用此機制傳遞專案以填充來自不同頁面的不同欄位的示例:

def parse_page1(self, response):
    item = MyItem()
    item['main_url'] = response.url
    request = scrapy.Request("http://www.example.com/some_page.html",
                             callback=self.parse_page2)
    request.meta['item'] = item
    yield request

def parse_page2(self, response):
    item = response.meta['item']
    item['other_url'] = response.url
    yield item

使用errbacks在請求處理中捕獲異常

請求的errback是在處理異常時被呼叫的函式。

它接收一個Twisted Failure例項作為第一個引數,並可用於跟蹤連線建立超時,DNS錯誤等。

這裡有一個示例爬蟲記錄所有錯誤,並捕獲一些特定的錯誤,如果需要:

import scrapy

from scrapy.spidermiddlewares.httperror import HttpError
from twisted.internet.error import DNSLookupError
from twisted.internet.error import TimeoutError, TCPTimedOutError

class ErrbackSpider(scrapy.Spider):
    name = "errback_example"
    start_urls = [
        "http://www.httpbin.org/",              # HTTP 200 expected
        "http://www.httpbin.org/status/404",    # Not found error
        "http://www.httpbin.org/status/500",    # server issue
        "http://www.httpbin.org:12345/",        # non-responding host, timeout expected
        "http://www.httphttpbinbin.org/",       # DNS error expected
    ]

    def start_requests(self):
        for u in self.start_urls:
            yield scrapy.Request(u, callback=self.parse_httpbin,
                                    errback=self.errback_httpbin,
                                    dont_filter=True)

    def parse_httpbin(self, response):
        self.logger.info('Got successful response from {}'.format(response.url))
        # do something useful here...

    def errback_httpbin(self, failure):
        # log all failures
        self.logger.error(repr(failure))

        # in case you want to do something special for some errors,
        # you may need the failure's type:

        if failure.check(HttpError):
            # these exceptions come from HttpError spider middleware
            # you can get the non-200 response
            response = failure.value.response
            self.logger.error('HttpError on %s', response.url)

        elif failure.check(DNSLookupError):
            # this is the original request
            request = failure.request
            self.logger.error('DNSLookupError on %s', request.url)

        elif failure.check(TimeoutError, TCPTimedOutError):
            request = failure.request
            self.logger.error('TimeoutError on %s', request.url)

Request.meta特殊鍵

Request.meta屬性可以包含任何任意資料,但有一些特殊的鍵由Scrapy及其內建擴充套件識別。

那些是:

dont_redirect
dont_retry
handle_httpstatus_list
handle_httpstatus_all
dont_merge_cookies(參見cookies建構函式的Request引數)
cookiejar
dont_cache
redirect_urls
bindaddress
dont_obey_robotstxt
download_timeout
download_maxsize
download_latency
proxy

bindaddress

用於執行請求的出站IP地址的IP。

download_timeout

下載器在超時前等待的時間量(以秒為單位)。參見:DOWNLOAD_TIMEOUT

download_latency

自請求已啟動以來,用於獲取響應的時間量,即通過網路傳送的HTTP訊息。此元鍵僅在響應已下載時可用。雖然大多數其他元鍵用於控制Scrapy行為,但這應該是隻讀的。

請求子類

這裡是內建子類的Request列表。您還可以將其子類化以實現您自己的自定義功能。

FormRequest物件 
FormRequest類擴充套件了Request具有處理HTML表單的功能的基礎。它使用lxml.html表單 從Response物件的表單資料預填充表單欄位。

class scrapy.http.FormRequest(url[, formdata, ...])

FormRequest類增加了新的建構函式的引數。其餘的引數與Request類相同,這裡沒有記錄。

  • 引數:formdata(元組的dict或iterable) - 是一個包含HTML Form資料的字典(或(key,value)元組的迭代),它將被url編碼並分配給請求的主體。 
    FormRequest物件支援除標準以下類方法Request的方法:
classmethod from_response(response[, formname=None, formid=None, formnumber=0, formdata=None, formxpath=None, formcss=None, clickdata=None, dont_click=False, ...])

返回一個新FormRequest物件,其中的表單欄位值已預先<form>填充在給定響應中包含的HTML 元素中。有關示例,請參閱 使用FormRequest.from_response()來模擬使用者登入

該策略是在任何可檢視的表單控制元件上預設自動模擬點選,如a 。即使這是相當方便,並且經常想要的行為,有時它可能導致難以除錯的問題。例如,當使用使用javascript填充和/或提交的表單時,預設行為可能不是最合適的。要禁用此行為,您可以將引數設定 為。此外,如果要更改單擊的控制元件(而不是禁用它),您還可以使用 引數。<input type="submit"> from_response() dont_click True clickdata

引數:

  • response(Responseobject) - 包含將用於預填充表單欄位的HTML表單的響應
  • formname(string) - 如果給定,將使用name屬性設定為此值的形式。
  • formid(string) - 如果給定,將使用id屬性設定為此值的形式。
  • formxpath(string) - 如果給定,將使用匹配xpath的第一個表單。
  • formcss(string) - 如果給定,將使用匹配css選擇器的第一個形式。
  • formnumber(integer) - 當響應包含多個表單時要使用的表單的數量。第一個(也是預設)是0。
  • formdata(dict) - 要在表單資料中覆蓋的欄位。如果響應元素中已存在欄位,則其值將被在此引數中傳遞的值覆蓋。
  • clickdata(dict) - 查詢控制元件被點選的屬性。如果沒有提供,表單資料將被提交,模擬第一個可點選元素的點選。除了html屬性,控制元件可以通過其相對於表單中其他提交表輸入的基於零的索引,通過nr屬性來標識。
  • dont_click(boolean) - 如果為True,表單資料將在不點選任何元素的情況下提交。

這個類方法的其他引數直接傳遞給 FormRequest建構函式。 
在新版本0.10.3:該formname引數。 
在新版本0.17:該formxpath引數。 
新的版本1.1.0:該formcss引數。 
新的版本1.1.0:該formid引數。

請求使用示例

使用FormRequest通過HTTP POST傳送資料

如果你想在你的爬蟲中模擬HTML表單POST併發送幾個鍵值欄位,你可以返回一個FormRequest物件(從你的爬蟲)像這樣:

return [FormRequest(url="http://www.example.com/post/action",
                    formdata={'name': 'John Doe', 'age': '27'},
                    callback=self.after_post)]

使用FormRequest.from_response()來模擬使用者登入

網站通常通過元素(例如會話相關資料或認證令牌(用於登入頁面))提供預填充的表單欄位。進行剪貼時,您需要自動預填充這些欄位,並且只覆蓋其中的一些,例如使用者名稱和密碼。您可以使用 此作業的方法。這裡有一個使用它的爬蟲示例:<input type="hidden"> FormRequest.from_response()

import scrapy

class LoginSpider(scrapy.Spider):
    name = 'example.com'
    start_urls = ['http://www.example.com/users/login.php']

    def parse(self, response):
        return scrapy.FormRequest.from_response(
            response,
            formdata={'username': 'john', 'password': 'secret'},
            callback=self.after_login
        )

    def after_login(self, response):
        # check login succeed before going on
        if "authentication failed" in response.body:
            self.logger.error("Login failed")
            return

        # continue scraping with authenticated session...

響應物件

class scrapy.http.Response(url[, status=200, headers=None, body=b'', flags=None, request=None]) 
一個Response物件表示的HTTP響應,這通常是下載(由下載),並供給到爬蟲進行處理。

引數:

  • url(string) - 此響應的URL
  • status(integer) - 響應的HTTP狀態。預設為200
  • headers(dict) - 這個響應的頭。dict值可以是字串(對於單值標頭)或列表(對於多值標頭)。
  • body(str) - 響應體。它必須是str,而不是unicode,除非你使用一個編碼感知響應子類,如 TextResponse
  • flags(list) - 是一個包含屬性初始值的 Response.flags列表。如果給定,列表將被淺複製。
  • request(Requestobject) - 屬性的初始值Response.request。這代表Request生成此響應。

url 
包含響應的URL的字串。

此屬性為只讀。更改響應使用的URL replace()。

status 
表示響應的HTTP狀態的整數。示例:200, 404。

headers 
包含響應標題的類字典物件。可以使用get()返回具有指定名稱的第一個標頭值或getlist()返回具有指定名稱的所有標頭值來訪問值。例如,此呼叫會為您提供標題中的所有Cookie:

response.headers.getlist('Set-Cookie')

body 
本回復的正文。記住Response.body總是一個位元組物件。如果你想unicode版本使用 TextResponse.text(只在TextResponse 和子類中可用)。

此屬性為只讀。更改響應使用的主體 replace()。

request 
Request生成此響應的物件。在響應和請求通過所有下載中介軟體後,此屬性在Scrapy引擎中分配。特別地,這意味著:

HTTP重定向將導致將原始請求(重定向之前的URL)分配給重定向響應(重定向後具有最終URL)。 
Response.request.url並不總是等於Response.url 
此屬性僅在爬蟲程式程式碼和 Spider Middleware中可用,但不能在Downloader Middleware中使用(儘管您有通過其他方式可用的請求)和處理程式response_downloaded。

meta 
的快捷方式Request.meta的屬性 Response.request物件(即self.request.meta)。

與Response.request屬性不同,Response.meta 屬性沿重定向和重試傳播,因此您將獲得Request.meta從您的爬蟲傳送的原始屬性。

也可以看看

Request.meta 屬性

flags 
包含此響應的標誌的列表。標誌是用於標記響應的標籤。例如:’cached’,’redirected ‘等等。它們顯示在Response(_ str_ 方法)的字串表示上,它被引擎用於日誌記錄。

copy() 
返回一個新的響應,它是此響應的副本。

replace([ url,status,headers,body,request,flags,cls ] ) 
返回具有相同成員的Response物件,但通過指定的任何關鍵字引數賦予新值的成員除外。該屬性Response.meta是預設複製。

urljoin(url ) 
通過將響應url與可能的相對URL 組合構造絕對url。

urlparse.urljoin(response.url, url)

響應子類

這裡是可用的內建Response子類的列表。您還可以將Response類子類化以實現您自己的功能。

TextResponse物件

class scrapy.http.TextResponse(url[, encoding[, ...]])

TextResponse物件向基Response類新增編碼能力 ,這意味著僅用於二進位制資料,例如影象,聲音或任何媒體檔案。

TextResponse物件支援一個新的建構函式引數,除了基礎Response物件。其餘的功能與Response類相同,這裡沒有記錄。

引數: encoding(string) - 是一個字串,包含用於此響應的編碼。如果你建立一個TextResponse具有unicode主體的物件,它將使用這個編碼進行編碼(記住body屬性總是一個字串)。如果encoding是None(預設值),則將在響應標頭和正文中查詢編碼。 
TextResponse除了標準物件之外,物件還支援以下屬性Response

text 
響應體,如unicode。

同樣response.body.decode(response.encoding),但結果是在第一次呼叫後快取,因此您可以訪問 response.text多次,無需額外的開銷。

注意 
unicode(response.body)不是一個正確的方法來將響應身體轉換為unicode:您將使用系統預設編碼(通常為ascii)而不是響應編碼。

encoding 
包含此響應的編碼的字串。編碼通過嘗試以下機制按順序解決:

  1. 在建構函式編碼引數中傳遞的編碼
  2. 在Content-Type HTTP頭中宣告的編碼。如果此編碼無效(即未知),則會被忽略,並嘗試下一個解析機制。
  3. 在響應主體中宣告的編碼。TextResponse類不提供任何特殊功能。然而, HtmlResponse和XmlResponse類做。
  4. 通過檢視響應體來推斷的編碼。這是更脆弱的方法,但也是最後一個嘗試。

selector 
一個Selector使用響應為目標例項。選擇器在第一次訪問時被延遲例項化。

TextResponse物件除了標準物件外還支援以下方法Response:

xpath(查詢) 
快捷方式TextResponse.selector.xpath(query)

response.xpath('//p')

css(query) 
快捷方式 TextResponse.selector.css(query):

response.css('p')

body_as_unicode() 
同樣text,但可用作方法。保留此方法以實現向後相容; 請喜歡response.text。

HtmlResponse物件

class scrapy.http.HtmlResponse(url [,... ] ) 
本HtmlResponse類的子類,TextResponse 這增加了通過檢視HTML編碼自動發現支援META HTTP-EQUIV屬性。見TextResponse.encoding。

XmlResponse物件

class scrapy.http.XmlResponse(url [,... ] ) 
本XmlResponse類的子類,TextResponse這增加了通過檢視XML宣告線路編碼自動發現支援。見TextResponse.encoding。