Scrapy爬虫完成爬取循环后如何在内部重启爬取流程
Scrapy爬虫循环重启实现方案
需求与现有逻辑
需要实现Scrapy爬虫在完成所有坐标的多页爬取后,自动等待一段时间重启爬取流程(再次调用start_requests()),且重启逻辑需在爬虫内部完成。现有核心爬取逻辑如下:
def start_requests(): # 从数据库获取坐标列表及每个坐标上次爬取的最后页码 for coordinate in coordinates: yield scrapy.Request( f"{settings.BASE_URL}/search.json?latitude={coordinate.latitude}&longitude={coordinate.longitude}&page={coordinate.current_page}", callback=self.parse_coordinates, cb_kwargs={'coordinate': coordinate} ) def parse_coordinates(self, response, coordinate): # 解析页面数据 # 判断是否到达最后一页 if not last_page: # 更新坐标的当前页码,生成下一页请求 yield scrapy.Request( f"{settings.BASE_URL}/search.json?latitude={coordinate.latitude}&longitude={coordinate.longitude}&page={coordinate.current_page}", callback=self.parse_coordinates, cb_kwargs={'coordinate': coordinate} ) else: # 重置该坐标的页码计数器为1,以便下次循环重新开始
失败方案回顾
- 在
start_requests末尾加暂停代码:阻塞Scrapy事件循环,导致爬虫无法处理请求 - 使用
task.LoopingCall:主爬取循环结束后爬虫直接关闭,定时任务无法生效
正确实现方案
方案1:利用spider_idle信号触发重启
当爬虫进入空闲状态(所有请求处理完成)时,触发延迟重启逻辑:
from scrapy import signals from twisted.internet import reactor from twisted.internet.defer import Deferred class YourSpider(scrapy.Spider): name = "your_spider" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.restart_delay = 3600 # 自定义等待时长(秒) self.is_restarting = False # 避免重复触发重启 @classmethod def from_crawler(cls, crawler, *args, **kwargs): spider = super().from_crawler(crawler, *args, **kwargs) crawler.signals.connect(spider.spider_idle_handler, signal=signals.spider_idle) return spider def spider_idle_handler(self, spider): if self.is_restarting: return self.is_restarting = True self.logger.info(f"所有爬取任务完成,{self.restart_delay}秒后重启") # 异步延迟执行重启 d = Deferred() reactor.callLater(self.restart_delay, self.restart_spider, d) return d def restart_spider(self, d): # 重置所有坐标页码(需实现数据库更新逻辑) self.reset_all_coordinate_pages() # 重新生成初始请求并加入调度 for req in self.start_requests(): self.crawler.engine.crawl(req, spider=self) self.is_restarting = False d.callback(None) def reset_all_coordinate_pages(self): # 实现将数据库中所有坐标的页码重置为1的逻辑 pass # 原有start_requests和parse_coordinates方法
方案2:跟踪坐标完成状态,精准触发重启
通过计数器跟踪所有坐标的爬取完成情况,当全部完成时触发延迟重启:
from twisted.internet import reactor class YourSpider(scrapy.Spider): name = "your_spider" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.restart_delay = 3600 self.total_coords = 0 self.completed_coords = 0 def start_requests(self): coordinates = self.get_coordinates_from_db() self.total_coords = len(coordinates) self.completed_coords = 0 for coordinate in coordinates: yield scrapy.Request( f"{settings.BASE_URL}/search.json?latitude={coordinate.latitude}&longitude={coordinate.longitude}&page={coordinate.current_page}", callback=self.parse_coordinates, cb_kwargs={'coordinate': coordinate} ) def parse_coordinates(self, response, coordinate): # 页面解析逻辑... if not last_page: yield scrapy.Request( f"{settings.BASE_URL}/search.json?latitude={coordinate.latitude}&longitude={coordinate.longitude}&page={coordinate.current_page}", callback=self.parse_coordinates, cb_kwargs={'coordinate': coordinate} ) else: # 重置单个坐标页码 self.reset_single_coord_page(coordinate) self.completed_coords += 1 # 所有坐标完成时触发重启 if self.completed_coords == self.total_coords: self.logger.info(f"所有坐标爬取完成,{self.restart_delay}秒后重启") reactor.callLater(self.restart_delay, self.restart_spider) def restart_spider(self): for req in self.start_requests(): self.crawler.engine.crawl(req, spider=self) def reset_single_coord_page(self, coordinate): # 实现单个坐标页码重置的数据库更新逻辑 pass
核心注意事项
- 必须使用Twisted的
reactor.callLater实现异步延迟,禁止用time.sleep()阻塞事件循环 - 加重启状态标记,避免重复触发重启逻辑
- 确保页码重置逻辑正确更新数据库,保证下次爬取从第一页开始
内容的提问来源于stack exchange,提问作者samuel guedon
相关产品推荐
相关产品推荐

