如何在抓取时避免CAPTCHA(切断信号,而非解决)

解决CAPTCHA既慢又花钱,只是治标不治本。持久的解决方案是永远不触发它:通过清理触发它的信号来降低您的风险评分。

当抓取器遇到CAPTCHA时,本能反应是求助于解决服务。这是错误的本能。CAPTCHA不是一道可以突破的墙——它是风险评分的可见输出,已经决定您看起来像自动化程序。解决它既慢,每次解决都要花钱,而且对降低评分没有任何作用,因此下一次请求又会被挑战。如何在抓取时避免CAPTCHA的持久答案是停止触发它们:清理那些将您的评分推向红色的信号。

为什么解决是失败的选择

考虑reCAPTCHA v2的实际工作原理。网站嵌入一个公共站点密钥;解决挑战会将一个长令牌写入服务器稍后验证的隐藏g-recaptcha-response字段。该令牌设计为单次使用——一种重放保护措施——因此您无法解决一次并重复使用。解决服务(人力农场或机器学习解决器)每次挑战返回一个新令牌,这意味着每次评分保持高时,您都要付费并等待几秒钟。您自动化了症状,而不是消除了原因。

还有一个更微妙的陷阱:显示挑战正是因为页面所有者不希望该路径上有自动化流量。如果存在文档化的API,请使用它。如果没有,务实的目标是看起来足够像普通访问者,以至于风险引擎从未升级。这完全取决于您发送的信号。

风险评分带图示,显示IP声誉、TLS指纹、请求头、请求速率和cookie如何将抓取器推向CAPTCHA
挑战是输出。您实际上可以控制的是产生它的风险评分。

信号1:IP质量是最大的杠杆

最强的输入是请求来自哪里。数据中心IP范围被分类并预先评分;来自其中一个的新请求可以在您发送字节负载之前就开始在风险刻度上升到一半。住宅IP——真实的家庭连接——起点要低得多。这就是为什么经典的现场建议是:使用住宅IP运行,一旦出现挑战,旋转到新的出口,而不是继续使用已烧毁的那个。一个住宅代理池,在90M+ IP中进行每次请求旋转,使这一过程自动化——您永远不会从一个地址抓取整个任务。

在信任任何池之前,先测量它。我们的免费IP质量评分检查器显示反机器人引擎会为出口分配的欺诈/声誉评分——具有高欺诈评分的数据中心IP是一个等待发生的CAPTCHA。如果您想了解为什么声誉在大多数网站上胜过指纹的完整图景,我们关于为什么欺诈评分很重要的文章有更深入的解释。

import requests

# Detect a challenge in the response and rotate the exit instead of retrying
CHALLENGE_MARKERS = ("g-recaptcha", "hcaptcha", "/cdn-cgi/challenge", "captcha-delivery")

def looks_challenged(resp):
    if resp.status_code in (403, 429, 503):
        return True
    body = resp.text[:20000].lower()
    return any(m in body for m in CHALLENGE_MARKERS)

def fetch(url):
    # rotating gateway hands out a new residential IP each request
    proxy = "http://USER:PASS@rotating.quantumproxies.io:8000"
    r = requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=20)
    if looks_challenged(r):
        return None  # burn this exit, the gateway rotates on the next call
    return r

信号2:您的TLS指纹暴露了您

即使在干净的IP上,TLS握手也会暴露您。来自Python或Go默认堆栈的原始请求会产生一个JA3/JA4指纹,看起来与Chrome的完全不同——真实浏览器会宣传特定的密码顺序、扩展和ALPN值,而脚本库不会复制。反机器人引擎会对该握手进行哈希并与已知的机器人签名进行匹配。解决方案是发送一个真实的浏览器指纹,或者通过TLS模拟客户端,或者运行一个实际的浏览器引擎。我们在JA3/JA4指纹工作原理中介绍了这些机制。

信号3:请求头一致性

