如何控制分布式爬蟲結束


Scrapy-Redis 空跑問題,redis_key鏈接跑完后,自動關閉爬蟲

問題:

  • scrapy-redis框架中,reids存儲的xxx:requests已經爬取完畢,但程序仍然一直運行,如何自動停止程序,結束空跑。

相信大家都很頭疼,尤其是網上一堆搬來搬去的帖子,來看一下 我是如何解決這個問題的吧

課外了解

分布式擴展:

我們知道 scrapy 默認是單機運行的,那么scrapy-redis是如何把它變成可以多台機器協作的呢?

首先解決爬蟲等待,不被關閉的問題:

1、scrapy內部的信號系統會在爬蟲耗盡內部隊列中的request時,就會觸發spider_idle信號。

2、爬蟲的信號管理器收到spider_idle信號后,將調用注冊spider_idle信號的處理器進行處理。

3、當該信號的所有處理器(handler)被調用后,如果spider仍然保持空閑狀態, 引擎將會關閉該spider。

scrapy-redis 中的解決方案 在信號管理器上注冊一個對應在spider_idle信號下的spider_idle()方法,當spider_idle觸發是,信號管理器就會調用這個爬蟲中的spider_idle(), Scrapy_redis 源碼如下:

    def spider_idle(self): """Schedules a request if available, otherwise waits.""" # XXX: Handle a sentinel to close the spider. self.schedule_next_requests() # 這里調用schedule_next_requests() 來從redis中生成新的請求 raise DontCloseSpider # 拋出不要關閉爬蟲的DontCloseSpider異常,保證爬蟲活着 

解決思路:

  • 通過前面的了解,我們知道 爬蟲關閉的關鍵是 spider_idle 信號。
  • spider_idle信號只有在爬蟲隊列為空時才會被觸發, 觸發間隔為5s。
  • 那么我們也可以使用同樣的方式,在信號管理器上注冊一個對應在spider_idle信號下的spider_idle()方法。
  • 在 spider_idle() 方法中,編寫結束條件來結束爬蟲,這里以 判斷redis 中關鍵key 是否為空,為條件

解決方案:

  • redis_key 為空后一段時間關閉爬蟲

redis_key 為空后一段時間關閉爬蟲 的實現方案:

這里在 Scrapy 中的 exensions(擴展) 中實現,當然你也可以在pipelines(管道)中實現。

擴展框架提供一個機制,使得你能將自定義功能綁定到Scrapy。 擴展只是正常的類,它們在Scrapy啟動時被實例化、初始化。 關於擴展詳細見: scrapy 擴展(Extensions)

  • 在settings.py 文件的目錄下,創建一個名為 extensions.py 的文件,
  • 在其中寫入以下代碼
# -*- coding: utf-8 -*- # Define here the models for your scraped Extensions import logging import time from scrapy import signals from scrapy.exceptions import NotConfigured logger = logging.getLogger(__name__) class RedisSpiderSmartIdleClosedExensions(object): def __init__(self, idle_number, crawler): self.crawler = crawler self.idle_number = idle_number self.idle_list = [] self.idle_count = 0  @classmethod def from_crawler(cls, crawler): # first check if the extension should be enabled and raise # NotConfigured otherwise if not crawler.settings.getbool('MYEXT_ENABLED'): raise NotConfigured # 配置僅僅支持RedisSpider if not 'redis_key' in crawler.spidercls.__dict__.keys(): raise NotConfigured('Only supports RedisSpider') # get the number of items from settings idle_number = crawler.settings.getint('IDLE_NUMBER', 360) # instantiate the extension object ext = cls(idle_number, crawler) # connect the extension object to signals crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened) crawler.signals.connect(ext.spider_closed, signal=signals.spider_closed) crawler.signals.connect(ext.spider_idle, signal=signals.spider_idle) # return the extension object return ext def spider_opened(self, spider): logger.info("opened spider %s redis spider Idle, Continuous idle limit: %d", spider.name, self.idle_number) def spider_closed(self, spider): logger.info("closed spider %s, idle count %d , Continuous idle count %d", spider.name, self.idle_count, len(self.idle_list)) def spider_idle(self, spider): self.idle_count += 1 self.idle_list.append(time.time()) idle_list_len = len(self.idle_list) # 判斷 redis 中是否存在關鍵key, 如果key 被用完,則key就會不存在 if idle_list_len > 2 and spider.server.exists(spider.redis_key): self.idle_list = [self.idle_list[-1]] elif idle_list_len > self.idle_number: logger.info('\n continued idle number exceed {} Times' '\n meet the idle shutdown conditions, will close the reptile operation' '\n idle start time: {}, close spider time: {}'.format(self.idle_number, self.idle_list[0], self.idle_list[0])) # 執行關閉爬蟲操作 self.crawler.engine.close_spider(spider, 'closespider_pagecount') 
  • 在settings.py 中添加以下配置, 請將 lianjia_ershoufang 替換為你的項目目錄名。

MYEXT_ENABLED=True # 開啟擴展 IDLE_NUMBER=360 # 配置空閑持續時間單位為 360個 ,一個時間單位為5s # 在 EXTENSIONS 配置,激活擴展 'EXTENSIONS'= { 'lianjia_ershoufang.extensions.RedisSpiderSmartIdleClosedExensions': 500, }, 
  • 完成空閑關閉擴展,爬蟲會在持續空閑 360個時間單位后關閉爬蟲

配置說明:

MYEXT_ENABLED: 是否啟用擴展,啟用擴展為 True, 不啟用為 False IDLE_NUMBER: 關閉爬蟲的持續空閑次數,持續空閑次數超過IDLE_NUMBER,爬蟲會被關閉。默認為 360 ,也就是30分鍾,一分鍾12個時間單位 

結語

此方法只使用於 5秒內跑不完一組鏈接的情況,如果你的一組鏈接5秒就能跑完,你可以在此基礎上做一些判斷。原理一樣,大家可以照葫蘆畫瓢。

哈哈,我的方式是不是特別棒呀!


免責聲明!

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



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