Scarnox · Blog

抓取时如何不被封禁:附代码的实用指南

抓取时被封禁,通常意味着以下三种情况之一:你发送请求的频率过高、过快;你的客户端看起来完全不像浏览器或已知的机器人;或者你的 IP 地址位于网站不太信任的名单上。只要养成几个习惯,就能减少这三种情况:及早发现封禁,网站施压时放慢速度,保持一致且诚实的身份,并且只在适当的地方使用代理。本指南用可运行的代码逐一说明。请仅在你获准抓取的网站上使用。

识别封禁

封禁并不总是以错误页面的形式出现。请注意以下情况:

每次失败都要记录状态码和一小段响应正文示例。如果一个封禁在你的日志中看不到,你就无法修复它。

用重试进行退避

当网站提示“请放慢速度”时,你的代码应该放慢的幅度不止一点点。带尝试次数上限的指数退避是标准做法。如果网站发送了 Retry-After,它也应当遵循该值:

import time
import requests

session = requests.Session()
session.headers.update({
    "User-Agent": "ExampleBot/1.0 (+https://example.com/bot)",
    "Accept-Language": "en-US,en;q=0.9",
})

def polite_get(url: str, max_attempts: int = 4) -> requests.Response:
    delay = 2.0
    resp = None
    for _ in range(max_attempts):
        resp = session.get(url, timeout=30)
        if resp.status_code not in (429, 503):
            return resp
        retry_after = resp.headers.get("Retry-After", "")
        wait = float(retry_after) if retry_after.isdigit() else delay
        time.sleep(wait)
        delay *= 2
    return resp  # 仍被限制:返回最后一个响应,由调用方处理

resp = polite_get("https://example.com/products?page=1")
print(resp.status_code, len(resp.text))

注意这个函数没有做什么。它不会重试 403,也不会轮换身份来绕过某个限制。403 是一个停止的信号,而不是需要解决的谜题。

一步一步诊断封禁

当一次运行开始失败时,请先按以下步骤检查,再改动其他任何东西:

  1. 查看第一个失败的 URL 及其状态码。封禁通常从某个特定位置开始,而不是随机出现。
  2. 比较第一次失败前几分钟的请求频率。突发流量是最常见的原因。
  3. 检查失败是否只集中在某一条路径、某一个板块,或某个时段。
  4. 用正常浏览器手动打开一次同一个 URL。如果浏览器能正常加载,问题出在你的请求模式上。如果浏览器也被封,说明网站暂时不再向你提供该页面。
  5. 之后才去修改代码。先放慢速度,其次加入退避,最后再审视你的身份。

同时改动多个地方,就无法知道是哪一项起了作用。

保持一致、诚实的身份

当身份在请求之间发生变化,或者与其他流量不匹配时,抓取器就容易被封。一个会话加上一个清晰的 User-Agent,是最简单且稳定的身份。给你的机器人起个名字,附上联系链接,并且只发送普通客户端会发送的请求头。为了看起来像许多不同的访客而频繁更换 User-Agent,是一种规避行为,它往往会让封禁更可能发生,而不是更不可能。

如果网站对浏览器和普通 HTTP 客户端提供的内容不同,请对这些页面使用真实的浏览器,并保持同样的速率限制。这是一条正当的途径,也能让请求模式保持合理。

谨慎使用代理

代理会改变请求的来源地址。它是满足合法需求的工具,例如在你获准测试的网站上从特定地区进行测试,或者把负载分散到你自己的基础设施上。它并不是摆脱网站对你设置的限制的手段。代理的格式与代理类型的任务中所用的格式相同:

proxies = {
    "http": "http://user:pass@proxy.example.net:8000",
    "https": "http://user:pass@proxy.example.net:8000",
}
resp = session.get("https://example.com/products", proxies=proxies, timeout=30)

选择代理类型是另一个问题,请参阅住宅代理与数据中心代理对比和抓取用轮换代理。

当封禁变成验证码

有些网站会用验证码而不是硬性封禁来应对速率问题。合理的顺序是:先停下,放慢速度,重新检查规则,然后才考虑识别验证码。对于 PopularCaptcha 控件,Scarnox 的实时 API 会返回一个 token,你需要把它提交到 h-captcha-response 字段中。对于其他验证码类型,请查看当前的上线列表,因为 reCAPTCHA、Cloudflare Turnstile、GeeTest、AWS WAF 和 Arkose 的支持即将推出。详细指南见网页抓取验证码。

如果你在某个网站上开始遇到封禁,原因几乎总是请求模式。抓取时避免验证码介绍了防止出现验证码的习惯。

负责任地抓取

下一步