Scarnox · Blog

如何用 Python 识别 PopularCaptcha(可运行示例,2026)

如果你的 Python 脚本遇到了 PopularCaptcha,你不需要浏览器集群,也不需要自己的模型。你只需要页面的 sitekey、一次 POST 请求,以及返回的 token。

本指南会带你在大约五分钟内拿到一个可用的 token:获取 API 密钥,找到 sitekey,请求 token,把它提交到表单,并处理脚本无人值守运行时会出现的失败情况。识别失败不收费,所以重试只花费时间。

Scarnox 目前可以识别 PopularCaptcha,无代理或使用你自己的代理均可。reCAPTCHA、Cloudflare Turnstile 和 AWS WAF 等其他验证码类型即将支持,因此本文所有示例都使用 PopularCaptcha。验证码类型概述解释了它们之间的区别。

1. 获取 API 密钥

注册账户,免费试用额度,无需信用卡。你的 API 密钥在控制台中。请不要把它写进源代码,而是从环境变量中读取:

export SCARNOX_KEY="scarnox_YOURAPIKEY"

2. 找到 sitekey

打开带有验证码的页面,打开开发者工具,然后在元素面板中搜索 data-sitekey。PopularCaptcha 的 sitekey 是 UUID:

<div class="h-captcha" data-sitekey="10000000-ffff-ffff-ffff-000000000001"></div>

HTML 中找不到 data-sitekey?控件很可能是由 JavaScript 渲染的。请检查网络面板中控件的配置请求,其 sitekey 查询参数就是你需要的值。其他查找方法涵盖了 iframe 的 URL 和隐形控件。

把显示控件的页面 URL 作为 site_url 发送。域名最重要,但发送完整的页面 URL 是最稳妥的选择。

3. 请求 token

import os
import requests

API_URL = "https://api.scarnox.com/api/tasks/create"
API_KEY = os.environ["SCARNOX_KEY"]

def solve_captcha(sitekey: str, page_url: str) -> str:
    r = requests.post(
        API_URL,
        headers={"Authorization": f"Bearer {API_KEY}"},
        json={
            "captcha_type": "PopularCaptchaTaskProxyless",
            "site_key": sitekey,
            "site_url": page_url,
        },
        timeout=185,  # 调用会阻塞,直到 token 准备好
    )
    data = r.json()
    if data.get("errorId") != 0:
        raise RuntimeError(f"{data.get('errorCode')}: {data.get('errorDescription')}")
    return data["solution"]["token"]

if __name__ == "__main__":
    token = solve_captcha("10000000-ffff-ffff-ffff-000000000001", "https://example.com/signup")
    print(token[:40], "...")

这就是完整的集成。没有轮询循环,也没有第二个“获取结果”的调用。请求会在 token 准备好时返回,因此唯一需要设置好的参数,就是足以覆盖较慢识别的超时时间。使用约 185 秒。

成功的响应中 errorId 为 0,token 位于 solution.token 下。其他任何 errorId 都表示识别失败,响应中会包含说明原因的 errorCode 和 errorDescription。

4. 提交 token

Token 应放在网站期望验证码响应的位置。对于 PopularCaptcha,几乎总是一个名为 h-captcha-response 的表单字段。许多网站也接受 g-recaptcha-response,因此两个都发送:

import requests

def sign_up(session: requests.Session, form_url: str, email: str, sitekey: str) -> requests.Response:
    token = solve_captcha(sitekey, form_url)
    return session.post(form_url, data={
        "email": email,
        "h-captcha-response": token,
        "g-recaptcha-response": token,
    }, timeout=30)

这里有两个细节很关键。真实的网站通常还会检查页面加载时的会话 Cookie,因此在提交之前,请用同一个 requests.Session 加载表单。另外,应当提交到表单的 action URL,它不一定就是页面 URL。

在提交之前立即识别。Token 大约两分钟后过期,所以不要在一个长任务开始时就获取它然后一直保留。如果网站拒绝了 token,请在开发者工具中打开表单,确认隐藏字段的名称。

5. 处理失败

识别失败不收费,因此重试只花费时间。但有些错误重试也不会成功,所以不要盲目循环。每次都要检查 errorId,记录 errorCode 和 errorDescription,并且只对临时性失败进行重试。

网络错误需要单独处理,因为 requests 在没有任何响应的情况下就会抛出异常:

import requests

try:
    token = solve_captcha(sitekey, page_url)
except requests.RequestException as exc:
    print("network problem, retry later:", exc)
except RuntimeError as exc:
    print("solve rejected:", exc)

错误处理指南提供了一套完整的重试策略,包括退避和抖动。

常见错误

免费试用

注册账户,免费试用额度,无需信用卡。在将示例接入更大的任务之前,先在你自己的表单上运行上面的示例。大规模识别的费用,请查看当前定价。

记录每次识别

每次识别记录一行日志:sitekey、页面的主机名、errorId、失败时的 errorCode,以及调用耗时。日志中不要包含 API 密钥和代理密码。这些记录能告诉你,一次缓慢的运行到底是慢在页面、代理还是识别服务。在你提交支持请求时,它们也是你最先需要的信息。

循环中的识别

如果你的脚本需要连续识别许多验证码,请保持循环简单:识别、提交、检查结果,然后再继续下一个。不要提前识别并把 token 放进队列,因为每个 token 都会在约两分钟内过期。同一网站上的两次识别之间留一小段停顿,可以让你的请求频率接近人类的操作节奏。

下一步