You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 20:45:35