Scrapy-Redis 空跑问题,redis_key链接跑完后,自动关闭爬虫

时间:2023-03-09 02:18:22
Scrapy-Redis 空跑问题,redis_key链接跑完后,自动关闭爬虫

首先解决爬虫等待,不被关闭的问题:

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 是否为空,为条件

在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个 ,一个时间单位为5s # 在 EXTENSIONS 配置,激活扩展
'EXTENSIONS'= {
'lianjia_ershoufang.extensions.RedisSpiderSmartIdleClosedExensions': ,
},
MYEXT_ENABLED: 是否启用扩展,启用扩展为 True, 不启用为 False
IDLE_NUMBER: 关闭爬虫的持续空闲次数,持续空闲次数超过IDLE_NUMBER,爬虫会被关闭。默认为 ,也就是30分钟,一分钟12个时间单位