scrapy自定義重試方法
這是崔斯特的第八十五篇原創文章
自定義重試方法 (๑• . •๑)
Scrapy是自帶有重試的,但一般是下載出錯才會重試,當然你可以在Middleware處來完成你的邏輯。這篇文章主要介紹的是如何在spider裡面完成重試。使用場景比如,我解析json出錯了,html中不包含我想要的資料,我要重試這個請求(request)。
我們先看看官方是如何完成重試的
scrapy/downloadermiddlewares/retry.py
def _retry(self, request, reason, spider): retries = request.meta.get('retry_times', 0) + 1 retry_times = self.max_retry_times if 'max_retry_times' in request.meta: retry_times = request.meta['max_retry_times'] stats = spider.crawler.stats if retries <= retry_times: logger.debug("Retrying %(request)s (failed %(retries)d times): %(reason)s", {'request': request, 'retries': retries, 'reason': reason}, extra={'spider': spider}) retryreq = request.copy() retryreq.meta['retry_times'] = retries retryreq.dont_filter = True retryreq.priority = request.priority + self.priority_adjust if isinstance(reason, Exception): reason = global_object_name(reason.__class__) stats.inc_value('retry/count') stats.inc_value('retry/reason_count/%s' % reason) return retryreq else: stats.inc_value('retry/max_reached') logger.debug("Gave up retrying %(request)s (failed %(retries)d times): %(reason)s", {'request': request, 'retries': retries, 'reason': reason}, extra={'spider': spider})
可以看到非常清晰,在meta中傳遞一個引數 retry_times
,來記錄當前的request採集了多少次,如果重試次數小於設定的最大重試次數,那麼重試。
根據這段程式碼我們自定義的重試可以這麼寫
def parse(self, response): try: data = json.loads(response.text) except json.decoder.JSONDecodeError: r = response.request.copy() r.dont_filter = True yield r
捕獲異常,如果返回不是json,那就重試,注意需要設定不過濾。
這種方法簡單粗暴,存在BUG,就是會陷入死迴圈。我也可以記錄重試的次數,用meta傳遞。
def parse(self, response): try: data = json.loads(response.text) except json.decoder.JSONDecodeError: retries = response.meta.get('cus_retry_times', 0) + 1 if retries <= self.cus_retry_times: r = response.request.copy() r.meta['cus_retry_times'] = retries r.dont_filter = True yield r else: self.logger.debug("Gave up retrying {}, failed {} times".format( response.url, retries ))
這樣就完成了自定義重試,你完全可以在中介軟體完成,但是我更喜歡這種方法,可以清楚地知道爬蟲具體哪裡會存在問題。
其實以上這種方法也不好,因為你可能會在很多地方都需要重試,每個函式都需要,那每次都寫一遍,太不美觀。更好的方法是將此方法封裝為 scrapy.http.Response
的一個函式,需要用的時候直接調。程式碼就不貼了,有興趣的可以研究下,用到python的繼承。