API Tutorials

如何使用 API 解决 Cloudflare 挑战

网站突然弹出一整页“正在检查您的浏览器……”,刷新也没用,这就是 Cloudflare Challenge——圈内也叫它“五秒盾”。它和 Cloudflare Turnstile 不是一回事:Turnstile 只是表单里的一个小部件,Challenge 会把整个页面拦下来,不通过验证就什么都拿不到。

CaptchaAI 用真实浏览器实例过这一关,返回一个 qa_session_cookie cookie,把它和对应的 User-Agent 一起带上,你的请求就能正常访问被保护的页面了。

在动手写代码前,先记住三条 Cloudflare Challenge 独有的硬性规则——这也是它和 CaptchaAI 支持的其他验证码类型最大的区别:

  1. 必须带代理 —— 求解器要用你自己的代理去解,这样 qa_session_cookie 才会绑定到你能控制的 IP 上。
  2. User-Agent 也是返回结果的一部分 —— cookie 和一个具体的 User-Agent 字符串绑定,后续请求必须原样使用响应里给的那个 User-Agent,不能用你自己浏览器的。
  3. IP 和 User-Agent 必须全程一致 —— 之后访问受保护页面的每一次请求,代理和 User-Agent 都要和解题时完全相同,换一个都会被打回验证页。

下面给出 Python、Node.js、PHP 三套可直接跑起来的完整实现。

找的是 Cloudflare Turnstile? 那是另一种验证码,解法也不一样。看这篇:如何用 API 解决 Cloudflare Turnstile


为什么用 CaptchaAI 处理 Cloudflare 挑战

自己搭浏览器集群硬啃这道全页拦截,投入产出比并不划算。CaptchaAI 值得用的原因:

  • 真实浏览器求解 —— 用真实浏览器实例过 Cloudflare 的 JavaScript 验证,不是简单模拟,遇到升级也不容易失效。
  • 一次返回你要的全部信息 —— qa_session_cookie 和对应的 User-Agent 在同一个响应里全给你,不用再拼凑。
  • 支持多种代理协议 —— HTTP、HTTPS、SOCKS4、SOCKS5 都能用,接入你现有的代理池不用改造。
  • 和其他类型统一的 API 模式 —— 提交 → 轮询 → 取结果,和 CaptchaAI 支持的所有其他验证码类型是同一套流程,学一次到处用。
  • 按线程计费 —— 基于线程的套餐,最低 BASIC $15/月(5 线程)起,同一线程内解多少次都不额外收费。

本地化测试要点

在国内网络环境下测试 Cloudflare 保护的站点,有两点特别容易踩坑:

  • 出口 IP 信誉分:跨境电商团队做 checkout 流程 QA 时经常遇到这个问题——测试用的 VPS 常开在中国香港或新加坡,公共云出口 IP 信誉分不如本地住宅网络,Cloudflare 的风控就更容易触发整页拦截。让求解器带上你自己控制、信誉分较高的代理去过验证,比反复更换测试节点省事得多。
  • 合规边界:不管是用 API 自动过验证,还是人工点开浏览器,抓取的内容都应限定在你自己有权访问的范围内,符合《网络安全法》《数据安全法》与 PIPL(个人信息保护法)对数据处理的要求——这也是本文默认的适用场景。

开始前需要准备什么

准备好下面这些,再动手写代码:

  • CaptchaAI API Key —— 在 captchaai.com/api.php 获取,32 位字符串。
  • 目标页面 URL —— 被 Cloudflare 保护的那个页面的完整地址。
  • 一个能用的代理 —— HTTP、HTTPS、SOCKS4 或 SOCKS5 都行,前提是这个代理能连到目标站点。
  • 账号开通代理权限 —— CaptchaAI 账号默认不开代理功能,第一次请求前得先联系 CaptchaAI 支持开通。
  • 运行环境 —— Python 3.7+ 配 requests,或 Node.js 18+(自带 fetch)。

注意: 没开通代理权限,method=cloudflare_challenge 是用不了的。还没开通的话,先去 CaptchaAI 提工单。


