Scrapy自动限速AutoThrottle完全指南

📂 所属阶段:第三阶段 — 攻防演练(中间件与反爬篇)
🔗 相关章节:Downloader Middleware · 代理IP池集成 · 反爬对抗实战

目录


AutoThrottle是什么?

AutoThrottle是Scrapy内置的下载中间件,替代了静态的DOWNLOAD_DELAY,能根据目标网站的响应负载动态调节请求间隔和并发数,实现“人性化访问”:

  • 既不浪费时间爬得太慢
  • 又不给服务器造成过大压力触发反爬

为什么不直接用静态延迟?

静态延迟太“死板”:

  1. 低峰期网站响应快,但还是按固定5秒爬,效率低
  2. 高峰期网站响应慢,固定延迟反而增加服务器压力/触发限流
  3. 新网站难预估延迟,调大调小都踩坑

工作原理(简化版)

AutoThrottle不需要复杂算法,核心逻辑很直观:

  1. 初始阶段:用AUTOTHROTTLE_START_DELAY作为初始请求间隔
  2. 学习阶段:抓取过程中持续记录平均响应时间
  3. 调节阶段
    • 目标延迟≈平均响应时间÷目标并发数(默认1.0,即单站点“等待上一个响应→发下一个”)
    • 延迟严格控制在AUTOTHROTTLE_START_DELAYAUTOTHROTTLE_MAX_DELAY之间
  4. 并发约束:不会超过全局CONCURRENT_REQUESTS和单域名CONCURRENT_REQUESTS_PER_DOMAIN

核心配置参数

只需要在settings.py里启用并调几个参数即可上手:

# 1. 核心开关
AUTOTHROTTLE_ENABLED = True  # 生产环境必须开,开发调试也建议开

# 2. 延迟范围控制(最重要的两个)
AUTOTHROTTLE_START_DELAY = 3  # 初始请求间隔(秒),小网站1-2,大防护站5-10
AUTOTHROTTLE_MAX_DELAY = 60  # 最大请求间隔(秒),避免异常时无限等待

# 3. 目标并发控制(决定“激进/保守”程度)
AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0  # 默认保守(单站点≈串行)
# 比如设成2.0:单站点可以“同时发2个请求”,效率翻倍但风险增加

# 4. 调试开关(开发时必开)
AUTOTHROTTLE_DEBUG = True  # 控制台输出当前延迟、响应时间等信息

关键参数说明(避坑用)

  • 不要同时设DOWNLOAD_DELAY:AutoThrottle会覆盖/干扰它
  • AUTOTHROTTLE_TARGET_CONCURRENCY别超过全局并发数的1/3:防止单个域名占满资源
  • AUTOTHROTTLE_MAX_DELAY别设太长:比如超过120秒,不如直接暂停重试

场景化高级配置

1. 保守模式(高防护网站:微博、知乎、小红书)

AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 8
AUTOTHROTTLE_MAX_DELAY = 120
AUTOTHROTTLE_TARGET_CONCURRENCY = 0.5  # 更保守:“发一个→等2倍响应时间→发下一个”
CONCURRENT_REQUESTS = 2  # 全局只留2个并发槽位
CONCURRENT_REQUESTS_PER_DOMAIN = 1  # 单个域名绝对串行

2. 平衡模式(普通网站:新闻站、博客站)

AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 2
AUTOTHROTTLE_MAX_DELAY = 60
AUTOTHROTTLE_TARGET_CONCURRENCY = 2.0  # 单站点同时发2个
CONCURRENT_REQUESTS = 16
CONCURRENT_REQUESTS_PER_DOMAIN = 4

3. 激进模式(公开API、数据接口)

AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 0.5
AUTOTHROTTLE_MAX_DELAY = 15
AUTOTHROTTLE_TARGET_CONCURRENCY = 5.0  # 单站点同时发5个
CONCURRENT_REQUESTS = 32
CONCURRENT_REQUESTS_PER_DOMAIN = 8

最简单的自定义限速

如果内置AutoThrottle不够用(比如需要区分AJAX和普通请求、加随机抖动防规律),可以继承内置AutoThrottle修改,不用重写整个中间件:

import random
from scrapy.downloadermiddlewares.autothrottle import AutoThrottle
from urllib.parse import urlparse

class CustomAutoThrottle(AutoThrottle):
    def _adjust_delay(self, slot, delay):
        """
        重写延迟调节函数:
        1. AJAX/API请求降延迟
        2. 所有请求加±20%的随机抖动防规律
        """
        # 获取当前请求的URL
        # 注意:内置AutoThrottle没有直接传request,但可以通过slot.requests_queue获取最后一个(这里简化为仅通过settings区分)
        # 更严谨的方式是重写process_response获取request的URL
        domain = slot.domain
        
        # 内置延迟计算(保留核心逻辑)
        base_delay = super()._adjust_delay(slot, delay)
        
        # 1. 区分接口类型:
        # 这里仅举个例子,实际可以重写process_response/process_request存request的标记
        if 'api' in domain or 'ajax' in domain:
            base_delay *= 0.6  # 接口降60%的延迟
        
        # 2. 加随机抖动(最有效的防规律手段之一)
        jitter = random.uniform(0.8, 1.2)
        final_delay = base_delay * jitter
        
        # 3. 再套一层最小最大限制(更安全)
        final_delay = max(self.start_delay * 0.5, min(final_delay, self.max_delay))
        
        return final_delay

启用自定义中间件

settings.py里替换内置AutoThrottle:

DOWNLOADER_MIDDLEWARES = {
    # 禁用内置的
    'scrapy.downloadermiddlewares.autothrottle.AutoThrottle': None,
    # 启用自定义的,优先级要和内置一致(400左右)
    'myproject.middlewares.CustomAutoThrottle': 400,
}

常见问题与最佳实践

常见问题

1. 爬虫速度还是很慢?

排查方向:

  • 是不是同时设了DOWNLOAD_DELAY?赶紧删掉
  • 是不是AUTOTHROTTLE_TARGET_CONCURRENCY设太低了?调到2-5试试
  • 是不是CONCURRENT_REQUESTS全局限制了?比如设成8,可以提到16-32(只要目标网站不封)

2. 还是被封IP?

  • 别单独依赖AutoThrottle,配合代理IP池User-Agent轮换Cookie池一起用
  • 换保守模式:AUTOTHROTTLE_TARGET_CONCURRENCY=0.5,单域名串行
  • 观察AutoThrottle的调试日志,如果延迟一直压在MAX_DELAY,说明网站负载很高,建议暂停10-30分钟再爬

3. AutoThrottle的延迟总是跳变?

这是正常的!因为它是动态学习的,刚开始数据少跳变多,爬20-30个页面后就稳定了。


最佳实践

  1. 开发先用保守模式+调试日志:先摸清楚目标网站的响应特性
  2. 必须加随机抖动:不管是内置还是自定义,防规律是反爬的关键
  3. 单个域名的并发别超过4:除非是明确的公开API
  4. 定期保存爬取状态:用Scrapy的JOBDIR,如果被封可以暂停换IP后继续
  5. 监控响应状态码:如果连续出现429(限流)、403(禁止访问),立即暂停并调整策略

💡 核心要点总结: AutoThrottle是Scrapy最实用的反爬工具之一,不要自己瞎写复杂的限速算法,先把内置的用好,再根据需求微调。配合代理、UA轮换、Cookie,能解决80%的反爬问题。