天天看點

網絡爬蟲架構Scrapy詳解之Request

作者:zarten,網際網路一線工作者。

位址:zhihu.com/people/zarten

介紹

Request類是一個http請求的類,對于爬蟲而言是一個很重要的類。通常在Spider中建立這樣的一個請求,在Downloader中執行這樣的一個請求。同時也有一個子類FormRequest繼承于它,用于post請求。

在Spider中通常用法:

yield scrapy.Request(url = 'zarten.com')           

複制

類屬性和方法有:

url

method

headers

body

meta

copy()

replace([url, method, headers, body, cookies, meta, encoding, dont_filter, callback, errback])           

複制

Request

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

複制

參數說明:

  • url 請求的url
  • callback 回調函數,用于接收請求後的傳回資訊,若沒指定,則預設為parse()函數
  • method http請求的方式,預設為GET請求,一般不需要指定。若需要POST請求,用FormRequest即可
  • headers 請求頭資訊,一般在settings中設定即可,也可在middlewares中設定
  • body str類型,為請求體,一般不需要設定(get和post其實都可以通過body來傳遞參數,不過一般不用)
  • cookies dict或list類型,請求的cookie dict方式(name和value的鍵值對):
cookies = {'name1' : 'value1' , 'name2' : 'value2'}           

複制

list方式:

cookies = [
{'name': 'Zarten', 'value': 'my name is Zarten', 'domain': 'example.com', 'path': '/currency'}
]           

複制

  • encoding 請求的編碼方式,預設為'utf-8'
  • priority int類型,指定請求的優先級,數字越大優先級越高,可以為負數,預設為0
  • dont_filter 預設為False,若設定為True,這次請求将不會過濾(不會加入到去重隊列中),可以多次執行相同的請求
  • errback 抛出錯誤的回調函數,錯誤包括404,逾時,DNS錯誤等,第一個參數為Twisted Failure執行個體
from scrapy.spidermiddlewares.httperror import HttpError
from twisted.internet.error import DNSLookupError
from twisted.internet.error import TimeoutError, TCPTimedOutError

class ToScrapeCSSSpider(scrapy.Spider):
    name = "toscrape-css"
    # start_urls = [
    #     'http://quotes.toscrape.com/',
    # ]

    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.info(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.info('HttpError錯誤 on %s', response.url)

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

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

複制

  • flags list類型,一般不會用到,發送請求的标志,一般用于日志記錄
  • meta 可使用者自定義從Request到Response傳遞參數,這個參數一般也可在middlewares中處理
yield scrapy.Request(url = 'zarten.com', meta = {'name' : 'Zarten'})           

複制

在Response中:

my_name = response.meta['name']           

複制

不過也有scrapy内置的特殊key,也非常有用,它們如下:

  • proxy 設定代理,一般在middlewares中設定

可以設定http或https代理

request.meta['proxy'] = 'https://' + 'ip:port'           

複制

  • downloadtimeout 設定請求逾時等待時間(秒),通常在settings中設定DOWNLOADTIMEOUT,預設是180秒(3分鐘)
  • maxretrytimes 最大重試次數(除去第一次下載下傳),預設為2次,通常在settings中 RETRY_TIMES設定
  • dont_redirect 設為True後,Request将不會重定向
  • dont_retry 設為True後,對于http連結錯誤或逾時的請求将不再重試請求
  • handlehttpstatuslist http傳回碼200-300之間都是成功的傳回,超出這個範圍的都是失敗傳回,scrapy預設是過濾了這些傳回,不會接收這些錯誤的傳回進行處理。不過可以自定義處理哪些錯誤傳回:
yield scrapy.Request(url= 'https://httpbin.org/get/zarten', meta= {'handle_httpstatus_list' : [404]})           

複制

在parse函數中可以看到處理404錯誤:

def parse(self, response):
        print('傳回資訊為:',response.text)           

複制

  • handlehttpstatusall 設為True後,Response将接收處理任意狀态碼的傳回資訊
  • dontmergecookies scrapy會自動儲存傳回的cookies,用于它的下次請求,當我們指定了自定義cookies時,如果我們不需要合并傳回的cookies而使用自己指定的cookies,可以設為True
  • cookiejar 可以在單個spider中追蹤多個cookie,它不是粘性的,需要在每次請求時都帶上
def start_requests(self):
        urls = ['http://quotes.toscrape.com/page/1',
                'http://quotes.toscrape.com/page/3',
                'http://quotes.toscrape.com/page/5',
                ]
        for i ,url in enumerate(urls):
            yield scrapy.Request(url= url, meta= {'cookiejar' : i})


    def parse(self, response):
        next_page_url = response.css("li.next > a::attr(href)").extract_first()
        if next_page_url is not None:
            yield scrapy.Request(response.urljoin(next_page_url), meta= {'cookiejar' : response.meta['cookiejar']}, callback= self.parse_next)

    def parse_next(self, response):
        print('cookiejar:', response.meta['cookiejar'])           

複制

  • dont_cache 設為True後,不會緩存
  • redirect_urls 暫時還不清楚具體的作用,知道的小夥伴們歡迎在評論留言
  • bindaddress 綁定輸出IP
  • dontobeyrobotstxt 設為True,不遵守robots協定,通常在settings中設定
  • downloadmaxsize 設定下載下傳器最大下載下傳的大小(位元組),通常在settings中設定DOWNLOADMAXSIZE,預設為1073741824 (1024MB=1G),若不設定最大的下載下傳限制,設為0
  • download_latency 隻讀屬性,擷取請求的響應時間(秒)
def start_requests(self):

        headers = {
            'user-agent' : 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
        }
        yield scrapy.Request(url= 'https://www.amazon.com', headers= headers)

    def parse(self, response):

        print('響應時間為:', response.meta['download_latency'])           

複制

  • downloadfailon_dataloss 很少用到,詳情看這裡
  • referrer_policy 設定Referrer Policy

FormRequest

FormRequest 類為Request的子類,用于POST請求

這個類新增了一個參數 formdata,其他參數與Request一樣,詳細可參考上面的講述

一般用法為:

yield scrapy.FormRequest(url="http://www.example.com/post/action",
                    formdata={'name': 'Zarten', 'age': '27'},
                    callback=self.after_post)           

複制