如何判断你踩到的是 Cloudflare 挑战

Cloudflare 挑战是整页拦截,不是嵌入式小部件。出现下面任意一种情况,基本可以确定就是它:

  • 内容加载前先弹出“正在检查您的浏览器……”或“请稍候……”。
  • URL 一闪而过目标路径,紧接着就跳转到验证页。
  • 响应头带 cf-mitigated: challenge,或者状态码是 403,附带一个 Cloudflare Ray ID。
  • HTML 里能找到 <div id="challenge-body-text">,或者引用了 /cdn-cgi/challenge-platform/
  • 通过验证后,浏览器里会出现一个 qa_session_cookie cookie。

如果你看到的是表单里嵌了一个带复选框或转圈动画的小部件,那不是 Challenge,是 Cloudflare Turnstile——解法完全不同,别用错方法。


Cloudflare 挑战和 Turnstile 到底差在哪

两种保护长得不像,解法也不一样,先分清楚再动手:

Cloudflare 挑战 Cloudflare Turnstile
长什么样 整页拦截插页(“正在检查您的浏览器……”) 表单上一个小部件(复选框或转圈动画)
API 方法 cloudflare_challenge turnstile
要不要代理 要,强制 不用,可选
拿到什么 qa_session_cookie cookie + User-Agent Turnstile token
怎么用结果 每次请求都带上 cookie + User-Agent + 同一个代理 把 token 塞进表单字段,提交表单
和 IP 的关系 cookie 绑定代理 IP token 不绑定 IP
常见场景 需要先过整页拦截,才能拿到页面上的公开内容 提交受保护的表单(登录、注册、结账)

如果你面对的是表单里的小部件,用这篇 Turnstile 教程才对路。


求解流程

整个过程拆开看,其实就是提交、等待、轮询、拿结果四步:

Identify Cloudflare 验证流程 page
              ↓
  POST to in.php
    method=cloudflare_challenge
    pageurl + proxy + proxytype
              ↓
       receive captcha ID
              ↓
     wait 20 seconds
              ↓
  GET res.php (action=get, id=…, json=1)
      ↓                      ↓
 CAPCHA_NOT_READY       status=1
  (wait 5s, retry)           ↓
                  extract qa_session_cookie + user_agent
                             ↓
              set cookie + User-Agent + same proxy
                             ↓
                   access protected page

Python 实现

requests 就够了,不需要额外依赖:

import time
import requests

API_KEY = "YOUR_CAPTCHAAI_API_KEY"
PAGE_URL = "https://example.com/protected-page"
PROXY = "user:[email protected]:8080"
PROXY_TYPE = "HTTP"

SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"


def solve_cloudflare_challenge(api_key, pageurl, proxy, proxytype):
    """Solve a Cloudflare 验证流程 and return qa_session_cookie cookie + User-Agent."""

    # Step 1: Submit the task
    submit_resp = requests.post(
        SUBMIT_URL,
        data={
            "key": api_key,
            "method": "cloudflare_challenge",
            "pageurl": pageurl,
            "proxy": proxy,
            "proxytype": proxytype,
            "json": 1,
        },
        timeout=30,
    )
    submit_resp.raise_for_status()
    submit_data = submit_resp.json()

    if submit_data.get("status") != 1:
        raise RuntimeError(f"Submit failed: {submit_data}")

    captcha_id = submit_data["request"]
    print(f"Task created — captcha ID: {captcha_id}")

    # Step 2: Wait before first poll (Cloudflare 验证流程 takes longer)
    time.sleep(20)

    # Step 3: Poll for result
    for _ in range(60):
        result_resp = requests.get(
            RESULT_URL,
            params={
                "key": api_key,
                "action": "get",
                "id": captcha_id,
                "json": 1,
            },
            timeout=30,
        )
        result_resp.raise_for_status()
        result_data = result_resp.json()

        if result_data.get("request") == "CAPCHA_NOT_READY":
            time.sleep(5)
            continue

        if result_data.get("status") == 1:
            return {
                "qa_session_cookie": result_data["result"],
                "user_agent": result_data["user_agent"],
            }

        raise RuntimeError(f"Polling error: {result_data}")

    raise TimeoutError("Cloudflare 验证流程 solve timed out")


