使用 Scrapy 抓取页面,有时候不想全局 DOWNLOAD_DELAY,而是只让某些请求慢一点(比如翻页、访问敏感接口)。 下面是一个按请求定制 delay的小方案,方便以后直接抄。

核心思路

  • 写一个 Downloader Middleware
  • request.meta 里读一个自定义字段:delay_request_by(单位:秒)
  • 如果存在,就用 Twisted 的 reactor.callLater 暂停这次请求一会儿再继续。

Downloader Middleware

middlewares.py

from twisted.internet.defer import Deferred
from twisted.internet import reactor

class DelayedRequestsMiddleware(object):
    # reference from: https://stackoverflow.com/questions/19135875/add-a-delay-to-a-specific-scrapy-request
    def process_request(self, request, spider):
        delay_s = request.meta.get('delay_request_by', None)
        if not delay_s:
            return  # 没有设置就直接放行

        deferred = Deferred()
        reactor.callLater(delay_s, deferred.callback, None)
        return deferred  # 返回 Deferred,让 Scrapy 等待

在 settings.py 中启用

DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.DelayedRequestsMiddleware': 543,
}

myproject.middlewares 换成自己项目的实际路径即可。

在 Spider 里为特定请求加延迟

spider.py

class SomeSpider(scrapy.Spider):
    name = 'some-spider'
    # ...

    def parse(self, response):
        # ...
        yield scrapy.Request(
            next_page,
            callback=self.parse,
            meta={'delay_request_by': 5}  # 这里指定延迟 5 秒
        )
        # ...
  • 不需要延迟的请求:不要delay_request_by,或者设为 None
  • 需要特殊照顾的请求:直接在 meta 里带上秒数即可。