7.scrapy中間件

scrapy中間件

1. 中間件

1.1 什麼是中間件

若是有學過django的同窗,應該對這個名詞不陌生了,在django中,中間件能夠對請求作統一批量的處理python

那麼在爬蟲中,中間件的做用也是作批量處理的,好比把全部請求的請求頭添加一個值等等等。django

因爲爬蟲是一個發請求,獲取響應的過程,因此在scrapy框架中有兩個中間件。json

2. 兩種中間件

在scrapy框架中所謂的中間件本質上就是一個類,裏面有一些方法。cookie

固然你想應用中間件,必需要在settings文件中註冊一下,優先級仍然是數值小的優先級高,通常放在上面。框架

# 爬蟲中間件
SPIDER_MIDDLEWARES = {
   'spider1.middlewares.Spider1SpiderMiddleware': 543,
}

# 下載中間件
DOWNLOADER_MIDDLEWARES = {
   'spider1.middlewares.Spider1DownloaderMiddleware': 543,
}

2.1 爬蟲中間件

實際上中間件的方法只有四個就好了,可是框架給咱們綁定了一個爬蟲打開時的信號。scrapy

from scrapy import signals


class Spider1SpiderMiddleware:


    @classmethod
    def from_crawler(cls, crawler):
        # This method is used by Scrapy to create your spiders.
        s = cls()
        # 建立spider(爬蟲對象)的時候,註冊一個信號
        # 信號: 當爬蟲的打開的時候 執行 spider_opened 這個方法
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def process_spider_input(self, response, spider):
        # 下載完成後,執行,而後交給parse處理
        return None

    def process_spider_output(self, response, result, spider):
        """
        經歷過parse函數以後執行
        :param response: 上一次請求返回的結果
        :param result: yield的對象 包含 [item/Request] 對象的可迭代對象
        :param spider: 當前爬蟲對象
        :return: 返回Request對象 或 Item對象
        """
        for i in result:
            yield i

    def process_spider_exception(self, response, exception, spider):
    
        """若是執行parse拋出異常的話 會執行這個函數 默認不對異常處理交給下一個中間件處理"""
        pass

    def process_start_requests(self, start_requests, spider):
     
        """
        爬蟲啓動時調用
        :param start_requests: 包含 Request 對象的可迭代對象
        :param spider:
        :return: Request 對象
        """
        for r in start_requests:
            yield r

    def spider_opened(self, spider):
        # 生成爬蟲日誌
        spider.logger.info('Spider opened: %s' % spider.name)

2.1.1 方法解析

爬蟲開始時有個起始url,這個時候會通過爬蟲中間件的 process_start_requests 方法,而後去下載,接着ide

咱們看到 process_spider_input 和 process_spider_output 這兩個方法中參數都有一個response參數能夠判斷出函數

這兩個方法都是在下載完成後,拿到response以後纔會執行的。網站

當下載完成後再通過爬蟲中間件,這個時候會執行 process_spider_input 方法,這個時候的response中有url

包含此次請求的全部內容,其中請求的request對象也被封裝成了 response.resquest 。

而後來到 spider 文件中,經歷parse方法,這個時候返回值多是Request對象或者item對象,而後若是

parse 方法中拋出異常會執行爬蟲中間件的 process_spider_exception 方法,通常狀況下無異常,繼續走爬蟲

中間件中的 process_spider_output 方法。這就是爬蟲中間件中方法的在什麼地方執行的過程。

至於沒講到的那兩個方法,是基於信號作出的關於爬蟲開啓時日誌的拓展。對爬蟲中間件無影響,後面我會將

到關於信號實現的自定義拓展。

2.2 下載中間件

默認下載中間件源碼:

class Spider1DownloaderMiddleware:


    @classmethod
    def from_crawler(cls, crawler):
        # This method is used by Scrapy to create your spiders.
        # 這個方法同上,和爬蟲中間件同樣的功能
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def process_request(self, request, spider):
  
        """
        請求須要被下載時,通過全部下載中間件的process_request調用
        spider處理完成,返回時調用
        :param request:
        :param spider:
        :return:
            None,繼續往下執行,去下載
            Response對象,中止process_request的執行,開始執行process_response
            Request對象,中止中間件的執行,將Request從新放到調度器中
            raise IgnoreRequest異常,中止process_request的執行,開始執行process_exception
        """
        return None

    def process_response(self, request, response, spider):

        """
        下載獲得響應後,執行
        :param request: 請求對象
        :param response: 響應對象
        :param spider: 爬蟲對象
        :return:
            返回request對象,中止中間件,將Request對象從新放到調度器中
            返回response對象,轉交給其餘中間件process_response
            raise IgnoreRequest 異常: 調用Request.errback
        """
        return response

    def process_exception(self, request, exception, spider):
        
        """當下載處理器(download handler)或process_request() (下載中間件)拋出異常
            :return
                None: 繼續交給後續中間件處理異常
                Response對象: 中止後續process_exception方法
                Request對象: 中止中間件,request將會被從新調用下載
        """
        pass

    def spider_opened(self, spider):
        spider.logger.info('Spider opened: %s' % spider.name)