# Solve the challenge
solution = solve_cloudflare_challenge(API_KEY, PAGE_URL, PROXY, PROXY_TYPE)
print(f"qa_session_cookie: {solution['qa_session_cookie']}")
print(f"User-Agent: {solution['user_agent']}")

# Step 4: Access the protected page using the SAME proxy and User-Agent
session = requests.Session()
session.headers.update({"User-Agent": solution["user_agent"]})
session.cookies.set("qa_session_cookie", solution["qa_session_cookie"], domain="example.com")

proxies = {
    "http": f"http://{PROXY}",
    "https": f"http://{PROXY}",
}

response = session.get(PAGE_URL, proxies=proxies, timeout=30)
print(f"Status: {response.status_code}")
print(f"Content length: {len(response.text)} chars")

这段代码做了什么:

  1. 带上 method=cloudflare_challenge、页面 URL 和你的代理,向 in.php 提交任务。
  2. 先等 20 秒,再开始每 5 秒轮询一次 res.php
  3. 拿到 qa_session_cookie 的值和求解器实际用的 User-Agent 字符串。
  4. 用同一个代理、同一个 cookie、同一个 User-Agent 去请求受保护页面。

划重点: qa_session_cookie 同时绑定代理 IP 和 User-Agent,两者只要变一个,Cloudflare 就会拒绝请求,重新弹出验证页。


Node.js 实现

技术栈是 Node.js 的话,逻辑完全一样,直接照抄:

const API_KEY = "YOUR_CAPTCHAAI_API_KEY";
const PAGE_URL = "https://example.com/protected-page";
const PROXY = "user:[email protected]:8080";
const PROXY_TYPE = "HTTP";

const SUBMIT_URL = "https://ocr.captchaai.com/in.php";
const RESULT_URL = "https://ocr.captchaai.com/res.php";

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function solveCloudflareChallenge(apiKey, pageurl, proxy, proxytype) {
  // Step 1: Submit the task
  const submitResp = await fetch(SUBMIT_URL, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      key: apiKey,
      method: "cloudflare_challenge",
      pageurl: pageurl,
      proxy: proxy,
      proxytype: proxytype,
      json: "1",
    }),
  });

  const submitData = await submitResp.json();
  if (submitData.status !== 1) {
    throw new Error(`Submit failed: ${JSON.stringify(submitData)}`);
  }

  const captchaId = submitData.request;
  console.log(`Task created — captcha ID: ${captchaId}`);

  // Step 2: Wait before first poll
  await sleep(20_000);

  // Step 3: Poll for result
  for (let i = 0; i < 60; i++) {
    const resultResp = await fetch(
      `${RESULT_URL}?${new URLSearchParams({
        key: apiKey,
        action: "get",
        id: captchaId,
        json: "1",
      })}`
    );

    const resultData = await resultResp.json();

    if (resultData.request === "CAPCHA_NOT_READY") {
      await sleep(5_000);
      continue;
    }

    if (resultData.status === 1) {
      return {
        cfClearance: resultData.result,
        userAgent: resultData.user_agent,
      };
    }

    throw new Error(`Polling error: ${JSON.stringify(resultData)}`);
  }

  throw new Error("Cloudflare 验证流程 solve timed out");
}

