1. 程式人生 > >Scrapy-Redis redis_key連結跑完後,自動關閉爬蟲

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):
        # 首先檢查是否應該啟用和提高擴充套件
        # 否則不配置
        if not crawler.settings.getbool('MYEXT_ENABLED'):
            raise NotConfigured

        # 獲取配置中的時間片個數,預設為360個,30分鐘
        idle_number = crawler.settings.getint('IDLE_NUMBER', 360)

        # 例項化擴充套件物件
        ext = cls(idle_number, crawler)

        # 將擴充套件物件連線到訊號, 將signals.spider_idle 與 spider_idle() 方法關聯起來。
        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())       # 每次觸發 spider_idle時,記錄下觸發時間戳
        idle_list_len = len(self.idle_list)         # 獲取當前已經連續觸發的次數

        # 判斷 當前觸發時間與上次觸發時間 之間的間隔是否大於5秒,如果大於5秒,說明redis 中還有key 
        if idle_list_len > 2 and self.idle_list[-1] - self.idle_list[-2] > 6:
            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秒就能跑完,你可以在此基礎上做一些判斷。原理一樣,大家可以照葫蘆畫瓢。

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

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