2.2.1 process_request

process_request(self, request, spider)

該方法是下載器中間件類中的一個方法,該方法是每一個請求從引擎發送給下載器下載以前都會通過該方法。因此該

方法常常用來處理請求頭的替換,IP的更改,Cookie等的替換。

參數:

  • request (Request 對象) – 處理的request

  • spider (Spider 對象) – 該request對應的spider

返回值:

返回 None:Scrapy將繼續處理該request,執行其餘的中間件的相應方法,直到合適的下載器處理函數

(download handler)被調用,該request被執行(其response被下載)。

若是其返回Response對象:Scrapy將不會調用任何其餘的 process_request ( ) 或 process_exception ( )

方法,或相應地下載函數; 其將返回response。

若是其返回Request對象,Scrapy則中止調用 process_request 方法並從新調度返回的request。當新返回的

request被執行後, 相應地中間件鏈將會根據下載的response被調用。

若是其raise一個 IgnoreRequest 異常,則安裝的下載中間件的 process_exception ( ) 方法會被調用。如

果沒有任何一個方法處理該異常, 則request的 errback(Request.errback) 方法會被調用。若是沒有代碼處

理拋出的異常, 則該異常被忽略且不記錄(不一樣於其餘異常那樣)。

2.2.2 process_response

process_response(self, request, response, spider)

該方法是下載器中間件類中的一個方法,該方法是每一個響應從下載器發送給spider以前都會通過該方法。

參數:

  • request (Request 對象) – response所對應的request

  • response (Response 對象) – 被處理的response

  • spider (Spider 對象) – response所對應的spider

返回值:

若是其返回一個Response (能夠與傳入的response相同,也能夠是全新的對象), 該response會被其餘中間件的

process_response() 方法處理。

若是其返回一個Request對象,則返回的request會被從新調度下載。處理相似於 process_request() 返回

request所作的那樣。

若是其拋出一個 IgnoreRequest 異常,則調用request的 errback(Request.errback) 。若是沒有代碼處理

拋出的異常,則該異常被忽略且不記錄(不一樣於其餘異常那樣)。

2.2.3 隨機替換User-Agent

通常門戶網站都會在請求頭中的User-Agent中設置反爬,因此咱們常常會在寫爬蟲的時候,要手動處理

User-Agent,可是通常狀況下咱們都是大批量爬取,若是在這裏指定一個 User-Agent 的話,也會可能被

網站檢測出來是爬蟲,這裏咱們提供一種隨機 User-Agent 的方法。

import Faker

class UserAgentDownloaderMiddleware(object):
    
    def __init__(self):
        self.fake = faker.Faker()
        
    def process_request(self, request, spider):
        # 隨機生成一個User-Agent
        useragent = self.fake.user_agent()
        # 更新請求頭的內容
        request.headers.update({"User-Agent": useragent})
        return None

2.2.4 設置cookie值

有時候咱們想要爬取的數據能夠須要登陸才能看到,這時候咱們就須要登陸網頁。登錄後的網頁通常都會在本地保存
該網頁的登陸信息Cookies在本地。只要獲取該Cookie,那麼在之後跳轉到其餘網頁的時候,只須要攜帶該Cookie便可。

class UserAgentDownloaderMiddleware(object):
    
    def __init__(self):
        self.fake = faker.Faker()
        
    def process_request(self, request, spider):
        # 隨機生成一個User-Agent
        useragent = self.fake.user_agent()
        # 更新請求頭的內容
        request.headers.update({"useragent": useragent})
        return None

2.2.5 設置代理

工做中你用爬蟲,必定最經常使用的就是代理,這裏咱們能夠自定義代理中間件

def get_proxy():
    """獲取代理的函數"""
    response = requests.get('http://134.175.188.27:5010/get/')
    data = response.json()
    return data["proxy"]


class ProxyDownloaderMiddleware(object):
    """代理中間件"""
    def process_request(self, request, spider):
        request.meta['proxy'] = get_proxy()
        return None

設置了代理以後,可能會出現代理不可用,通常咱們都是在配置文件中設置重試。

RETRY_ENABLED = True  # 是否開啓超時重試
RETRY_TIMES = 2       # initial response + 2 retries = 3 requests 重試次數
RETRY_HTTP_CODES =  [500, 502, 503, 504, 522, 524, 408, 429] # 重試的狀態碼
DOWNLOAD_TIMEOUT = 1  # 1秒沒有請求到數據,主動放棄
相關文章
相關標籤/搜索