(async () => {
  const solution = await solveCloudflareChallenge(
    API_KEY,
    PAGE_URL,
    PROXY,
    PROXY_TYPE
  );
  console.log(`qa_session_cookie: ${solution.cfClearance}`);
  console.log(`User-Agent: ${solution.userAgent}`);

  // Step 4: Access protected page with cookie, User-Agent, and same proxy
  // Note: Node.js fetch does not natively support proxies.
  // Use a proxy agent library like undici, https-proxy-agent, or node-fetch with proxy.
  // Example with undici:
  //
  // import { ProxyAgent } from 'undici';
  // const proxyAgent = new ProxyAgent(`http://${PROXY}`);
  //
  // const response = await fetch(PAGE_URL, {
  //   headers: {
  //     'User-Agent': solution.userAgent,
  //     'Cookie': `qa_session_cookie=${solution.cfClearance}`,
  //   },
  //   dispatcher: proxyAgent,
  // });

  console.log("Use the qa_session_cookie cookie and User-Agent with the same proxy for all subsequent requests.");
})();

PHP 实现

用原生 curlfile_get_contents 就能写完,不需要额外的包:

<?php
$apiKey    = "YOUR_CAPTCHAAI_API_KEY";
$pageUrl   = "https://example.com/protected-page";
$proxy     = "user:[email protected]:8080";
$proxyType = "HTTP";

// Step 1: Submit the task
$submit = file_get_contents("https://ocr.captchaai.com/in.php?" . http_build_query([
    "key"       => $apiKey,
    "method"    => "cloudflare_challenge",
    "pageurl"   => $pageUrl,
    "proxy"     => $proxy,
    "proxytype" => $proxyType,
    "json"      => 1,
]));

$submitData = json_decode($submit, true);
if ($submitData["status"] !== 1) {
    die("Submit failed: " . $submit);
}

$captchaId = $submitData["request"];
echo "Task created — captcha ID: $captchaId\n";

// Step 2: Wait and poll
sleep(20);

$cfClearance = null;
$userAgent   = null;

for ($i = 0; $i < 60; $i++) {
    $result = file_get_contents("https://ocr.captchaai.com/res.php?" . http_build_query([
        "key"    => $apiKey,
        "action" => "get",
        "id"     => $captchaId,
        "json"   => 1,
    ]));

    $resultData = json_decode($result, true);

    if ($resultData["request"] === "CAPCHA_NOT_READY") {
        sleep(5);
        continue;
    }

    if ($resultData["status"] === 1) {
        $cfClearance = $resultData["result"];
        $userAgent   = $resultData["user_agent"];
        echo "qa_session_cookie: $cfClearance\n";
        echo "User-Agent: $userAgent\n";
        break;
    }

    die("Polling error: " . $result);
}

if (!$cfClearance) {
    die("Solve timed out");
}

// Step 3: Access the protected page
$ch = curl_init($pageUrl);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_PROXY          => "123.123.123.123:8080",
    CURLOPT_PROXYUSERPWD   => "user:password",
    CURLOPT_HTTPHEADER     => ["User-Agent: $userAgent"],
    CURLOPT_COOKIE         => "qa_session_cookie=$cfClearance",
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo "Status: $httpCode\n";
echo "Content length: " . strlen($response) . " chars\n";

常见错误与避坑指南

  1. 没带代理 —— API 直接拒绝请求。请求里补上 proxyproxytype,Cloudflare 挑战必须带代理,没有商量余地。
  2. 解题用一个代理,访问页面又换了一个 —— Cloudflare 拒绝这个 qa_session_cookie。解题和后续访问,代理必须是同一个,一个字符都不能差。
  3. 用自己浏览器的 User-Agent —— Cloudflare 拒绝请求。只用 API 响应里 user_agent 字段返回的那个值,别自己拼一个。
  4. 请求没带 json=1 —— 响应里没有 user_agent 字段。请求始终带上 json=1,响应才会同时包含 result(也就是 qa_session_cookie)和 user_agent
  5. 账号没开代理权限 —— API 直接报错。发请求前先联系 CaptchaAI 支持,把账号的代理权限开通。
  6. cookie 过期了还在用 —— 过一阵子又开始被拦截。qa_session_cookie 有有效期,一旦请求又开始被拦,就重新解一次。典型有效期是 15–30 分钟。
  7. 把挑战和 Turnstile 搞混 —— 方法传错,直接解题失败。整页拦截 = cloudflare_challenge;表单里的小部件 = turnstile,别用混了。