请求头必须彼此一致,并与指纹一致。一个声称是Windows上的Chrome 120的请求,但省略了匹配的sec-ch-ua客户端提示,按错误顺序发送请求头,或将移动User-Agent与桌面TLS配置文件配对的请求是明显不一致的。不要仅仅设置一个User-Agent——发送一个真实浏览器会发送的完整一致集,并保持与您正在模拟的平台一致。

# Coherent header set that matches a Chrome-on-Windows fingerprint
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                  "AppleWebKit/537.36 (KHTML, like Gecko) "
                  "Chrome/120.0.0.0 Safari/537.36",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "en-US,en;q=0.9",
    "sec-ch-ua": '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
    "sec-ch-ua-platform": '"Windows"',
    "Upgrade-Insecure-Requests": "1",
}
两列清单将CAPTCHA触发信号映射到其缓解措施
从左列工作到右列,挑战首先停止出现。

信号4:行为和速度

每个IP的请求频率是一个主要的速率信号。一个地址每分钟六十个请求读作一个机器人;同样的六十个请求分布在六十个IP上读作六十个人。放慢速度,在请求之间增加抖动,并在池中分配流量。在JavaScript密集的目标上,行为评分还会监控鼠标移动、滚动和停留时间——如果您正在驱动一个无头浏览器,不要立即触发点击。旋转减少了基于IP的阻止;它不能为机枪式请求模式开脱。完整的纪律在我们的反封禁清单中。

信号5:会话和cookie连续性

还有一个更安静的信号:连续性。一个没有cookie、没有引用者和没有历史记录的请求看起来像是凭空出现的——这正是一个天真的机器人所做的。真实用户会积累一个会话:他们登陆一个页面,设置cookie,并在点击之间携带它们。保持会话中的cookie,通过合理的页面进入,而不是冷入一个受保护的路由,并在一个连贯的流程中保持一个身份。在登录中途旋转IP会产生相反的效果——它会破坏连续性并推高评分——这正是粘性会话存在于购物车和登录等有状态步骤的原因。

当挑战不可避免时

有些路由会阻止每个访问者——登录墙、结账、强力保护的搜索。在那里,再多的信号卫生也无法消除挑战,并且手动维护浏览器指纹、TLS模拟和干净池本身就成为一个项目。这就是将整个堆栈交给一个Scraper API的时机,该API携带真实的浏览器指纹,旋转住宅IP,并按需渲染JavaScript——您发送一个URL并获得HTML或JSON,挑战处理已包括在内。比自制的解决农场更少的活动部件和更高的成功率。

从干净的住宅IP开始

常见问题解答

如何在网页抓取时避免CAPTCHA?

降低触发它们的风险评分。从住宅IP而不是被标记的数据中心范围进行抓取,发送真实的浏览器TLS指纹和一致的请求头,调整请求速度并将其分散到多个IP上,并在会话中携带cookie。当出现挑战时,旋转出口而不是重试同一个已烧毁的IP。

解决CAPTCHA还是避免CAPTCHA更好?

避免它们。解决是每次挑战,花费金钱和时间,并且reCAPTCHA令牌是一次性使用的,因此如果风险评分持续高,意味着您每次请求都要再次付费。预防一次性解决原因。将解决保留给无论信号多么干净都挑战每个访问者的罕见路由。

住宅代理能阻止CAPTCHA吗?

它们消除了最大的单一触发因素——糟糕的IP声誉——但它们本身不是一个完整的解决方案。住宅IP与Python默认TLS指纹和机枪式请求速率配对仍会被挑战。结合干净的IP、浏览器指纹、一致的请求头和合理的速度。

为什么即使在住宅IP上我也会遇到CAPTCHA?

因为IP只是一个输入。您的TLS/JA3指纹、请求头一致性、请求速率和缺乏会话cookie仍然会影响评分。一个曾被标记的住宅出口也可能携带最近的历史。使用免费的IP质量工具检查出口,发送真实的浏览器指纹,并在假设IP是问题之前放慢请求速度。

不要将CAPTCHA视为障碍。它是您在它出现之前发送的所有信息的读出。清理IP、指纹、请求头和速度,读出保持绿色——无需解决器。

免费检查您的出口IP的欺诈评分