故障排查

ERROR_BAD_PROXY

代理本身连不上,或者被标记为不可用。解决方法:

  1. 先单独测一下这个代理,能不能连到目标站点。
  2. 换一个代理试试。
  3. 检查格式对不对:账号密码认证要写成 login:password@IP:PORT,纯 IP 认证就是 IP:PORT

ERROR_PROXY_CONNECTION_FAILED

CaptchaAI 没能通过你的代理加载验证页。可能是代理临时掉线,也可能是目标站点把这个代理拉黑了。换个代理再试。

ERROR_CAPTCHA_UNSOLVABLE

这次挑战解不了。常见原因:

  • 代理太慢或者不稳定。
  • 目标站点在 Cloudflare 之外还叠了别的防护。
  • 换一个新的代理,重新发一次请求再试。

要么是 cookie 过期,要么是 Cloudflare 把验证机制轮换了。重新解一次,拿到新的 cookie 就行。建议主动监控成功率,在 cookie 过期前提前刷新,不要等到被拦了才补救。

响应里没有 user_agent 字段

请求没带 json=1。不带这个参数,响应就是纯文本,不会包含 User-Agent 信息。Cloudflare 挑战必须始终带上 json=1

按顺序检查这三点:

  1. 代理和解题时用的是不是同一个
  2. User-Agent 是不是原样用了 user_agent 响应字段里的值
  3. cookie 的 domain 和目标站点是否匹配

三条都对还是 403,大概率是 cookie 已经过期了,重新解一次。

完整的 CaptchaAI 错误码列表,参考错误处理参考(即将上线)或 API 文档站 docs.captchaai.com


完整的可运行示例

想要一个包含环境配置、轮询、重试和错误处理的完整项目?

在 GitHub 上查看完整可运行示例 →


常见问题

国内网络访问海外 Cloudflare 站点,为什么更容易弹出验证页?

Cloudflare 的风控会综合考虑访问来源的 IP 信誉、ASN 归属和历史行为。国内出口 IP、机房 IP、公共云 IP 往往信誉分偏低,触发整页验证的概率明显更高,这和你的代码写得好不好没关系,是网络层面的风控结果。用一个信誉良好、能稳定到达目标站点的代理,是最直接的解决办法。

典型有效期是 15–30 分钟,具体因站点而异,有些站点设置的过期时间更长。与其猜测,不如直接监控请求状态:一旦开始收到 403,就说明 cookie 失效了,立刻重新解一次即可。

可以,只要这些请求用的是解题时那同一个代理和同一个 User-Agent。CaptchaAI 按线程计费,一个线程里可以反复用同一个已解好的 cookie 发多次请求,直到它过期为止,不需要每个请求都重新解一次。

一直报 ERROR_CAPTCHA_UNSOLVABLE,是不是我的代理有问题?

大概率是。先单独测试这个代理能不能正常连到目标站点,再看看目标站点是不是在 Cloudflare 之外还叠加了别的防护层。多数情况下换一个更稳定的代理、重新发起请求就能解决。

不带代理能解决 Cloudflare 挑战吗?

不能。这是这个验证码类型的硬性要求,因为 Cloudflare 本身就是靠“解题 IP 和后续访问 IP 必须一致”这条规则来判断合法性的,CaptchaAI 没法绕开这条底层规则。


立即开始解决 Cloudflare 挑战

  1. 开通代理权限 —— 还没开的话先联系 CaptchaAI 支持
  2. 拿到 API Key —— captchaai.com/api.php
  3. 准备好代理 —— HTTP/HTTPS/SOCKS4/SOCKS5 均可,前提是能连到目标站点
  4. 复制上面 Python、Node.js 或 PHP 的代码 —— 把占位符换成你自己的 Key、URL 和代理
  5. 后续所有请求都带上返回的 qa_session_cookie + User-Agent + 同一个代理

如果还是卡住,看看上面的故障排查部分,或者查完整的 Cloudflare 挑战错误码与修复方法

相关文章

该文章已禁用评论。