API 文档

关于可用端点、请求格式、参数以及示例响应的参考,帮助你将 API 集成到自己的应用中。

本文档面向希望将 CapSkip 直接集成到自己脚本、应用或自动化系统中的开发者。使用第三方软件的用户请参阅“教程”部分获取设置说明。

CapSkip 模拟广泛使用的验证码识别服务的 API,使其无需任何改动即可连接到兼容的第三方软件。集成通常只需运行 CapSkip。

本文档说明如何提交请求和获取结果。CapSkip 支持多个 API 系列,包括 2Captcha 风格 API(in.php / res.php)、AntiCaptcha、CapMonster、CapSolver、NextCaptcha 和 EzCaptcha 使用的 JSON createTask / getTaskResult API,以及 DeathByCaptcha REST API。同一系列中的服务共享相同的端点、请求和响应格式,每个服务只有基础 URL(主机和端口)不同。

API 家族服务
2captcha 风格2captcha.com, rucaptcha.com, solvecaptcha.com, captchas.io
JSON(createTask / getTaskResult)anti-captcha.com, capmonster.cloud, capsolver.com, nextcaptcha.com, ez-captcha.com
DeathByCaptchadeathbycaptcha.com

图片验证码

普通验证码是一张包含扭曲但人类可读文字的图片。要识别它,用户必须输入图片中显示的文字。

要识别普通验证码,请通过 HTTP POST 请求将图片提交到 API 端点。使用配置好的本地地址和端口,将请求直接发送到你的 CapSkip 实例,例如: http://127.0.0.1:PORT/in.php

CapSkip 接受 multipart/form-data 或 Base64 编码格式的图片。

Multipart 示例表单

<form method="post" action="http://127.0.0.1:PORT/in.php" enctype="multipart/form-data">
<input type="hidden" name="method" value="post">
Your key:
<input type="text" name="key" value="YOUR_APIKEY">
The CAPTCHA file:
<input type="file" name="file">
<input type="submit" value="Upload and get the ID">
</form>

YOUR_APIKEY 表示你的 API 密钥(如果在 CapSkip 中启用了 API 密钥校验)。如果禁用了 API 密钥校验,任何字符串值都会被接受。

Base64 示例表单

<form method="post" action="http://127.0.0.1:PORT/in.php">
<input type="hidden" name="method" value="base64">
Your key:
<input type="text" name="key" value="YOUR_APIKEY">
The CAPTCHA file body in base64 format:
<textarea name="body">BASE64_FILE</textarea>
<input type="submit" value="Upload and get the ID">
</form>

YOUR_APIKEY 表示你的 API 密钥(如果在 CapSkip 中启用了 API 密钥校验)。如果禁用了 API 密钥校验,任何字符串值都会被接受。

BASE64_FILE 是 Base64 编码的图片数据。

POST 请求参数列表
POST 参数 类型 是否必填 说明
key 字符串 是 你的 CapSkip API 密钥。
method 字符串 是 post:使用 multipart/form-data 提交图片
base64:以 Base64 编码字符串提交图片
file 文件 是* 验证码图片文件。
* 当 method=post 时必填。
body 字符串 是* Base64 编码的验证码图片数据。
* 当 method=base64 时必填。
json 整数
默认:0
否 0:以纯文本返回响应
1:以 JSON 格式返回响应

提交验证码(multipart 文件上传):

curl -X POST -F "key=YOUR_API_KEY" -F "method=post" -F "[email protected]" http://127.0.0.1:8080/in.php

提交验证码(base64 编码):

curl -X POST -d "key=YOUR_API_KEY&method=base64&body=BASE64_IMAGE_DATA" http://127.0.0.1:8080/in.php

提交请求后,如果一切正确,CapSkip 会以纯文本返回验证码 ID: OK|12345

如果使用了 json=1 参数,响应将以 JSON 格式返回:

{
    "status":1,
    "request":"12345"
}

等待 1 秒,然后向结果端点(/res.php)发送带有返回的验证码 ID 的 HTTP GET 请求。

如果验证码已被识别,CapSkip 会以纯文本返回结果: OK|TEXT

如果指定了 json=1 ,响应将是:

{
    "status":1,
    "request":"TEXT"
}

如果验证码尚未识别完成,CapSkip 会返回: CAPCHA_NOT_READY

这种情况下,等待 1 秒并重复请求,直到收到最终结果。如果 CapSkip 返回空的响应体,说明结果已被读取或该 ID 不存在。每个结果只能读取一次。

GET 请求参数列表
GET 参数 类型 是否必填 说明
key 字符串 是 你的 CapSkip API 密钥。
action 字符串 是 get:获取所提交验证码的答案。
id 整数 是 由以下方法返回的验证码 ID in.php.
json 整数
默认:0
否 0:以纯文本返回响应
1:以 JSON 格式返回响应
curl "http://127.0.0.1:8080/res.php?key=YOUR_API_KEY&action=get&id=CAPTCHA_ID"

recaptcha v2 solver  reCAPTCHA V2

reCAPTCHA v2,也称为“我不是机器人”reCAPTCHA,是一种广泛使用的验证码类型。访客勾选一个复选框后,Google 要么立即放行,要么要求其选出匹配的图片,之后才能提交表单。

要识别 reCAPTCHA v2,请发送 googlekey 和 pageurl 参数,连同 method=userrecaptcha 以及你的 CapSkip API 密钥。

你可以获取 googlekey 使用以下方法之一:

右键点击 reCAPTCHA 小组件并选择 检查。找到一个以如下开头的 URL:

www.google.com/recaptcha/api2/anchor

复制该 URL 中 k 参数的值。或者,在页面源代码中找到 data-sitekey 属性并复制其值。

CapSkip sitekey

获得 site key 后,向以下地址提交 HTTP GET 或 POST 请求: http://127.0.0.1:PORT/in.php

POST 请求参数列表
参数 类型 是否必填 说明
key 字符串 是 你的 CapSkip API 密钥。
method 字符串 是 userrecaptcha:指定一个 reCAPTCHA v2 请求。
googlekey 字符串 是 在目标页面上找到的 k 或 data-sitekey 参数的值。
pageurl 字符串 是 reCAPTCHA 所在页面的完整 URL。
enterprise 整数
默认:0
否 1:表示 reCAPTCHA Enterprise v2。
0:标准 reCAPTCHA v2。
invisible 整数
默认:0
否 1:表示隐形 reCAPTCHA。
0:标准复选框 reCAPTCHA。
data-s 字符串 否 在页面上找到的 data-s 参数的值。适用于 Google 搜索和某些 Google 服务。
json 整数
默认:0
否 0:以纯文本返回响应。
1:以 JSON 格式返回响应。
proxy 字符串 否 代理地址。IP 认证格式: IP:PORT (示例: 123.123.123.123:3128)。登录/密码认证格式: login:password@IP:PORT
proxytype 字符串 否 代理类型。支持的值: HTTP, HTTPS, SOCKS5, SOCKS5H。默认: HTTP 当提供了 proxy 但省略了 proxytype 时。

提交 reCAPTCHA v2(标准):

curl -X POST -d "key=YOUR_API_KEY&method=userrecaptcha&googlekey=6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-&pageurl=https://example.com" http://127.0.0.1:8080/in.php

提交 reCAPTCHA v2(隐形):

curl -X POST -d "key=YOUR_API_KEY&method=userrecaptcha&googlekey=6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-&pageurl=https://example.com&invisible=1" http://127.0.0.1:8080/in.php

提交 Enterprise reCAPTCHA v2:

curl -X POST -d "key=YOUR_API_KEY&method=userrecaptcha&googlekey=6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-&pageurl=https://example.com&enterprise=1" http://127.0.0.1:8080/in.php

提交 Enterprise reCAPTCHA v2(隐形):

curl -X POST -d "key=YOUR_API_KEY&method=userrecaptcha&googlekey=6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-&pageurl=https://example.com&enterprise=1&invisible=1" http://127.0.0.1:8080/in.php

如果请求成功,CapSkip 会以纯文本返回验证码 ID: OK|12345

如果使用了 json=1 参数,响应将以 JSON 格式返回:

{
    "status":1,
    "request":"12345"
}

如果请求失败,CapSkip 会返回一个错误代码。

等待 15 到 20 秒,然后向结果端点发送 HTTP GET 请求以获取答案: http://127.0.0.1:PORT/res.php

GET 请求参数列表
GET 参数 类型 是否必填 说明
key 字符串 是 你的 CapSkip API 密钥。
action 字符串 是 get:获取所提交验证码的答案。
id 整数 是 由以下方法返回的验证码 ID in.php.
json 整数
默认:0
否 0 以纯文本返回响应。 1 以 JSON 返回响应,并在可用时包含 userAgent 值。
curl "http://127.0.0.1:8080/res.php?key=YOUR_API_KEY&action=get&id=CAPTCHA_ID"

如果验证码已被识别,CapSkip 会以纯文本或 JSON 响应并返回答案 token。token 看起来类似如下:

03AHJ_Vuve5Asa4koK3KSMyUkCq0vUFCR5Im4CwB7PzO3dCxIo11i53epEraq-uBO5mVm2XRikL8iKOWr0aG50sCuej9bXx5qcviUGSm4iK4NC_Q88flavWhaTXSh0VxoihBwBjXxwXuJZ-WGN5Sy4dtUl2wbpMqAj8Zwup1vyCaQJWFvRjYGWJ_TQBKTXNB5CCOgncqLetmJ6B6Cos7qoQyaB8ZzBOTGf5KSP6e-K9niYs772f53Oof6aJeSUDNjiKG9gN3FTrdwKwdnAwEYX-F37sI_vLB1Zs8NQo0PObHYy0b0sf7WSLkzzcIgW9GR0FwcCCm1P8lB-50GQHPEBJUHNnhJyDzwRoRAkVzrf7UkV8wKCdTwrrWqiYDgbrzURfHc2ESsp020MicJTasSiXmNRgryt-gf50q5BMkiRH7osm4DoUgsjc_XyQiEmQmxl5sqZP7aKsaE-EM00x59XsPzD3m3YI6SRCFRUevSyumBd7KmXE8VuzIO9lgnnbka4-eZynZa6vbB9cO3QjLH0xSG3-egcplD1uLGh79wC34RF49Ui3eHwua4S9XHpH6YBe7gXzz6_mv-o-fxrOuphwfrtwvvi2FGfpTexWvxhqWICMFTTjFBCEGEgj7_IFWEKirXW2RTZCVF0Gid7EtIsoEeZkPbrcUISGmgtiJkJ_KojuKwImF0G0CsTlxYTOU2sPsd5o1JDt65wGniQR2IZufnPbbK76Yh_KI2DY4cUxMfcb2fAXcFMc9dcpHg6f9wBXhUtFYTu6pi5LhhGuhpkiGcv6vWYNxMrpWJW_pV7q8mPilwkAP-zw5MJxkgijl2wDMpM-UUQ_k37FVtf-ndbQAIPG7S469doZMmb5IZYgvcB4ojqCW3Vz6Q

每个 token 都附带获取它时所用的浏览器 User-Agent。使用 json=1 时,响应中包含一个 userAgent 字段,与 request并列。在纯文本模式下,从 X-Recaptcha-User-Agent 响应头读取相同的值。如果你使用 JSON API, getTaskResult 会将其作为 solution.userAgent返回。提交 token 时请发送这个 User-Agent,因为有些网站会检查两者是否一致。如果没有可用的 User-Agent,响应中不会包含该字段和该响应头。

{
    "status": 1,
    "request": "03AHJ_Vuve5Asa4koK3KSMyUkCq0vUFCR5Im4CwB7PzO3dCxIo11i53epEraq...",
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
}

如果验证码尚未识别完成,CapSkip 会返回 CAPCHA_NOT_READY。这种情况下,等待 5 秒并重复请求。如果 CapSkip 返回空的响应体,说明结果已被读取或该 ID 不存在。每个结果只能读取一次。

找到 ID 为 g-recaptcha-response 的元素,并通过移除 display: none 样式使其可见。

g recaptcha response CapSkip

请注意: 在某些情况下,页面内容是动态生成的,而 g-recaptcha-response element may not appear in the static HTML source. In such situations, inspect the page structure using your browser’s developer tools to locate the dynamically generated element.


作为替代,你可以使用 JavaScript 直接设置 g-recaptcha-response 字段的值:

document.getElementById("g-recaptcha-response").innerHTML="TOKEN";

页面上会出现一个输入框。将答案 token 粘贴到该字段并提交表单。

recaptcha v2 solver  reCAPTCHA V2 回调

在某些情况下,页面没有提交按钮,而是使用回调函数。当 reCAPTCHA 被识别后,该回调函数会自动执行。

POST 和 GET 请求参数列表见此处: reCAPTCHA V2 POST 与 GET 请求参数

回调函数通常定义在 data-callback reCAPTCHA 小组件的属性,例如:

data-callback="myCallbackFunction"

在其他情况下,回调函数被定义为 callback 的 grecaptcha.render() 函数的参数,例如:

grecaptcha.render('example', {
'sitekey' : 'someSitekey',
'callback' : myCallbackFunction,
'theme' : 'dark'
});

Another way to locate the callback function is to open the browser’s JavaScript console and inspect the reCAPTCHA configuration object:

___grecaptcha_cfg.clients[0].aa.l.callback

请注意, aa.l 属性可能会有所不同,页面上也可能存在多个 reCAPTCHA 客户端。在这种情况下,你还应检查 clients[1], clients[2],以及其他条目,以定位正确的配置对象。

或者,你可以使用以下脚本自动提取 reCAPTCHA 参数:

function findRecaptchaClients() {
  if (typeof (___grecaptcha_cfg) !== 'undefined') {
    return Object.entries(___grecaptcha_cfg.clients).map(([cid, client]) => {
      const data = { id: cid, version: cid >= 10000 ? 'V3' : 'V2' };
      const objects = Object.entries(client).filter(([_, value]) => value && typeof value === 'object');

      objects.forEach(([toplevelKey, toplevel]) => {
        const found = Object.entries(toplevel).find(([_, value]) => (
          value && typeof value === 'object' && 'sitekey' in value && 'size' in value
        ));
     
        if (typeof toplevel === 'object' && toplevel instanceof HTMLElement && toplevel['tagName'] === 'DIV'){
            data.pageurl = toplevel.baseURI;
        }
        
        if (found) {
          const [sublevelKey, sublevel] = found;

          data.sitekey = sublevel.sitekey;
          const callbackKey = data.version === 'V2' ? 'callback' : 'promise-callback';
          const callback = sublevel[callbackKey];
          if (!callback) {
            data.callback = null;
            data.function = null;
          } else {
            data.function = callback;
            const keys = [cid, toplevelKey, sublevelKey, callbackKey].map((key) => `['${key}']`).join('');
            data.callback = `___grecaptcha_cfg.clients${keys}`;
          }
        }
      });
      return data;
    });
  }
  return [];
}

最后,调用回调函数:

myCallbackFunction();

或者:

___grecaptcha_cfg.clients[0].aa.l.callback();

在某些情况下,回调函数需要一个参数。大多数情况下,你应该将识别得到的 token 作为该参数传入。例如:

myCallbackFunction('TOKEN');

recaptcha v2 solver  reCAPTCHA V2 隐形版

reCAPTCHA v2 还有一种隐形模式。你可以在此查看示例:

https://www.google.com/recaptcha/api2/demo?invisible=true

隐形 reCAPTCHA 不显示“我不是机器人”复选框。相反,它通常附加在按钮上,或在页面加载或用户交互(例如点击按钮或提交表单)时自动触发。

在内部,隐形 reCAPTCHA 小组件被渲染在一个隐藏的 <div> 定位到视口可见区域之外的元素,使其对用户不可见。

根据用户的 Cookie 和风险评分,reCAPTCHA 可能会自动通过而不显示挑战。否则,会出现标准的图片挑战。

大多数情况下,挑战完成后会执行一个回调函数。更多详情请参阅上文的回调部分。

POST 和 GET 请求参数列表见此处: reCAPTCHA V2 POST 与 GET 请求参数

如何判断 reCAPTCHA 是否为隐形版?

你可以通过以下指标之一来识别隐形 reCAPTCHA:

  • “我不是机器人”复选框不可见,但在用户交互后会出现挑战。

  • reCAPTCHA iframe 的 URL 中包含参数 size=invisible.

  • reCAPTCHA 配置对象包含一个 size 属性,其值设置为 invisible,例如:
    ___grecaptcha_cfg.clients[0].aa.l.size === "invisible"

通过 API 识别隐形 reCAPTCHA 时,请包含参数: invisible=1

如何在浏览器中处理隐形 reCAPTCHA?
方法 1:使用 JavaScript

将 g-recaptcha-response 字段的值设置为 CapSkip 返回的 token:

document.getElementById("g-recaptcha-response").innerHTML="TOKEN";

设置 token 后,执行验证成功后通常会发生的操作。

在大多数情况下,这意味着提交表单。你需要通过其 id, name或其他属性来识别正确的表单,然后触发提交。以下是几个示例:

document.getElementById("recaptcha-demo-form").submit(); //by id "recaptcha-demo-form"
document.getElementsByName("myFormName")[0].submit(); //by element name "myFormName"
document.getElementsByClassName("example").submit(); //by class name "example"

在某些情况下,当 reCAPTCHA 被识别后会自动执行一个回调函数。

回调函数通常定义在 data-callback reCAPTCHA 小组件的属性,例如:

data-callback="myCallbackFunction"

在其他情况下,回调函数被定义为 callback 的 grecaptcha.render() 函数的参数,例如:

grecaptcha.render('example', {
'sitekey' : 'someSitekey',
'callback' : myCallbackFunction,
'theme' : 'dark'
});

你只需调用该函数:

myCallbackFunction();
方法 2:修改 HTML

移除包含 reCAPTCHA 小组件的 <div> 元素。

<div style="visibility: hidden; position: absolute; width:100%; top: -10000px; left: 0px; right: 0px; transition: visibility 0s linear 0.3s, opacity 0.3s linear; opacity: 0;">
  <div style="width: 100%; height: 100%; position: fixed; top: 0px; left: 0px; z-index: 2000000000; background-color: #fff; opacity: 0.5;  filter: alpha(opacity=50)"></div>
  <div style="margin: 0 auto; top: 0px; left: 0px; right: 0px; position: absolute; border: 1px solid #ccc; z-index: 2000000000; background-color: #fff; overflow: hidden;">
 <iframe src="https://www.google.com/recaptcha/api2/bframe?hl=en&v=r20170213115309&k=6LfP0CITAAAAAHq9FOgCo7v_fb0-pmmH9VW3ziFs#zglq3yifgkmj"
      title="reCAPTCHA 挑战"
      style="width: 100%; height: 100%;"
      scrolling="no"
      name="zglq3yifgkmj"
      frameborder="0"></iframe>
  </div>
</div>

从页面中移除整个 reCAPTCHA 区块。

<div class="">
  <!-- BEGIN: ReCAPTCHA implementation example. -->
  <div
    id="recaptcha-demo"
    class="g-recaptcha"
    data-sitekey="6LfP0CITAAAAAHq9FOgCo7v_fb0-pmmH9VW3ziFs"
    data-callback="onSuccess"
    data-bind="recaptcha-demo-submit"
  >
    <div
      class="grecaptcha-badge"
      style="width: 256px; height: 60px; transition: right 0.3s ease 0s; position: fixed; bottom: 14px; right: -186px; box-shadow: 0px 0px 5px gray;"
    >
      <div class="grecaptcha-logo">
        <iframe
          src="https://www.google.com/recaptcha/api2/anchor?k=6LfP0CITAAAAAHq9FOgCo7v_fb0-pmmH9VW3ziFs&co=aHR0cHM6Ly93d3cuZ29vZ2xlLmNvbTo0NDM.&hl=en&v=r20170213115309&size=invisible&cb=uror1hlow5a"
          title="reCAPTCHA 小组件"
          scrolling="no"
          name="undefined"
          width="256"
          height="60"
          frameborder="0"
        ></iframe>
      </div>
      <div class="grecaptcha-error"></div>
      <textarea
        id="g-recaptcha-response"
        name="g-recaptcha-response"
        class="g-recaptcha-response"
        style="width: 250px; height: 40px; border: 1px solid #c1c1c1; margin: 10px 25px; padding: 0px; resize: none;  display: none; "
      ></textarea>
    </div>
  </div>
  <script>
    var onSuccess = function (response) {
      var errorDivs = document.getElementsByClassName('recaptcha-error');
      if (errorDivs.length) {
        errorDivs[0].className = '';
      }
      var errorMsgs = document.getElementsByClassName('recaptcha-error-message');
      if (errorMsgs.length) {
        errorMsgs[0].parentNode.removeChild(errorMsgs[0]);
      }
      document.getElementById('recaptcha-demo-form').submit();
    };
  </script>
  <!-- Optional noscript fallback. --><!-- END: ReCAPTCHA implementation example. -->
</div>

在被移除的区块位置插入以下代码:

<input type="submit" /> <textarea name="g-recaptcha-response">%g-recaptcha-response%</textarea>

%g-recaptcha-response% 表示从 CapSkip 收到的答案 token。

替换区块后,页面上会出现一个“提交查询”按钮。点击该按钮,将 g-recaptcha-response 值以及所有其他必需的表单数据一起提交到网站。

recaptcha v2 solver  reCAPTCHA V3

reCAPTCHA v3 是 Google 开发的一种现代验证码机制。它不显示可见的挑战,也不需要用户交互,而是根据交互为人类的可能性给出一个分数。

从技术上讲,reCAPTCHA v3 与 reCAPTCHA v2 类似。网站从 reCAPTCHA API 获得一个 token,然后在 POST 请求中发送到目标服务器,并通过 reCAPTCHA API 验证。

关键区别在于 reCAPTCHA v3 不显示可见的挑战。相反,它返回一个评估用户是人类还是机器人的评分。该评分称为 score ,取值范围为 0.0 到 1.0。评分会发送到网站,网站再据此决定如何处理请求。

还有一个名为 action的附加参数,它让网站能够区分不同的用户交互。验证 token 后,reCAPTCHA API 会返回与该请求关联的 action 名称。

如何使用 CapSkip 识别 reCAPTCHA v3?

首先,确认目标网站正在使用 reCAPTCHA v3。

reCAPTCHA v3 的特征包括:

  • 没有可见的验证码或图片挑战

  • 加载的 api.js 脚本带有一个 render=SITEKEY 参数,例如:
    https://www.google.com/recaptcha/api.js?render=SITEKEY

  • 加载的 ___grecaptcha_cfg.clients 数组中包含一个具有较高数字索引的条目,例如 clients[100000]

要识别 reCAPTCHA v3,请确定以下参数:

  1. sitekey
    可在 render 的 api.js 脚本 URL 中找到。它也可能出现在 iframe 的 URL 中、调用 grecaptcha.execute()的 JavaScript 代码中,或 ___grecaptcha_cfg 配置对象内部。
  2. action
    通过检查 JavaScript 代码中对以下内容的调用来定位它: grecaptcha.execute(),例如:
    grecaptcha.execute('SITEKEY', {action: 'do_something'})
    在某些情况下,查找 action 需要检查页面加载的多个 JavaScript 文件。如果无法确定 action 值,你可以使用默认值 "verify".
  3. pageurl
    。
理解评分

可接受的评分阈值因网站而异,只能通过测试确定。评分范围为:

  • 0.0 → 很可能是机器人

  • 1.0 → 很可能是人类

大多数网站使用 0.3 到 0.7 之间的阈值,因为即使是正常用户也可能获得较低的评分。

你可以使用以下参数传入你想要的阈值 min_score 参数,但最终评分始终由 Google 在验证时决定,识别工具无法保证。

POST 请求参数列表
参数 类型 是否必填 说明
key 字符串 是 你的 CapSkip API 密钥。
method 字符串 是 userrecaptcha:指定一个 reCAPTCHA 请求。
version 字符串 是 v3:表示该请求用于 reCAPTCHA v3。
googlekey 字符串 是 在目标页面上找到的 data-sitekey 参数的值。
pageurl 字符串 是 reCAPTCHA 所在页面的完整 URL。
enterprise 整数
默认:0
否 1:表示 reCAPTCHA Enterprise v3。
0:标准 reCAPTCHA v3。
action 字符串
默认:verify
否 在目标页面上找到的 action 在页面上定义的参数的值。
min_score 浮点数 否 请求的 token 最低评分。当你的服务器验证 token 时,Google 会给出最终评分,因此此值只是一个提示,并不保证。无论 Google 之后给出的评分如何,CapSkip 都会返回它获得的 token。
json 整数
默认:0
否 0:以纯文本返回响应。
1:以 JSON 格式返回响应。
proxy 字符串 否 代理地址。IP 认证格式: IP:PORT (示例: 123.123.123.123:3128)。登录/密码认证格式: login:password@IP:PORT
proxytype 字符串 否 代理类型。支持的值: HTTP, HTTPS, SOCKS5, SOCKS5H。默认: HTTP 当提供了 proxy 但省略了 proxytype 时。

提交 reCAPTCHA v3:

curl -X POST -d "key=YOUR_API_KEY&method=userrecaptcha&version=v3&action=submit&min_score=0.7&googlekey=6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-&pageurl=https://example.com" http://127.0.0.1:8080/in.php

提交 Enterprise reCAPTCHA v3:

curl -X POST -d "key=YOUR_API_KEY&method=userrecaptcha&version=v3&action=submit&min_score=0.7&googlekey=6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-&pageurl=https://example.com&enterprise=1" http://127.0.0.1:8080/in.php

如果请求成功,CapSkip 会以纯文本返回验证码 ID: OK|12345

如果使用了 json=1 参数,响应将以 JSON 格式返回:

{
    "status":1,
    "request":"12345"
}

如果发生错误,CapSkip 会返回一个错误代码。

等待 10 到 15 秒,然后向结果端点发送 HTTP GET 请求: http://127.0.0.1:PORT/res.php

在请求中提供返回的验证码 ID。下表列出了全部可用参数。

如果验证码识别成功,CapSkip 会以纯文本或 JSON 格式返回结果。返回值是一个类似如下的验证 token:

03AHJ_Vuve5Asa4koK3KSMyUkCq0vUFCR5Im4CwB7PzO3dCxIo11i53epEraq-uBO5mVm2XRikL8iKOWr0aG50sCuej9bXx5qcviUGSm4iK4NC_Q88flavWhaTXSh0VxoihBwBjXxwXuJZ-WGN5Sy4dtUl2wbpMqAj8Zwup1vyCaQJWFvRjYGWJ_TQBKTXNB5CCOgncqLetmJ6B6Cos7qoQyaB8ZzBOTGf5KSP6e-K9niYs772f53Oof6aJeSUDNjiKG9gN3FTrdwKwdnAwEYX-F37sI_vLB1Zs8NQo0PObHYy0b0sf7WSLkzzcIgW9GR0FwcCCm1P8lB--gf50q5BMkiRH7osm4DoUgsjc_XyQiEmQmxl5sqZP7aKsaE-EM00x59XsPzD3m3YI6SRCFRUevSyumBd7KmXE8VuzIO9lgnnbka4-eZynZa6vbB9cO3QjLH0xSG3--o-fxrOuphwfrtwvvi2FGfpTexWvxhqWICMFTTjFBCEGEgj7_IFWEKirXW2RTZCVF0Gid7EtIsoEeZkPbrcUISGmgtiJkJ_KojuKwImF0G0CsTlxYTOU2sPsd5o1JDt65wGniQR2IZufnPbbK76Yh_KI2DY4cUxMfcb2fAXcFMc9dcpHg6f9wBXhUtFYTu6pi5LhhGuhpkiGcv6vWYNxMrpWJW_pV7q8mPilwkAP-zw5MJxkgijl2wDMpM-UUQ_k37FVtf-ndbQAIPG7S469doZMmb5IZYgvcB4ojqCW3Vz6Q

每个 token 都附带获取它时所用的浏览器 User-Agent。使用 json=1 时,响应中包含一个 userAgent 字段,与 request并列。在纯文本模式下,从 X-Recaptcha-User-Agent 响应头读取相同的值。如果你使用 JSON API, getTaskResult 会将其作为 solution.userAgent返回。提交 token 时请发送这个 User-Agent,因为有些网站会检查两者是否一致。如果没有可用的 User-Agent,响应中不会包含该字段和该响应头。

{
    "status": 1,
    "request": "03AHJ_Vuve5Asa4koK3KSMyUkCq0vUFCR5Im4CwB7PzO3dCxIo11i53epEraq...",
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
}

如果验证码尚未识别完成,CapSkip 会返回 CAPCHA_NOT_READY。等待 5 秒并重复请求。如果 CapSkip 返回空的响应体,说明结果已被读取或该 ID 不存在。每个结果只能读取一次。

GET 请求参数列表
GET 参数 类型 是否必填 说明
key 字符串 是 你的 CapSkip API 密钥。
action 字符串 是 get:获取所提交验证码的答案。
id 整数 是 由以下方法返回的验证码 ID in.php.
json 整数
默认:0
否 0 以纯文本返回响应。 1 以 JSON 返回响应,并在可用时包含 userAgent 值。
curl "http://127.0.0.1:8080/res.php?key=YOUR_API_KEY&action=get&id=CAPTCHA_ID"

从 CapSkip 收到 token 后,你必须将它正确提交到目标网站。理解其工作方式的最佳方法是观察作为正常用户完成验证时发送的请求。大多数浏览器都提供开发者工具,其中的 Network 标签可让你检查发出的请求。

在大多数情况下,token 通过 POST 请求发送。参数名可能是 g-recaptcha-response,与 reCAPTCHA v2 类似,或类似于 g-recaptcha-response-100000。在某些实现中,可能使用不同的参数名。

你应检查网络请求,确定 token 的传输方式,然后据此构造你的请求。

recaptcha v2 solver  reCAPTCHA Enterprise

reCAPTCHA Enterprise 是 Google reCAPTCHA 系统的高级版本。它可以在 v2 和 v3 两种模式下运行,为网站管理员提供额外的控制,包括评估并报告某次交互是人类还是自动化的能力。

如何识别 reCAPTCHA Enterprise?

第一步是确定该网站是否使用 Enterprise 版本的 reCAPTCHA。

reCAPTCHA Enterprise 的关键特征包括:

  • 页面加载 enterprise.js 而不是 api.js,例如:

    <script src="https://recaptcha.net/recaptcha/enterprise.js" async defer></script>

  • 网站的 JavaScript 代码调用 grecaptcha.enterprise.METHOD 而不是 grecaptcha.METHOD

接下来,确定使用的是哪种实现:v2、隐形 v2 还是 v3。通常可以通过分析小组件的渲染方式及其在页面上的行为来判断。

按照下面的流程图确定正确的实现。它适用于绝大多数情况。

recaptcha enterprise

按照针对 reCAPTCHA v2 或 v3 所述的相同方式确定验证码参数。

对于 v2 Enterprise 实现,可能有额外的可选数据。在大多数情况下,这是在 s 或 data-s 参数。如果存在,请通过以下方式将该值包含在你的请求中 data-s 参数中定义的自定义字符串。

POST 和 GET 请求参数列表见此处: reCAPTCHA V2 POST 与 GET 请求参数

对于 v3 Enterprise 实现,你可能还需要 action 值。要找到它,请检查网站的 JavaScript 代码并定位 grecaptcha.enterprise.execute() 调用。 action 参数通常在该函数中传递。请记住, action 是可选的,在某些情况下可能未定义。

POST 和 GET 请求参数列表见此处: reCAPTCHA V3 POST 与 GET 请求参数

向 /in.php 端点提交请求时,请包含附加参数: enterprise=1

之后,以与识别 reCAPTCHA v2 或 v3 相同的方式与 CapSkip API 交互。返回 token 后,根据目标网站的实现将其提交。

Cloudflare turnstile solving Cloudflare Turnstile

Cloudflare Turnstile 是 Cloudflare 开发的一种现代验证码替代方案。它无需依赖传统的视觉挑战即可验证访客是否为人类。Turnstile 可以作为独立小组件出现,也可以作为挑战页面的一部分,并且在极少或无需用户交互的情况下运行。

有两种常见的 Turnstile 实现:

1. 独立 Turnstile 小组件

独立的 Turnstile 小组件直接嵌入在网站页面中,通常用于保护表单免受自动提交。在这种情况下:

  • 从页面中提取 sitekey 。

  • 将它连同完整的 pageurl.

  • 一起发送到 CapSkip API。 cf-turnstile-response 字段中。

  • 在某些实现中,token 可能还需要放入 g-recaptcha-response 字段中。

  • 如果在 turnstile.render() 配置中定义了回调,请用返回的 token 执行它。

然后照常提交表单。

2. Cloudflare 挑战页面上的 Turnstile

当网站通过 Cloudflare 代理,并在授予访问权限前显示 Turnstile 挑战页面时会出现这种情况。在这种情况下,你必须提取以下参数:

  • cData

  • chlPageData

  • action

这些值必须包含在你的 API 请求中。此外,提交 token 时,你必须使用 CapSkip API 返回的 User-Agent 值。

如何提取所需参数?

要提取所需参数,你可以重写 turnstile.render 方法并拦截调用时传入的参数。例如,将以下 JavaScript 代码注入页面。该脚本必须在 Turnstile 小组件加载之前执行,才能成功捕获参数。

const i = setInterval(()=>{
if (window.turnstile) {
clearInterval(i)
window.turnstile.render = (a,b) => {
let p = {
method: "turnstile",
key: "YOUR_API_KEY",
sitekey: b.sitekey,
pageurl: window.location.href,
data: b.cData,
pagedata: b.chlPageData,
action: b.action,
userAgent: navigator.userAgent,
json: 1
}
console.log(JSON.stringify(p))
window.tsCallback = b.callback
return 'foo'
}
}
},50)
POST 请求参数列表
参数 类型 是否必填 说明
key 字符串 是 你的 CapSkip API 密钥。
method 字符串 是 turnstile:指定一个 Cloudflare Turnstile 请求。
sitekey 字符串 是 在目标页面上找到的 data-sitekey 参数的值。
pageurl 字符串 是 Turnstile 挑战所在页面的完整 URL。
action 字符串 否 在 data-action 属性中定义或传给 turnstile.render().
data 字符串 否 的可选 action 值。 cData 传给 turnstile.render() 或在 data-cdata 属性中定义的值。
pagedata 字符串 否 的可选 action 值。 chlPageData 传给 turnstile.render().
json 整数
默认:0
否 0:以纯文本返回响应。
1:以 JSON 格式返回响应。
proxy 字符串 否 代理地址。IP 认证格式: IP:PORT (示例: 123.123.123.123:3128)。登录/密码认证格式: login:password@IP:PORT
proxytype 字符串 否 代理类型。支持的值: HTTP, HTTPS, SOCKS5, SOCKS5H。默认: HTTP 当提供了 proxy 但省略了 proxytype 时。

提交 Turnstile(独立):

curl -X POST -d "key=YOUR_API_KEY&method=turnstile&sitekey=0x4AAAAAAABUYP0XeMJF0xoy&pageurl=https://example.com" http://127.0.0.1:8080/in.php

提交 Turnstile(挑战页面,可选 action、data、pagedata):

curl -X POST -d "key=YOUR_API_KEY&method=turnstile&sitekey=0x4AAAAAAABUYP0XeMJF0xoy&pageurl=https://example.com&action=managed&data=...&pagedata=..." http://127.0.0.1:8080/in.php

如果请求成功,CapSkip 会以纯文本返回验证码 ID: OK|12345

如果使用了 json=1 参数,响应将以 JSON 格式返回:

{
    "status":1,
    "request":"12345"
}

如果发生错误,CapSkip 会返回一个错误代码。

使用返回的 ID 从 API 的 /res.php 端点获取结果。

GET 请求参数列表
GET 参数 类型 是否必填 说明
key 字符串 是 你的 CapSkip API 密钥。
action 字符串 是 get:获取所提交验证码的答案。
id 整数 是 由以下方法返回的验证码 ID in.php.
json 整数
默认:0
否 0 - 以纯文本返回响应。
1 - 以 JSON 格式返回响应,包含 userAgent 值。

对于 Cloudflare Turnstile,识别工具会使用特定的浏览器 User-Agent,你在提交 token 时必须发送相同的 User-Agent。使用 json=1 时,响应中包含一个 userAgent 字段。在纯文本模式下,从 X-Turnstile-User-Agent 响应头读取相同的值。

curl "http://127.0.0.1:8080/res.php?key=YOUR_API_KEY&action=get&id=CAPTCHA_ID"

cloudflare challenge solver Cloudflare Challenge (cf_clearance)

部分使用 Cloudflare 的网站会在显示任何内容之前,先用一个写着 “Just a moment...” 的完整页面拦下每一位访问者。通过该页面后会设置一个 cf_clearance Cookie,之后所有携带该 Cookie 的请求都会被直接放行。CapSkip 会针对你发送的 URL 通过该页面并返回 Cookie,因此没有自带浏览器的脚本或工具也能用普通 HTTP 请求抓取该网站。

工作原理

此方法用于完整页面挑战。如果网站是在表单中显示 Turnstile 小组件,你需要的是 token 而不是 Cookie:请参阅上方的 Cloudflare Turnstile 部分。

Cloudflare 会将 cf_clearance Cookie 绑定到获取它的 IP 地址和 User-Agent。从其他地址重放的 Cookie,或搭配不同 User-Agent 使用的 Cookie,都会被拒绝,挑战页面也会重新出现。因此,此方法的每个请求都必须带上你的代理,返回结果也会告诉你应发送哪个 User-Agent。

完整流程分为四步:

  1. 在 CapSkip 中开启 返回 cf_clearance Cookie (见下文)。
  2. 将页面 URL 和你的代理提交到 /createTask.
  3. 轮询 /getTaskResult ,直到 Cookie 就绪。
  4. 通过同一代理、使用同一 User-Agent 并带上该 Cookie 发送你自己的请求。

此方法仅适用于 JSON createTask / getTaskResult API。旧版 in.php / res.php 接口不支持 Cookie 模式。

开始之前

此方法默认关闭。打开 CapSkip,进入 设置, 验证码类型, Cloudflare,然后开启 返回 cf_clearance Cookie。关闭期间,此方法的所有请求都会被拒绝并返回 ERROR_TASK_NOT_SUPPORTED。该开关在下一个请求时即生效,无需重启。

每个请求都必须满足两项要求:

  • 代理。 Cookie 仅在获取它的地址上有效,因此请求中必须指定你之后要使用的代理。未带代理的请求会在执行任何工作之前被拒绝。支持 HTTP、HTTPS、SOCKS5 和 SOCKS5H 代理,不支持 SOCKS4。
  • 匹配的 User-Agent(如果你要发送的话)。 它必须是 Windows 版 Chrome 的 User-Agent,且主版本号与运行 CapSkip 的电脑上安装的 Google Chrome 相同。其他任何 User-Agent 都会得到被网站拒绝的 Cookie,因此会提前以错误拒绝。最简单的做法是完全不发送 User-Agent,直接使用随 Cookie 返回的那个。
任务类型

同一请求有三种写法均可接受,每种对应一个支持 Cookie 模式的客户端系列。它们的识别方式完全相同,只是字段名和返回结果的结构不同。

任务类型风格是否支持说明
AntiCloudflareTask 任务CapSolver是代理以单个 proxy 字符串传入。返回结果包含 Cookie、值与其相同的 token ,以及 User-Agent。
TurnstileTask 任务 连同 cloudflareTaskType 设为 cf_clearanceCapMonster是代理以独立字段传入。返回结果包含 Cookie;如果请求未发送 User-Agent,还会附带 User-Agent。
TurnstileTask 任务 不带 websiteKeyNextCaptcha是代理以独立字段传入。仅在所选服务为 NextCaptcha 时识别。返回结果包含 Cookie 和 User-Agent。
AntiCloudflareTask 参数
参数类型是否必填说明
clientKey字符串是*你的 CapSkip API 密钥。仅当启用了 API 密钥验证 时才需要。
type字符串是必须为 AntiCloudflareTask 任务.
websiteURL字符串是完整的 http 或 https URL,即显示挑战的页面地址。
proxy字符串是用于识别的代理。支持 IP:PORT:LOGIN:PASSWORD, LOGIN:PASSWORD@IP:PORT, http://LOGIN:PASSWORD@IP:PORT (也支持 https://, socks5://, socks5h://)以及 socks5:IP:PORT:LOGIN:PASSWORD。登录名和密码为可选项。
userAgent字符串否你随 Cookie 一起发送的 User-Agent。请参阅 开始之前 了解可接受的值。
html字符串否仅为兼容而接受,会被忽略。
TurnstileTask (cf_clearance) 参数
参数类型是否必填说明
clientKey字符串是*你的 CapSkip API 密钥。仅当启用了 API 密钥验证 时才需要。
type字符串是必须为 TurnstileTask 任务.
cloudflareTaskType字符串是必须为 cf_clearance。缺少此字段时,该任务按 Turnstile token 请求处理。
websiteURL字符串是完整的 http 或 https URL,即显示挑战的页面地址。
websiteKey字符串否仅为兼容而接受。该页面没有可提供的 sitekey,因此任意值均可。
proxyType字符串
默认值:http
否http, https, socks5 或 socks5h.
proxyAddress字符串是代理的 IP 地址或主机名。
proxyPort整数是代理端口。
proxyLogin字符串否代理登录名。
proxyPassword字符串否代理密码。按原样使用,因此诸如 @ 和 : 之类的字符都可以正常使用。
userAgent字符串否你随 Cookie 一起发送的 User-Agent。请参阅 开始之前 了解可接受的值。
htmlPageBase64字符串否仅为兼容而接受,会被忽略。

NextCaptcha 写法使用相同的 proxyType, proxyAddress, proxyPort, proxyLogin 和 proxyPassword 字段,并将 type 设为 TurnstileTask 任务 ,且不带 websiteKey.

提交任务

使用 JSON 请求体向你的 CapSkip API 端点提交 HTTP POST 请求(/createTask).

提交 AntiCloudflareTask:

curl -X POST \
-H "Content-Type: application/json" \
-d '{
    "clientKey": "YOUR_API_KEY",
    "task": {
        "type": "AntiCloudflareTask",
        "websiteURL": "https://www.example.com/",
        "proxy": "203.0.113.10:8080:login:password"
    }
}' \
http://127.0.0.1:8080/createTask

提交 TurnstileTask,并附带 cloudflareTaskType:

curl -X POST \
-H "Content-Type: application/json" \
-d '{
    "clientKey": "YOUR_API_KEY",
    "task": {
        "type": "TurnstileTask",
        "cloudflareTaskType": "cf_clearance",
        "websiteURL": "https://www.example.com/",
        "websiteKey": "xxxxxx",
        "proxyType": "http",
        "proxyAddress": "203.0.113.10",
        "proxyPort": 8080,
        "proxyLogin": "login",
        "proxyPassword": "password"
    }
}' \
http://127.0.0.1:8080/createTask

请求被接受后,CapSkip 会返回任务 ID。如果请求在该设置关闭时发出、未带代理,或所带的 User-Agent 无法使用,会在这里(即提交请求本身)被拒绝,不会执行任何工作。

{
    "errorId": 0,
    "errorCode": "",
    "errorDescription": "",
    "taskId": "2122988149"
}
获取结果

等待约 5 秒,然后向 /getTaskResult 发送包含任务 ID 的 HTTP POST 请求。状态为 processing时,每隔几秒重复一次。大多数页面会在 5 到 20 秒内通过;代理较慢时耗时会更长。

curl -X POST \
-H "Content-Type: application/json" \
-d '{"clientKey": "YOUR_API_KEY", "taskId": "2122988149"}' \
http://127.0.0.1:8080/getTaskResult
{
    "errorId": 0,
    "taskId": "2122988149",
    "status": "processing"
}

任务就绪时的返回结果,适用于 AntiCloudflareTask 任务. token 的值与 Cookie 相同:

{
    "errorId": 0,
    "errorCode": "",
    "errorDescription": "",
    "taskId": "2122988149",
    "solution": {
        "cookies": {
            "cf_clearance": "3gBq2EXAMPLE9vRk7pT0c1aZ.uWx4yNq8mLs-1792066388-1.2.1.1-Qx7aEXAMPLEpZ0kR3vT9wYb2"
        },
        "token": "3gBq2EXAMPLE9vRk7pT0c1aZ.uWx4yNq8mLs-1792066388-1.2.1.1-Qx7aEXAMPLEpZ0kR3vT9wYb2",
        "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36"
    },
    "status": "ready"
}

任务就绪时的返回结果,适用于 TurnstileTask 任务 连同 cloudflareTaskType。如果请求自带了 userAgent,solution 中仅包含 Cookie,因为该 Cookie 绑定在你发送的 User-Agent 上。如果未发送,则会添加 userAgent ,以便你知道应使用哪个 User-Agent:

{
    "errorId": 0,
    "errorCode": "",
    "errorDescription": "",
    "taskId": "2122988149",
    "solution": {
        "cf_clearance": "3gBq2EXAMPLE9vRk7pT0c1aZ.uWx4yNq8mLs-1792066388-1.2.1.1-Qx7aEXAMPLEpZ0kR3vT9wYb2",
        "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36"
    },
    "status": "ready"
}

NextCaptcha 写法返回的 cf_clearance 和 userAgent 并列放在 solution.

使用 Cookie

向该网站发送的每个请求都要带上 Cookie,并且通过 同一代理 发送,使用 同一 User-Agent。只要更改其中任何一项,Cloudflare 就会再次显示挑战页面。

curl -x "http://login:[email protected]:8080" \
-A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36" \
-b "cf_clearance=3gBq2EXAMPLE9vRk7pT0c1aZ.uWx4yNq8mLs-1792066388-1.2.1.1-Qx7aEXAMPLEpZ0kR3vT9wYb2" \
https://www.example.com/

Cookie 的有效期取决于该网站自身的 Cloudflare 设置,通常为 30 分钟,有时会长得多。挑战页面再次出现时,请提交新任务。如果你轮换代理,请为每个代理分别识别一次:在一个地址上获得的 Cookie 不能用于另一个地址。

错误

拒绝类错误在 createTask 请求中返回。识别失败则通过 getTaskResult 连同 status 设为 failed返回。选择 NextCaptcha 时,错误代码和 HTTP 状态改为遵循 NextCaptcha 自己的错误表。

代码含义
ERROR_TASK_NOT_SUPPORTED返回 cf_clearance Cookie 在 CapSkip 中处于关闭状态。
ERROR_PROXY_MISSINGTurnstileTask 任务 写法:请求中没有可用的代理。
ERROR_WRONG_USERAGENTTurnstileTask 任务 写法:User-Agent 不是与 CapSkip 所在电脑上安装的 Google Chrome 主版本号相同的 Windows 版 Chrome。
ERROR_BAD_PARAMETERSAntiCloudflareTask 任务 写法:没有可用的代理,或 User-Agent 不可用。两种写法均适用: websiteURL 缺失。
ERROR_PAGEURLwebsiteURL 不是完整的 http 或 https URL。
ERROR_PROXY_BANNEDCloudflare 直接封禁了该代理地址。请更换代理。
ERROR_PROXY_CONNECT_REFUSED无法通过该代理访问页面。请检查代理是否在线以及登录信息是否正确。
ERROR_CAPTCHA_UNSOLVABLE未能及时获取 Cookie,或页面根本没有显示 Cloudflare 挑战(此时errorDescription 的内容为 “No Cloudflare challenge on this page”)。请提交新任务重试。

hcaptcha solver hCaptcha

hCaptcha 是一种 CAPTCHA 小组件,许多注册、登录和结账表单都在使用。访问者勾选复选框,有时还要完成图片挑战,随后小组件会把一个 token 放入表单。网站在接受表单之前,会向 hCaptcha 校验该 token。CapSkip 针对您发送的 sitekey 和页面通过小组件验证,并返回 token,可直接随表单提交。hCaptcha Enterprise 和隐形小组件使用同一方法。

工作原理
  1. 在目标页面上找到 hCaptcha 小组件的 sitekey(见下文)。
  2. 将 sitekey 和页面 URL 提交到 /in.php 连同 method=hcaptcha.
  3. 轮询 /res.php 直到 token 就绪。
  4. 将 token 填入表单,或将其传给页面的回调函数,然后提交表单。

一次识别通常需要 10 到 30 秒。请每 5 秒轮询一次。

查找 Sitekey

sitekey 是公开标识符,对页面的每位访问者都相同。大多数页面将它写在小组件容器的属性中,即 data-sitekey:

<div class="h-captcha" data-sitekey="a5f74b19-9e45-40e0-b45d-47ff91b7a6c2"></div>

当页面通过脚本构建小组件时,请查找 sitekey 选项,位于 hcaptcha.render()中,或查找 sitekey= 参数,位于 hCaptcha iframe 的地址中。带有 data-size="invisible" 的小组件为隐形小组件:请发送 invisible=1 来指明这一点。

部分 hCaptcha Enterprise 网站还会向小组件传入一个 rqdata 值,方式是在小组件运行前调用 hcaptcha.setData()。如果页面这样做,请将该值作为 data发送。该值由网站针对单次挑战签发,并且很快过期,因此每个任务都要获取新的值。

POST 请求参数列表
参数类型是否必填说明
key字符串是*你的 CapSkip API 密钥。仅当启用了 API 密钥验证 时才需要。
method字符串是必须为 hcaptcha。表示您提交的是 hCaptcha。
sitekey字符串是hCaptcha 小组件的 sitekey。
pageurl字符串是小组件所在页面的完整 URL。
invisible整数
默认:0
否1 表示隐形小组件。
data字符串否加载的 rqdata 值,来自 hCaptcha Enterprise 小组件(网站设置了该值时)。
userAgent字符串否您提交表单时将使用的 User-Agent。只有当它与 CapSkip 识别所用的浏览器一致时,CapSkip 才会使用它。回复中始终会注明获取该 token 时所用的 User-Agent。
proxy字符串否代理地址。接受 IP:PORT, LOGIN:PASSWORD@IP:PORT 或 IP:PORT:LOGIN:PASSWORD.
proxytype字符串否代理类型: HTTP, HTTPS, SOCKS5 或 SOCKS5H.
json整数
默认:0
否0 以纯文本返回响应。 1 以 JSON 返回响应。

CapSkip 不强制此方法使用代理。测试和偶尔识别时,使用一个地址即可。超出这个范围后,hCaptcha 不仅会对浏览器评分,也会对 token 来源的网络评分,而来自同一 IP 的持续识别请求,正是其速率限制和信誉检查要拦截的对象。大批量使用时,请在以下位置配置代理池: 设置, 验证码类型, hCaptcha, 配置代理,或者在识别必须来自特定网络时,随每个任务发送代理。随任务发送的代理始终优先于列表。

向你的 CapSkip API 端点(/in.php)提交 HTTP GET 或 POST 请求,method 为 method=hcaptcha。表单字段、查询字符串和 JSON 请求体均可接受,字段名称相同。

提交 hCaptcha:

curl -X POST \
-d "key=YOUR_API_KEY" \
-d "method=hcaptcha" \
-d "sitekey=a5f74b19-9e45-40e0-b45d-47ff91b7a6c2" \
--data-urlencode "pageurl=https://accounts.hcaptcha.com/demo" \
-d "json=1" \
http://127.0.0.1:8080/in.php

如果一切正确,CapSkip 会以纯文本形式返回验证码 ID: OK|2122988149。如果发送了 json=1 参数,返回的则是 JSON 封装格式。无论哪种形式,返回的值都是你用于轮询结果的验证码 ID。

{
    "status": 1,
    "request": "2122988149"
}

否则,CapSkip 会直接在提交请求中返回错误代码,此时尚未进行任何处理:

错误原因
ERROR_RECAPTCHA_INVALID_SITEKEYsitekey 缺失或为空。
ERROR_PAGEURLpageurl 缺失,或不是 http 或 https 地址。
ERROR_PROXY_FORMAT无法读取 proxy 值。

等待约 10 秒,然后向结果端点(/res.php)发送一个 HTTP GET 请求,并带上你收到的验证码 ID。

GET 请求参数列表
参数类型是否必填说明
key字符串是*你的 CapSkip API 密钥。仅当启用了 API 密钥验证 时才需要。
action字符串是指定 get 以获取验证码答案。
id整数是由以下请求返回的验证码 ID: in.php 请求。
json整数
默认:0
否0 以纯文本返回响应。 1 以 JSON 返回响应。
curl "http://127.0.0.1:8080/res.php?key=YOUR_API_KEY&action=get&id=2122988149&json=1"

如果小组件已通过,CapSkip 将返回 token:

{
    "status": 1,
    "request": "P1_eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.hadwYXNza2V5xQZ2...f3Kq8nV2",
    "respKey": "E0_eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjA0...9wB1",
    "useragent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.0.0 Safari/537.36"
}

request 就是 token。 respKey 是 hCaptcha 随 token 一起交给小组件的第二个值;大多数网站从不读取它,它也可能为空。 useragent 是获取该 token 时所用的 User-Agent。

不带 json=1时,token 以纯文本形式跟在 OK| 前缀之后,User-Agent 则通过 X-HCaptcha-User-Agent 响应头返回:

OK|P1_eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.hadwYXNza2V5xQZ2...f3Kq8nV2

如果小组件尚未通过,CapSkip 将返回 CAPCHA_NOT_READY,其拼写方式与其他所有方法完全一致。请等待 5 秒后重复该请求。

每个结果只交付一次。第一次成功的轮询会返回 token 并将其丢弃,因此请从携带它的那次回复中保存好 token。

未能解出的任务会返回 ERROR_CAPTCHA_UNSOLVABLE。有两种失败会改为说明原因,因为重新提交同一任务仍会失败: ERROR_RECAPTCHA_INVALID_SITEKEY 表示 hCaptcha 拒绝了该页面的 sitekey,请检查 sitekey 和 pageurl,以及 ERROR_PROXY_CONNECT_REFUSED 表示无法连接到代理。

JSON API(createTask 接口)

hCaptcha 也可以通过 /createTask提交,结果从 /getTaskResult读取。可识别四种任务类型: HCaptchaTaskProxyless 和 HCaptchaEnterpriseTaskProxyless 无需您自己的代理即可识别, HCaptchaTask 和 HCaptchaEnterpriseTask 则需要提供代理。这四种类型的识别方式完全相同。

参数类型是否必填说明
type字符串是上述四种任务类型之一。
websiteURL字符串是小组件所在页面的完整 URL。
websiteKey字符串是hCaptcha 小组件的 sitekey。
isInvisibleBoolean否true 表示隐形小组件。
enterprisePayloadObject否包含 rqdata的对象,用于 hCaptcha Enterprise。也接受扁平的 rqdata 字段。
userAgent字符串否同上文的 in.php 一节。
proxy字符串否代理,可以是单个字符串,也可以使用 proxyType, proxyAddress, proxyPort, proxyLogin 和 proxyPassword 字段。
curl -X POST http://127.0.0.1:8080/createTask \
-H "Content-Type: application/json" \
-d '{
    "clientKey": "YOUR_API_KEY",
    "task": {
        "type": "HCaptchaTaskProxyless",
        "websiteURL": "https://accounts.hcaptcha.com/demo",
        "websiteKey": "a5f74b19-9e45-40e0-b45d-47ff91b7a6c2"
    }
}'

solution 中包含两份 token,分别位于 gRecaptchaResponse 和 token下,因此为任一名称编写的客户端都能直接读取:

{
    "errorId": 0,
    "status": "ready",
    "solution": {
        "gRecaptchaResponse": "P1_eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.hadwYXNza2V5xQZ2...f3Kq8nV2",
        "token": "P1_eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.hadwYXNza2V5xQZ2...f3Kq8nV2",
        "respKey": "E0_eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjA0...9wB1",
        "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.0.0 Safari/537.36"
    }
}

失败的任务返回的错误代码与 res.php上的相同,位于 errorCode.

提交 token

在页面上,小组件会将 token 写入表单内一个隐藏的 textarea ,名称为 h-captcha-response (以及一个名为 g-recaptcha-response的副本)。请将收到的 token 填入这两个字段,然后提交表单。如果小组件渲染时带有 data-callback,请改为用该 token 调用该函数,就像小组件本身会做的那样:

document.querySelector('[name="h-captcha-response"]').value = token;
const g = document.querySelector('[name="g-recaptcha-response"]');
if (g) g.value = token;
// 或者,当小组件带有 data-callback 时:
window[callbackName](token);

在不使用浏览器的情况下提交表单时,请将 token 作为 h-captcha-response 字段发送(如果表单中还有 g-recaptcha-response,也一并发送),并使用回复中的 User-Agent。

hCaptcha token 只能使用一次,并在签发约两分钟后过期。收到每个 token 后请尽快提交,并为每次表单提交请求新的 token。

严格 Sitekey

某些网站,尤其是 hCaptcha Enterprise 网站,即使小组件已通过,也会拒绝被 hCaptcha 评为有风险的 token。如果网站拒绝 CapSkip 返回的 token,请开启 hCaptcha 高分模式 ,位置为 设置, 验证码类型, hCaptcha。这需要在运行 CapSkip 的电脑上安装 Google Chrome。同时请使用优质代理,并从同一地址、使用回复中注明的 User-Agent 提交表单。

aws waf captcha solver AWS WAF CAPTCHA

AWS WAF CAPTCHA 是网站防火墙需要证明访问者是真人时,Amazon Web Services 显示的验证谜题:通常是九张图片组成的网格,并附有类似 “Choose all the curtains”(选出所有窗帘)的指示,有时则是把一辆小玩具车开到路径终点。通过后会获得一个 aws-waf-token cookie。把受保护页面上的值发送给 CapSkip,它会返回可兑换成该 cookie 的结果。

工作原理

网站上的 AWS WAF CAPTCHA 有两种形式,本方法都能处理:

  • 挑战页面。 防火墙对请求返回 HTTP 状态码 405 以及一个小页面,而不是正常内容。该页面包含一个 window.gokuProps 对象,并加载 challenge.js 和 captcha.js ,它们来自 awswaf.com 主机。
  • JS-API 小组件。 一个普通页面(例如联系表单或下载表单)加载 jsapi.js ,并通过以下方式自行绘制谜题: AwsWafCaptcha.renderCaptcha.

完整流程分为四步:

  1. 从受保护页面读取下面列出的值。
  2. 将它们提交到 /in.php 连同 method=amazon_waf.
  3. 轮询 /res.php ,直到答案就绪。
  4. 将答案兑换为 aws-waf-token ,并在发往该网站的请求中携带它。
在挑战页面上查找这些值

请求受保护的 URL 并保留 405 响应的正文,或在浏览器中打开它并使用 查看页面源代码。所需的一切都在 HTML 中:

<script>
  window.gokuProps = {
    "key": "AQIDAHjcYuEXAMPLEghicBgQ/7bFaQZ+m5FKCMDnO+vTbNg96AHDh0IR5vgzHNceHYqZR+GOAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM...",
    "iv": "CgAFRDIjfgAAA5yY",
    "context": "Rbp3r8fvbuhmrCqpUe8Mh1TLvs8OSKJZiSs2vCF4dt9rxVZI7xYJa2n5k/9r+XaXe7KUIu2I8nXTDHHkDvIOTDc2..."
  };
</script>
<script src="https://3f2a9c7d41b0.edge.sdk.awswaf.com/3f2a9c7d41b0/8e51d0c7a2f4/challenge.js"></script>
<script src="https://3f2a9c7d41b0.edge.sdk.awswaf.com/3f2a9c7d41b0/8e51d0c7a2f4/captcha.js"></script>
页面上的值发送为
gokuProps.keysitekey。通常以下列字符开头的长密钥: AQIDAH。它标识该网站的防火墙配置。
gokuProps.iviv。每个挑战页面都不同。
gokuProps.contextcontext。每个挑战页面都不同,并与以下参数一起变化: iv.
challenge.js 地址challenge_script。脚本的完整地址,与其出现在以下属性中的写法一致: src 属性中定义的值。
captcha.js 地址captcha_script。脚本的完整地址,与其出现在以下属性中的写法一致: src 属性中定义的值。

只有 sitekey 和 pageurl 是必需的。 iv 和 context 必须一起使用:要么都发送,要么都不发送。你未发送的任何值都会在求解时从 pageurl 读取;如果你保存的页面副本已超过几分钟,这样做更稳妥。

在小组件页面上查找这些值

打开显示谜题的页面,在其源代码中,或在浏览器开发者工具的 Network(网络)标签中,查找 jsapi.js 脚本和 renderCaptcha 调用里:

<script src="https://5b7e0a19c3d8.us-east-1.captcha-sdk.awswaf.com/5b7e0a19c3d8/jsapi.js"></script>
<script>
  AwsWafCaptcha.renderCaptcha(document.getElementById("captcha"), {
    apiKey: "Xr9kEXAMPLE2bWQx1vTq8LmZ0aYc4dHs7nFp3gJt6uKw5eRi...9AbCdEfGh=_0_1",
    onSuccess: function (wafToken) { /* ... */ }
  });
</script>
页面上的值发送为
apiKeysitekey。一个以短后缀结尾的长密钥,例如 _0_1。有些网站把它放在单独的 JavaScript 文件中而不是 HTML 里,所以如果页面中没有,请在已加载的脚本中搜索 apiKey 。
jsapi.js 地址jsapiScript。脚本的完整地址,与其出现在以下属性中的写法一致: src 属性中定义的值。

小组件页面没有 gokuProps,因此不要发送 iv 或 context。发送 jsapiScript 而不带这两个参数,就表示这是一个小组件求解请求。

挑战类型

显示哪种谜题由 AWS WAF 决定,你无需告诉 CapSkip 会出现哪一种。

Challenge外观是否支持说明
图片网格3 x 3 图块是九张图片和一条类似 “Choose all the beds”(选出所有床)的指示,最后点击 确认 按钮。最常见的谜题。
玩具车一张图片是小路线图上的一辆玩具车,需要把它移动到路径终点。

偶尔会有谜题无法作答,结果为 ERROR_CAPTCHA_UNSOLVABLE。请提交新任务:每次尝试都会抽取一个新谜题。

POST 请求参数列表
参数类型是否必填说明
key字符串是*你的 CapSkip API 密钥。仅当启用了 API 密钥验证 时才需要。
method字符串是必须为 amazon_waf。表示你提交的是 AWS WAF CAPTCHA。
sitekey字符串是加载的 gokuProps.key (挑战页面),或小组件的 apiKey.
pageurl字符串是完整的 http 或 https URL,即受 AWS WAF 保护的页面地址。
iv字符串否加载的 gokuProps.iv (挑战页面)。需与以下参数一起发送: context,或两者都不发送。
context字符串否加载的 gokuProps.context (挑战页面)。需与以下参数一起发送: iv,或两者都不发送。
challenge_script字符串否页面所用脚本的完整 URL: challenge.js.
captcha_script字符串否页面所用脚本的完整 URL: captcha.js.
jsapiScript字符串否仅限小组件页面:以下脚本的完整 URL: jsapi.js.
proxy字符串否代理地址。接受 IP:PORT, LOGIN:PASSWORD@IP:PORT 或 IP:PORT:LOGIN:PASSWORD.
proxytype字符串否代理类型: HTTP, HTTPS, SOCKS5 或 SOCKS5H. SOCKS4 不受支持。
json整数
默认:0
否0 以纯文本返回响应。 1 以 JSON 返回响应。

你发送的每个脚本 URL 都必须指向 awswaf.com 主机。来自其他任何地方的脚本都会被拒绝,因为它不是 AWS WAF 集成。

代理

本方法不强制使用代理,一个地址足以用于测试和偶尔的求解。超出这个范围就需要代理。受 AWS WAF 保护的网站通常会把 CAPTCHA 与速率限制和 IP 信誉规则结合使用,因此来自同一地址的持续求解很快会遭到更严格的对待,随后被直接封锁。请在 CapSkip 的 设置 中为 AWS WAF 配置代理列表,让它分散负载;或者在令牌必须来自特定网络时,按请求发送 proxy 。

提交任务

向你的 CapSkip API 端点(/in.php)提交 HTTP GET 或 POST 请求,method 为 method=amazon_waf。表单字段、JSON 正文和 GET 查询字符串都可以使用,字段名相同。密钥中包含 +, / 和 =,因此以表单字段发送时请对其进行 URL 编码。

提交挑战页面:

curl -X POST \
--data-urlencode "key=YOUR_API_KEY" \
--data-urlencode "method=amazon_waf" \
--data-urlencode "sitekey=AQIDAHjcYuEXAMPLEghicBgQ/7bFaQZ+m5FKCMDnO+vTbNg96AHDh0IR5vgzHNceHYqZR+GOAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM..." \
--data-urlencode "iv=CgAFRDIjfgAAA5yY" \
--data-urlencode "context=Rbp3r8fvbuhmrCqpUe8Mh1TLvs8OSKJZiSs2vCF4dt9rxVZI7xYJa2n5k/9r+XaXe7KUIu2I8nXTDHHkDvIOTDc2..." \
--data-urlencode "challenge_script=https://3f2a9c7d41b0.edge.sdk.awswaf.com/3f2a9c7d41b0/8e51d0c7a2f4/challenge.js" \
--data-urlencode "pageurl=https://www.example.com/" \
--data-urlencode "json=1" \
http://127.0.0.1:8080/in.php

提交小组件页面(JSON 正文):

curl -X POST \
-H "Content-Type: application/json" \
-d '{
    "key": "YOUR_API_KEY",
    "method": "amazon_waf",
    "sitekey": "Xr9kEXAMPLE2bWQx1vTq8LmZ0aYc4dHs7nFp3gJt6uKw5eRi...9AbCdEfGh=_0_1",
    "jsapiScript": "https://5b7e0a19c3d8.us-east-1.captcha-sdk.awswaf.com/5b7e0a19c3d8/jsapi.js",
    "pageurl": "https://www.example.com/contact",
    "json": 1
}' \
http://127.0.0.1:8080/in.php

如果一切正确,CapSkip 会以纯文本形式返回验证码 ID: OK|2122988149。如果发送了 json=1,响应则改为 JSON 格式:

{
    "status": 1,
    "request": "2122988149"
}

缺少 sitekey、无法使用的 pageurl、只有 iv 不带 context (或反过来)以及格式错误的代理,都会在此请求中被直接拒绝,不会开始任何处理。

获取结果

等待约 5 秒,然后向 /res.php 发送带有 CAPTCHA ID 的 HTTP GET 请求。答案为以下值时,每 5 秒重复一次: CAPCHA_NOT_READY。大多数任务在 15 秒内就绪;代理较慢时会更久。

参数类型是否必填说明
key字符串是*你的 CapSkip API 密钥。仅当启用了 API 密钥验证 时才需要。
action字符串是指定 get 以获取验证码答案。
id整数是由以下请求返回的验证码 ID: in.php 请求。
json整数
默认:0
否0 以纯文本返回响应。 1 以 JSON 返回响应。
curl "http://127.0.0.1:8080/res.php?key=YOUR_API_KEY&action=get&id=2122988149&json=1"

答案包含两个值,因此 request 是一个对象,而不是字符串:

{
    "status": 1,
    "request": {
        "captcha_voucher": "eyJ2b3VjaGVyIjoiRVhBTVBMRSJ9.9QwZcEXAMPLEvoucherJk4LmN0pQ",
        "existing_token": "7d4c2a91-EXAMPLE-4f0b-9e6d-3a1b5c8e2f70:EQoAeXAMPLEtokenAAAA:Wm9wc2E="
    }
}

不带 json=1,同一个对象会紧跟在 OK| 之后,写在同一行:

OK|{"captcha_voucher":"eyJ2b3VjaGVyIjoiRVhBTVBMRSJ9.9QwZcEXAMPLEvoucherJk4LmN0pQ","existing_token":"7d4c2a91-EXAMPLE-4f0b-9e6d-3a1b5c8e2f70:EQoAeXAMPLEtokenAAAA:Wm9wc2E="}
字段含义
captcha_voucherAWS WAF 出具的谜题已作答证明。
existing_token该凭证所对应的令牌。两者只能成对使用。

如果任务失败, /res.php 返回 ERROR_CAPTCHA_UNSOLVABLE,或 CapSkip 中配置的替换错误文本。请提交新任务重试。

将凭证兑换为 Cookie

AWS WAF 在自己的令牌主机上用这一对值兑换最终的 aws-waf-token :即加载 challenge.js 的主机和目录。在小组件页面上,请在 Network(网络)标签中找到该 challenge.js 请求; jsapi.js 会替你加载它。将 challenge.js 连同 voucher ,然后以 JSON 形式 POST 这一对值:

curl -X POST \
-H "Content-Type: text/plain;charset=UTF-8" \
-H "Origin: https://www.example.com" \
-d '{"captcha_voucher":"eyJ2b3VjaGVyIjoiRVhBTVBMRSJ9.9QwZcEXAMPLEvoucherJk4LmN0pQ","existing_token":"7d4c2a91-EXAMPLE-4f0b-9e6d-3a1b5c8e2f70:EQoAeXAMPLEtokenAAAA:Wm9wc2E="}' \
https://3f2a9c7d41b0.edge.sdk.awswaf.com/3f2a9c7d41b0/8e51d0c7a2f4/voucher

响应是一个 JSON 对象,其 token 字段包含 aws-waf-token。凭证一到就立即兑换,不要留着以后再用。

使用令牌

将令牌作为 aws-waf-token cookie 设置在受保护的域名上,并重新发送被拦截的请求。网站现在会返回真实内容,而不是 405 页面:

curl -b "aws-waf-token=7d4c2a91-EXAMPLE-4f0b-9e6d-3a1b5c8e2f70:HgoAxEXAMPLEcookieBBBB:Q2FwU2tpcA==" \
https://www.example.com/

在小组件页面上,这就是页面的 onSuccess 回调收到的同一个令牌。很多这类页面会把它作为 aws-waf-token cookie 传递,另一些则放在 x-aws-waf-token 请求头中,所以请查看页面自身的请求发送了什么,并照样发送。

令牌在网站的免验证时间(immunity time)内有效,AWS WAF 默认是 5 分钟,网站所有者可以延长。挑战再次出现时,请提交新任务。

JSON API

JSON createTask / getTaskResult API 也支持 AWS WAF,任务类型为 AmazonTask / AmazonTaskProxyless 和 AntiAwsWafTask / AntiAwsWafTaskProxyLess,字段名和结果格式与 CapSkip 当前模拟的服务一致。其中几种格式会直接返回最终的 aws-waf-token ,因此无需兑换凭证。选择 NextCaptcha 或 EzCaptcha 作为服务时不可用。

geetest v3 极验 v3

极验 v3 滑块是极验开发的一种交互式验证码。它通过滑块挑战来验证用户,在区分人类与机器人的同时,提供快速无缝的验证体验。

要用 CapSkip 识别极验 v3 验证码,你必须先从目标网站获取所需的验证码参数。所需参数为:

  • gt:网站公钥(静态)
  • challenge:动态挑战值
  • api_server:极验 API 服务器域名(可选)

这些值通常在网站初始化极验时可获得。

重要: 每次识别请求都必须获取一个新的 challenge 值。验证码在页面上加载后,之前的值就会失效。你应检查网站的网络请求,找到生成新 challenge 值的请求,并在每次向 CapSkip 提交识别请求前执行该请求。 challenge 值。

POST 请求参数列表
参数 类型 是否必填 说明
key 字符串 是* 你的 CapSkip API 密钥。仅当启用了 API 密钥验证 时才需要。
method 字符串 是 必须为 geetest。指定你提交的是极验 v3 验证码。
gt 字符串 是 加载的 gt 从目标网站获取的值。
challenge 字符串 是 加载的 challenge 从目标网站获取的值。每次识别请求都必须获取一个新值。
pageurl 字符串 是 包含极验验证码的页面的完整 URL。
api_server 字符串 否 目标网站使用的极验 API 服务器域名(例如 api.geetest.com 或 api-na.geetest.com).
json 整数
默认:0
否 0 以纯文本返回响应。 1 以 JSON 返回响应。
proxy 字符串 否 代理地址。IP 认证格式: IP:PORT (示例: 123.123.123.123:3128)。登录/密码认证格式: login:password@IP:PORT.
proxytype 字符串 否 代理类型。支持的值: HTTP, HTTPS, SOCKS5, SOCKS5H。默认: HTTP 当提供了 proxy 但省略了 proxytype 时。

向你的 CapSkip API 端点(/in.php)提交 HTTP GET 或 POST 请求,method 为 method=geetest。包含上一步获取的所需极验参数,以及包含验证码的页面的完整 URL。

curl -X POST \
-d "key=YOUR_API_KEY" \
-d "method=geetest" \
-d "gt=f1ab2cdefa3456789012345b6c78d90e" \
-d "challenge=12345678abc90123d45678ef90123a456b" \
-d "pageurl=https://www.example.com/" \
-d "api_server=api-na.geetest.com" \
http://127.0.0.1:8080/in.php

如果一切成功,CapSkip 会以纯文本返回验证码 ID:OK|212

如果使用了 json=1 参数,响应将以 JSON 格式返回:

{
    "status": 1,
    "request": "212"
}

否则,CapSkip 会返回相应的错误代码。

等待大约 5 秒,然后向 res.php 端点提交 HTTP GET 请求以获取结果。

GET 请求参数列表
参数 类型 是否必填 说明
key 字符串 是* 你的 CapSkip API 密钥。仅当启用了 API 密钥验证 时才需要。
action 字符串 是 指定 get 以获取验证码答案。
id 整数 是 由以下请求返回的验证码 ID: in.php 请求。
json 整数
默认:0
否 0 以纯文本返回响应。 1 以 JSON 返回响应。
curl "http://127.0.0.1:8080/res.php?key=YOUR_API_KEY&action=get&id=212&json=1"

如果验证码识别成功,CapSkip 会以 JSON 格式返回答案:

{
    "status": 1,
    "request": "{\"geetest_challenge\":\"1a2b3456cd67890e12345fab678901c2de\",\"geetest_validate\":\"09fe8d7c6ba54f32e1dcb0a9fedc8765\",\"geetest_seccode\":\"12fe3d4c56789ba01f2e345d6789c012|jordan\"}"
}

如果验证码尚未识别完成,CapSkip 会返回: CAPCHA_NOT_READY

等待 5 秒后重复请求。如果发生错误,CapSkip 会返回相应的错误代码。在向目标网站提交请求时,请使用 CapSkip 返回的值,填入以下字段:

  • geetest_challenge
  • geetest_validate
  • geetest_seccode

altcha solver ALTCHA

ALTCHA 是一种工作量证明(Proof of Work)类验证码。这里没有需要识别的图片,也没有需要播放的音频。目标站点下发一个挑战,客户端必须暴力枚举出满足该挑战的数字。CapSkip 会算出这个数字,并返回小组件本应生成的 payload。

工作原理

它的防御手段是 CPU 计算成本,而不是图像识别。服务器给出一个目标值和一个搜索范围(maxnumber),客户端不断对候选数字做哈希运算,直到其中一个与目标匹配。由此带来两点在验证码类型中并不常见的特性。

第一,识别过程是确定性的。这里没有模型,也没有准确率指标:答案要么就在给定范围之内,要么就是挑战本身格式有误。永远不会出现识别错误的情况。

第二,识别耗时由目标站点决定,而不是由 CapSkip 决定。官方参考小组件默认的范围是 1,000,000,只需几毫秒即可算完。站点可以自行调高这个值,有些站点会设为 999,999,999,对一个普通挑战来说大约相当于 5 亿次哈希运算。如果某个站点识别很慢,请先查看它的 maxnumber 值。

完整流程分为四步:

  1. 从小组件读取挑战的那个接口处取得该挑战。
  2. 将其提交到 /in.php 连同 method=altcha.
  3. 轮询 /res.php 直到 token 就绪。
  4. 将 token 回填到目标表单的 altcha 字段中。
识别前需要准备什么

挑战本身,两种形式任选其一。两种都支持,你的爬虫手上已经有哪一种就发哪一种。

参数 适用场景
challenge_json 你已经拿到了挑战文档。CapSkip 会在本地直接计算,完全不发起任何网络请求,这是最快的方式。
challenge_url 你手上只有提供挑战的接口地址。CapSkip 会替你抓取该挑战(如果你传了代理,就通过你的代理抓取),然后再进行计算。
在哪里找到挑战

打开浏览器开发者工具,在目标页面切换到 Network(网络)标签页,找到由 <altcha-widget> 元素为获取挑战而发出的那个请求。它的路径通常类似于 /altcha/challenge。该请求的 URL 就是你的 challenge_url,而它返回的 JSON 就是你的 challenge_json.

指定该接口的小组件属性在不同版本之间变过,因此请直接查看页面源码,不要想当然。小组件 v1 和 v2 使用 challengeurl="...",而 v3 及更高版本统一使用 challenge="..." 来同时表示 URL 和内联数据两种情况。有些部署会直接在页面内生成挑战,完全不发起任何请求。

挑战文档的形式如下:

{
    "algorithm": "SHA-256",
    "challenge": "3dd28253be6cc0c54d95f7f98c517e68a1b2c3d4e5f60718293a4b5c6d7e8f90",
    "salt": "46d5b1c8871e5152d902ee3f?expires=1893456000",
    "signature": "4b1cf0e0be0f4e5247e50b0f9a4498301234567890abcdef1234567890abcdef",
    "maxnumber": 1000000
}
挑战会过期,而且时间窗口很短

每个挑战都自带过期时间,它要么出现在 salt 的查询字符串里,要么以 parameters.expiresAt的形式给出。一旦过期,目标站点只会返回一个笼统的验证失败,看起来和答案错误一模一样。短至两分钟的时间窗口也很常见。

请在创建任务前的最后一刻再去获取挑战,并尽快提交 token。不要提前囤积挑战,也不要在用户填写表单期间一直攥着 token。如果你发送的是 challenge_url 而不是 challenge_json,那么当挑战在排队期间过期时,CapSkip 会自动重新抓取。

POST 请求参数列表
参数 类型 是否必填 说明
key 字符串 是* 你的 CapSkip API 密钥。仅当启用了 API 密钥验证 时才需要。
method 字符串 是 必须为 altcha。表示你提交的是 ALTCHA 挑战。
pageurl 字符串 是 挑战来源页面的完整 URL。
challenge_url 字符串 是* CapSkip 应当从中抓取挑战的接口地址。除非发送了 challenge_json ,否则该参数必填。
challenge_json 字符串 是* 挑战文档本身,以 JSON 字符串形式提供。除非发送了 challenge_url ,否则该参数必填。
proxy 字符串 否 代理地址。接受 IP:PORT, LOGIN:PASSWORD@IP:PORT 或 IP:PORT:LOGIN:PASSWORD。仅用于 challenge_url 的抓取。
proxytype 字符串 否 代理类型: HTTP, HTTPS, SOCKS5 或 SOCKS5H.
json 整数
默认:0
否 0 以纯文本返回响应。 1 以 JSON 返回响应。

只需发送 challenge_url 或 challenge_json。两者同时发送也是允许的,此时以内联文档为准,因为抓取只会重新取回你已经拥有的内容。

向你的 CapSkip API 端点(/in.php)提交 HTTP GET 或 POST 请求,method 为 method=altcha。表单字段和 JSON 请求体都支持,字段名完全相同;另外 GET 同样可用,因为 ALTCHA 不需要上传任何图片。

提交 ALTCHA(表单字段):

curl -X POST \
-d "key=YOUR_API_KEY" \
-d "method=altcha" \
-d "pageurl=https://www.example.com/signup" \
--data-urlencode "challenge_url=https://www.example.com/captcha/api/altcha/challenge" \
-d "json=1" \
http://127.0.0.1:8080/in.php

提交 ALTCHA(JSON 请求体):

curl -X POST \
-H "Content-Type: application/json" \
-d '{
    "key": "YOUR_API_KEY",
    "method": "altcha",
    "pageurl": "https://www.example.com/signup",
    "challenge_url": "https://www.example.com/captcha/api/altcha/challenge",
    "json": 1
}' \
http://127.0.0.1:8080/in.php

JSON 请求体支持三件表单编码做不到的事。开关类字段可以写成真正的布尔值 ("json": true), challenge_json 可以直接是嵌套的 JSON 文档,而不必是转义后的字符串;把某个字段设为 null 则等同于没有发送该字段。

如果一切正确,CapSkip 会以纯文本形式返回验证码 ID: OK|2122988149。如果发送了 json=1 参数,返回的则是 JSON 封装格式。无论哪种形式,返回的值都是你用于轮询结果的验证码 ID。

{
    "status": 1,
    "request": "2122988149"
}

否则 CapSkip 会返回相应的错误代码。格式有误的内联挑战会在提交请求时就被拒绝,而不是等到轮询之后才报错,因此你能在出错的那次请求上当场发现问题。

请等待约 5 秒,然后向结果接口(/res.php)发送一个 HTTP GET 请求,并带上你收到的验证码 ID。

GET 请求参数列表
参数 类型 是否必填 说明
key 字符串 是* 你的 CapSkip API 密钥。仅当启用了 API 密钥验证 时才需要。
action 字符串 是 指定 get 以获取验证码答案。
id 整数 是 由以下请求返回的验证码 ID: in.php 请求。
json 整数
默认:0
否 0 以纯文本返回响应。 1 以 JSON 返回响应。
curl "http://127.0.0.1:8080/res.php?key=YOUR_API_KEY&action=get&id=2122988149&json=1"

如果验证码已经识别完成,CapSkip 会以 JSON 格式返回结果:

{
    "status": 1,
    "request": "eyJhbGdvcml0aG0iOiJTSEEtMjU2IiwiY2hhbGxlbmdlIjoiM2RkMi...",
    "solution": {
        "token": "eyJhbGdvcml0aG0iOiJTSEEtMjU2IiwiY2hhbGxlbmdlIjoiM2RkMi...",
        "number": 9661
    },
    "cost": "0.0020",
    "createTime": 1788863246,
    "endTime": 1788863246,
    "errorId": 0,
    "solveCount": 1
}

request 和 solution.token 两者始终是同一个字符串,你的客户端需要读哪一个就读哪一个。 number 是解开该挑战的计数器值,返回它只是为了信息完整。如果没有发送 json=1,返回的结果就只是 OK|<token>.

如果验证码尚未识别完成,CapSkip 会返回 CAPCHA_NOT_READY,其拼写方式与其他所有方法完全一致。请等待 5 秒后重复该请求。

轮询循环请以 errorId为判断依据,而不是 status. status 是整数 1 ,也就是 res.php 契约一直以来返回的内容,也是所有兼容 SDK 读取的字段。如果你的客户端是参照某个展示了 "status": "ready"的文档页面写的,那么请改为检查 errorId === 0 ,或者检查是否存在 solution.token ,而不是前者。

提交 token

ALTCHA 小组件会把它的 payload 放进一个名为 altcha的表单字段中。请把 token 原封不动地填进该字段,与小组件本身提交的内容完全一致:

POST https://www.example.com/signup
Content-Type: application/x-www-form-urlencoded

email=someoneexample.com&altcha=eyJhbGdvcml0aG0iOiJTSEEtMjU2Iiwi...

payload 的内部结构取决于生成它的那个挑战。旧版挑战生成的是一个扁平文档,其中带有 number,而 PoW v2 挑战生成的文档会把原始挑战嵌套在 challenge 之下,把答案嵌套在 solution之下。请把 token 当作不透明数据原样传递,因为目标站点自己知道该期待哪种结构。

有些集成方式改为从 JSON 请求体的某个字段读取 payload,因此请看清页面自己的提交请求发送了什么,并照着做。不要对 token 重新编码、裁剪空白或调整字段顺序。它是一份 JSON 文档的 base64 编码,文档中的字段都被服务器的 HMAC 签名覆盖,任何改动都会让它失效。

支持的算法

你不需要自己判断某个站点用的是哪种方案。CapSkip 会读取挑战并自动选用对应的算法。

方案 代次 是否支持 说明
Legacy PoW v1 是 求出 n ,使得 SHA(salt + n) 等于挑战值。SHA-1、SHA-256、SHA-384 和 SHA-512 均受支持。这也是目前大多数部署仍在运行的形式。
PBKDF2 PoW v2 是 找出一个计数值,使其派生密钥以目标前缀开头。支持 SHA-256、SHA-384 和 SHA-512。这是 ALTCHA 官方推荐的默认方案。
SHA PoW v2 是 同一方案的迭代哈希变体。
Argon2id PoW v2 否 内存密集型密钥派生函数。直接拒绝,而不会尝试计算。
scrypt PoW v2 否 内存密集型密钥派生函数。直接拒绝,而不会尝试计算。

两种计算强度模式都能正常处理,且无需你做任何配置。在 确定性(deterministic) 模式下,服务器会预先算好目标值,因此识别耗时可预测;而在 概率性(probabilistic) 模式下,识别耗时会因挑战而异。三种小组件类型 (native, checkbox 和 switch) 均受支持,因为类型只是控件的视觉样式,永远不会传到 API。

Argon2id 和 scrypt 会被直接拒绝,而不会尝试计算。使用其中任意一种的任务会返回 ERROR_CAPTCHA_UNSOLVABLE ,耗时约三分之一秒,并且永不重试,因此绝不会在无声无息中给出错误答案。由于 ALTCHA 推荐 PBKDF2 作为默认方案,这只会影响极少数站点。

captchafox solver CaptchaFox

CaptchaFox 是一种注重隐私的验证码,它对浏览器本身进行评分,而不是要求访客去辨认任何内容。大多数访客根本不会看到任何挑战。CapSkip 通过在真实浏览器中驱动真实的小组件来完成识别,并返回该小组件本应生成的验证 token。

工作原理

CaptchaFox 的判定分三层进行,其中只有最后一层是可见的。小组件会先运行一小段工作量证明,再收集大量浏览器信号,然后把两者一起发送到它自己的 API。如果这些证据让服务满意,就会立即签发 token,屏幕上不会绘制任何挑战。只有在证据无法通过时,才会出现交互式挑战。

这种结构带来一个实际影响,值得在接入之前了解。token 由真实的浏览器会话产生,而不是根据你提供的参数计算出来的,因此 CapSkip 会针对你的页面 URL 加载小组件并让它真实运行。你把 site key 和页面交给 CapSkip,CapSkip 把 token 返回给你。

完整流程分为四步:

  1. 从目标页面读取 site key。
  2. 将其提交到 /in.php 连同 method=captchafox.
  3. 轮询 /res.php 直到 token 就绪。
  4. 将 token 回填到目标表单的 cf-captcha-response 字段中。
识别前需要准备什么

只有两个值,而且都直接从目标页面读取。没有需要抓取的挑战文档,也没有任何东西会在任务排队期间过期。

参数来源
sitekey渲染小组件时使用的公钥。它并不保密,对每位访客都相同,其前缀通常为 sk_.
pageurl小组件所在页面的完整 URL。CaptchaFox 会拿它与该 key 注册的域名进行核对,因此必须是真实的页面。
在哪里查找 site key

在目标页面上打开浏览器开发者工具,找到 CaptchaFox 的容器元素。站点会用两种方式之一渲染小组件,两种方式里 key 都是可见的。

<!-- Automatic rendering: the key is an attribute -->
<div class="captchafox" data-sitekey="sk_xtNxpk6fCdFbxh1_xJeGflSdCE9tn99G"></div>

<!-- Explicit rendering: the key is in the render call -->
<script>
  captchafox.render("#container", {
    sitekey: "sk_xtNxpk6fCdFbxh1_xJeGflSdCE9tn99G",
    onVerify: function (token) { /* ... */ }
  });
</script>

如果页面是在运行时才构建小组件,导致两种写法都不在返回的 HTML 中,请打开 Network 标签页,找到发往 api.captchafox.com的请求。在路径中,key 就紧跟在这一段之后: /captcha/.

页面 URL 必须与 key 相符

CaptchaFox 的 key 会绑定一份允许域名列表,服务在签发任何内容之前都会先检查主机名。key 本身正确、却用在列表之外的页面上时,会被永久拒绝,而不是偶尔失败。

CapSkip 会直接报告这种情况,而不是重试,因为重试不可能有帮助。如果某个 site key 立即且持续失败,请确认 pageurl 就是小组件真正运行的那个页面,而不是搜索页、跳转页,或者解析到别处的短链接。

挑战类型

出现哪种挑战不由你选择。由 CaptchaFox 决定,CapSkip 负责处理它给出的类型。

Challenge出现频率是否支持说明
隐形通常是浏览器证据让服务满意,屏幕上不绘制任何内容就直接签发 token。这是最常见、也是最快的一条路径。
滑块有时是需要把一块拼图拖入缺口的滑块挑战。CapSkip 会定位目标位置并完成拖动。
图片选择很少否一组供挑选的图片网格。会被报告为无法识别,这样你的客户端可以重新提交、换到一个新的挑战,而不必一直等到超时。
音频很少否无障碍备用方案。出于同样的原因被报告为无法识别。

这两种不支持的挑战并不常见,重试通常会换到另一种。请把无法识别的结果当作重新提交的信号,而不是该 key 永久失效。

POST 请求参数列表
参数类型是否必填说明
key字符串是*你的 CapSkip API 密钥。仅当启用了 API 密钥验证 时才需要。
method字符串是必须为 captchafox。表示你提交的是 CaptchaFox 验证码。
sitekey字符串是从目标页面读取到的 site key,其前缀通常为 sk_.
pageurl字符串是小组件所在页面的完整 URL。
api_server字符串否要加载的小组件入口地址。默认值为 https://cdn.captchafox.com/。参见下方的 选择小组件来源 一节。
useragent字符串否为与其他服务保持兼容而接受,但不会被使用。请改用结果中返回的 userAgent 值。
proxy字符串否代理地址。接受 IP:PORT, LOGIN:PASSWORD@IP:PORT 或 IP:PORT:LOGIN:PASSWORD.
proxytype字符串否代理类型: HTTP, HTTPS, SOCKS5 或 SOCKS5H.
json整数
默认:0
否0 以纯文本返回响应。 1 以 JSON 返回响应。

与大多数服务不同,CapSkip 并不强制这个方法使用代理。但只要你的识别量上来了,就需要代理:CaptchaFox 不只评估浏览器,也评估小组件所在的网络,因此同一个地址反复识别会把这个地址推向交互式挑战,再往后就是直接拒绝。测试和偶尔识别用一个地址就够。超出这个范围,请在 CapSkip 中配置代理池,让它分摊负载;如果 token 必须来自特定网络,也可以在每个请求里单独发送代理。

向你的 CapSkip API 端点(/in.php)提交 HTTP GET 或 POST 请求,method 为 method=captchafox。表单字段和 JSON 请求体都支持,字段名完全相同;另外 GET 同样可用,因为 CaptchaFox 没有需要上传的图片。

提交 CaptchaFox(表单字段):

curl -X POST \
-d "key=YOUR_API_KEY" \
-d "method=captchafox" \
-d "sitekey=sk_xtNxpk6fCdFbxh1_xJeGflSdCE9tn99G" \
-d "pageurl=https://www.example.com/signup" \
-d "json=1" \
http://127.0.0.1:8080/in.php

提交 CaptchaFox(JSON 请求体):

curl -X POST \
-H "Content-Type: application/json" \
-d '{
    "key": "YOUR_API_KEY",
    "method": "captchafox",
    "sitekey": "sk_xtNxpk6fCdFbxh1_xJeGflSdCE9tn99G",
    "pageurl": "https://www.example.com/signup",
    "json": 1
}' \
http://127.0.0.1:8080/in.php

如果一切正确,CapSkip 会以纯文本形式返回验证码 ID: OK|2122988149。如果发送了 json=1 参数,返回的则是 JSON 封装格式。无论哪种形式,返回的值都是你用于轮询结果的验证码 ID。

{
    "status": 1,
    "request": "2122988149"
}

否则 CapSkip 会返回相应的错误码。缺少 site key 或页面 URL 不可用时,会在提交请求本身上就被拒绝,而不是等到轮询之后才报错,因此你能在出错的那次请求上直接发现问题。

请等待约 5 秒,然后向结果接口(/res.php),并带上你收到的验证码 ID。一次 CaptchaFox 识别会运行真实的浏览器会话,因此可以预期它比 ALTCHA 这类基于计算的方法耗时更长,而在绘制出交互式挑战时还会更久一些。

GET 请求参数列表
参数类型是否必填说明
key字符串是*你的 CapSkip API 密钥。仅当启用了 API 密钥验证 时才需要。
action字符串是指定 get 以获取验证码答案。
id整数是由以下请求返回的验证码 ID: in.php 请求。
json整数
默认:0
否0 以纯文本返回响应。 1 以 JSON 返回响应。
curl "http://127.0.0.1:8080/res.php?key=YOUR_API_KEY&action=get&id=2122988149&json=1"

如果验证码已经识别完成,CapSkip 会以 JSON 格式返回结果:

{
    "status": 1,
    "request": "177f50c25b845601e5c779cdb51b040d523e8ab69efb4d5b343e28df07d05076",
    "solution": {
        "token": "177f50c25b845601e5c779cdb51b040d523e8ab69efb4d5b343e28df07d05076",
        "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
    },
    "cost": "0.00145",
    "createTime": 1788863246,
    "endTime": 1788863262,
    "errorId": 0,
    "solveCount": 1,
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
}

request 和 solution.token 始终是同一个字符串,因此你的客户端需要哪一个就读哪一个。如果不带 json=1,返回的结果就只是 OK|<token>.

当识别结果附带浏览器 User-Agent 时,它会以 userAgent返回,顶层和 solution内部都会包含。在纯文本模式下,从 X-CaptchaFox-User-Agent 响应头读取它。提交 token 时请发送该 User-Agent。

如果验证码尚未识别完成,CapSkip 会返回 CAPCHA_NOT_READY,其拼写方式与其他所有方法完全一致。请等待 5 秒后重复该请求。

轮询循环请以 errorId为判断依据,而不是 status. status 是整数 1 ,也就是 res.php 契约一直以来返回的内容,也是所有兼容 SDK 读取的字段。如果你的客户端是参照某个展示了 "status": "ready"的文档页面写的,那么请改为检查 errorId === 0 ,或者检查是否存在 solution.token ,而不是前者。

提交 token

CaptchaFox 小组件会把 token 放进一个表单字段,该字段名为 cf-captcha-response的表单字段中。请把 token 原封不动地填进该字段,与小组件本身提交的内容完全一致:

POST https://www.example.com/signup
Content-Type: application/x-www-form-urlencoded

email=someoneexample.com&cf-captcha-response=177f50c25b845601e5c779cdb51b040d...

有些接入方式改为从 JSON 请求体的字段里读取 token,所以请检查目标页面自身的提交请求发送了什么,并照着做。请把 token 当作不透明字符串原样传递。它会在服务器端针对产生它的那个会话进行校验,因此任何改动都会让它失效。

token 的有效期很短。请尽快提交,不要在用户填写表单期间一直持有;如果表单被放弃后又重新填写,请再识别一次。

选择小组件来源

CaptchaFox 会从两个位置发布它的小组件,站点加载的是哪一个,决定了它期望收到的 token 形态。需要发送 api_server 的场景只有一个:目标页面没有使用默认来源。

api_serverToken默认值说明
https://cdn.captchafox.com/普通的是标准小组件,绝大多数站点用的都是它。当你什么都不发送时,CapSkip 加载的就是它。
https://s.uicdn.com/mampkg/带 MAM_ 前缀否某些平台内嵌的打包版本。它返回的 token 会带有前缀 MAM_。请完全按照目标页面 script 标签中出现的样子,原样传入完整的包路径。

请从目标页面上加载小组件的那个 <script> 标签中读取该值。如果你发送了错误的来源,识别依然会成功,但返回的 token 格式是目标站点不会接受的,这看起来更像一次静默的校验失败,而不是一个报错。

capy puzzle solver Capy Puzzle

Capy Puzzle 是一种拖放式验证码:从一张照片中切出一块拼图,访客要把它滑回原来的缺口。CapSkip 会返回小组件本应写入页面的那三个值,可以直接随你的表单一起提交。

工作原理

在本页的各种验证码中,Capy 很特别:挑战的任何部分都不是由服务器签发的。小组件会自己生成 challenge key,向 Capy API 请求属于这个 key 的拼图,再由访客把拼图块拖到位。没有需要事先获取的 token,也没有需要重放的握手过程。

答案不是一个坐标。小组件会记录拼图块被拖动的轨迹,并把它编码成一个字符串,所以你提交回目标站点的是一段可信的拖动过程,而不是一个终点。CapSkip 会替你生成这条轨迹。

完整流程分为四步:

  1. 从目标页面读取验证码密钥。
  2. 将其提交到 /in.php 连同 method=capy.
  3. 轮询 /res.php 直到结果就绪。
  4. 把返回的三个值填回目标表单中提交。
识别前需要准备什么

只有两个值,而且都直接从目标页面读取。

参数来源
captchakey该网站的公开 Capy 密钥,其前缀通常为 PUZZLE_。它在页面源码中显示为 capy_captchakey,在小组件脚本 URL 中则是 k 查询参数。
pageurl小组件所在页面的完整 URL。Capy 不会看到这个值,但密钥是按站点注册的,因此请发送真实的页面。

还有第三个值得确认的值: api_server,即该密钥所属 Capy API 的根地址。它可以从同一个 script 标签中读取。CapSkip 默认使用 https://jp.api.capy.me,线上服务就在这个地址;只有当目标页面指向别处时,才需要另行指定。

有些验证码识别服务的文档里仍然写着 api.capy.me ,缺少区域前缀。该主机已经无法解析。如果你是从别处复制过来的,请把它删掉,让 CapSkip 使用默认值。

在哪里查找验证码密钥

在目标页面上打开浏览器开发者工具,找到 Capy 小组件。站点加载它的两种方式里,密钥都是可见的。

<!-- In the page source, as the widget configuration -->
<div id="capy"></div>
<script>
  window.capyOptions = {
    captchakey: "PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v",
    puzzle_div: "capy"
  };
</script>

<!-- Or in the script URL itself, as the k parameter -->
<script src="https://jp.api.capy.me/puzzle/get_js/?k=PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v"></script>

该脚本 URL 的根地址,也就是上例中的 https://jp.api.capy.me/ ,就是你在需要时要发送的 api_server 值。

答案是三个值,而不是一个 token

这是本页所有方法中唯一的结构性差异,值得在编写客户端之前先读一遍。reCAPTCHA、Turnstile 和 CaptchaFox 返回的都是一个不透明的字符串,而 Capy 的结果是三个各自独立、只有一起使用才有效的值。

返回的值填入的目标表单字段
captchakeycapy_captchakey
challengekeycapy_challengekey
answercapy_answer

由于单纯的 OK|<token> 只能容纳一个值而不是三个, /res.php 在这个方法下会返回完整的解决方案对象。纯文本响应里就带着它,JSON 响应的 request 字段同样如此。第四个字段 respKey会返回一个空字符串,以兼容那些针对其他服务编写的客户端。对拼图识别来说它不携带任何信息,可以忽略。

POST 请求参数列表
参数类型是否必填说明
key字符串是*你的 CapSkip API 密钥。仅当启用了 API 密钥验证 时才需要。
method字符串是必须为 capy。表示你提交的是 Capy Puzzle 验证码。
captchakey字符串是从目标页面读取到的验证码密钥,其前缀通常为 PUZZLE_. sitekey 和 websiteKey 均可作为别名使用。
pageurl字符串是小组件所在页面的完整 URL。
api_server字符串否该密钥所属 Capy API 的根地址。默认值为 https://jp.api.capy.me.
version字符串
默认:puzzle
否挑战类型。只有 puzzle 会被识别。参见下方的 Puzzle 与 Avatar 一节。
userAgent字符串否随拼图请求一起发送的 User-Agent。可选,很少需要设置。
proxy字符串否代理地址。接受 IP:PORT, LOGIN:PASSWORD@IP:PORT 或 IP:PORT:LOGIN:PASSWORD.
proxytype字符串否代理类型: HTTP, HTTPS, SOCKS5 或 SOCKS5H.
json整数
默认:0
否0 以纯文本返回响应。 1 以 JSON 返回响应。

CapSkip 并不强制这个方法使用代理,但只要你的识别量上来了,就需要代理。每次识别都会通过一次实时请求向 Capy API 取一份新的拼图,而同一个地址持续发出这样的请求,正是限流机制要抓的模式。测试和偶尔识别用一个地址就够。超出这个范围,请在 CapSkip 中配置代理池,让它分摊负载;如果某次识别必须来自特定网络,也可以在每个请求里单独发送代理。

向你的 CapSkip API 端点(/in.php)提交 HTTP GET 或 POST 请求,method 为 method=capy。表单字段、查询字符串和 JSON 请求体都以完全相同的字段名被接受,因为 Capy 没有需要上传的图片。

提交 Capy Puzzle(表单字段):

curl -X POST \
-d "key=YOUR_API_KEY" \
-d "method=capy" \
-d "captchakey=PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v" \
-d "pageurl=https://www.example.com/login" \
-d "json=1" \
http://127.0.0.1:8080/in.php

提交 Capy Puzzle(JSON 请求体):

curl -X POST \
-H "Content-Type: application/json" \
-d '{
    "key": "YOUR_API_KEY",
    "method": "capy",
    "captchakey": "PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v",
    "api_server": "https://jp.api.capy.me/",
    "pageurl": "https://www.example.com/login",
    "json": 1
}' \
http://127.0.0.1:8080/in.php

如果一切正确,CapSkip 会以纯文本形式返回验证码 ID: OK|2122988149。如果发送了 json=1 参数,返回的则是 JSON 封装格式。无论哪种形式,返回的值都是你用于轮询结果的验证码 ID。

{
    "status": 1,
    "request": "2122988149"
}

否则 CapSkip 会返回相应的错误码。缺少验证码密钥、页面 URL 不可用,或者 api_server 不是一个 URL 时,都会在提交请求本身上就被拒绝,而不是等到轮询之后才报错,因此你能在出错的那次请求上直接发现问题。

等待大约 3 秒,然后向结果端点(/res.php),并带上你收到的验证码 ID。识别结果会被刻意推迟到一个真人可能达到的时长之后才返回,原因参见下方的 时间控制 一节。

GET 请求参数列表
参数类型是否必填说明
key字符串是*你的 CapSkip API 密钥。仅当启用了 API 密钥验证 时才需要。
action字符串是指定 get 以获取验证码答案。
id整数是由以下请求返回的验证码 ID: in.php 请求。
json整数
默认:0
否0 以纯文本返回响应。 1 以 JSON 返回响应。
curl "http://127.0.0.1:8080/res.php?key=YOUR_API_KEY&action=get&id=2122988149&json=1"

如果验证码已经识别完成,CapSkip 会以 JSON 格式返回结果:

{
    "status": 1,
    "request": {
        "captchakey": "PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v",
        "challengekey": "BalY2gJaI8uA2SGVOZhqBQ3V0CYSNNGP",
        "answer": "0xax8ex0xax84x0xkx7qx0x18x76x0x1ix6sx0x26x68x0x2gx5kx0x34x50x",
        "respKey": ""
    },
    "solution": {
        "captchakey": "PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v",
        "challengekey": "BalY2gJaI8uA2SGVOZhqBQ3V0CYSNNGP",
        "answer": "0xax8ex0xax84x0xkx7qx0x18x76x0x1ix6sx0x26x68x0x2gx5kx0x34x50x",
        "respKey": ""
    },
    "cost": "0.00299",
    "createTime": 1788863246,
    "endTime": 1788863250,
    "errorId": 0,
    "solveCount": 1
}

request 和 solution 携带的是同一个对象,你的客户端需要哪一个就读哪一个。如果不带 json=1,同一个对象会跟在 OK| 前缀之后,成为一行 JSON。客户端以纯文本方式读取 res.php ,拿到的就是这一行:

OK|{"captchakey":"PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v","challengekey":"BalY2gJaI8uA2SGVOZhqBQ3V0CYSNNGP","answer":"0xax8ex0xax84x0xkx7qx","respKey":""}

如果验证码尚未识别完成,CapSkip 会返回 CAPCHA_NOT_READY,与其他所有方法一样,就是这个拼写。请等待 3 秒后重复该请求。

每个结果只投递一次。第一次成功的轮询会返回结果并把它丢弃,之后对同一个 ID 的轮询都会返回空内容,因此请保存好携带这些值的那次回复。

提交识别结果

这三个值要填进 Capy 小组件本应自己填写的那些表单字段。请原样提交:

<input type="hidden" name="capy_captchakey"   value="PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v">
<input type="hidden" name="capy_challengekey" value="BalY2gJaI8uA2SGVOZhqBQ3V0CYSNNGP">
<input type="hidden" name="capy_answer"       value="0xax8ex0xax84x0xkx7qx0x18x76x0x1ix6sx">

不要对 answer 字符串做裁剪、重新编码或任何其他形式的清理。它就是小组件本应记录下来的拖动轨迹,目标站点的后端会拿它与自己签发的挑战进行校验,因此任何改动都会让它失效。

challenge key 是一次性的,而且有效期很短。CapSkip 会为每次识别生成一个新的 challenge key,拼图也与它绑定,所以请尽快提交这三个值,而不要缓存它们,也绝不要把同一个 challengekey 用于第二次提交。

时间控制:Capy 会拒绝来得太快的答案

如果你自己动手对接 Capy,这部分会让你白白搭进去一天。Capy 会测量从发出拼图到收到答案之间的实际时间差,并拒绝任何看起来超出常人速度的答案。它的拒绝信息和答错时收到的完全一样,所以一个在 200 毫秒内返回的完全正确的答案,和一个有问题的识别工具没有任何区别。

在 Capy 自己的登录页面上实测,保持答案正确不变:

从拼图到验证的耗时结果
0.48 secondsRefused
1.03 秒及以上,最高测试到 4 秒Accepted

因此,CapSkip 会把每个结果都保留到自绘制拼图起经过足够时间之后再返回,大约是实测下限的两倍,因为这个下限属于 Capy,随时可能变动。这一等待会自动应用到每一次识别,无需任何配置。它消耗的是延迟而不是吞吐量;如果你是通过轮询获取结果,它是不可见的:任务只是大约需要两秒。

Puzzle 与 Avatar

Capy 提供两种挑战类型。它们是位于不同端点后面的不同挑战,CapSkip 只识别其中一种。

versionChallenge是否支持说明
puzzleAssemble a puzzle是默认类型,几乎所有部署用的都是它。把切下来的拼图块拖回它原本所在的缺口。
avatarDrag an object否另一种独立的挑战类型。提交时会直接返回 ERROR_BAD_PARAMETERS 而被拒绝,不会去尝试识别。

不发送 version 即表示 puzzle,所以大多数接入方式从来不会设置它。 avatar 请求被有意拒绝而不是尝试识别:把它当作 puzzle 来回答,会返回一个目标站点不接受的结果,这比一个明确的错误更糟,因为它看起来像是识别工具出了故障,而不是一种不受支持的挑战。

friendly captcha solver Friendly Captcha

Friendly Captcha 不要求访客做任何事,而是让访客的浏览器做一段算术运算。没有需要点击的图片,没有滑块,也没有音频备选方案,屏幕上根本没有可能做错的东西。CapSkip 会返回小组件本应生成的 token,可以直接随你的表单提交。

工作原理

Friendly Captcha 是一种工作量证明(proof of work)验证码。小组件会拿到一个难度设置,搜索哈希值低于该难度的数值,并把结果写入你表单中的隐藏字段。访客始终看不到任何东西,这正是该产品的用意:使用它的页面看起来就像没有验证码一样。

同一个名字下发布了两套完全不同的协议,而 sitekey 并不能告诉你某个站点用的是哪一套。它们只共用品牌和 sitekey 命名空间,此外毫无关系。判断该用哪一套是集成时首先必须做对的事,因此下文单独为它设了一节。

完整流程分为四步:

  1. 从目标页面读取 sitekey,并连同小组件脚本 URL 一起读出。
  2. 将两者一起提交给 /in.php 连同 method=friendly_captcha.
  3. 轮询 /res.php 直到 token 就绪。
  4. 把 token 放进小组件本应填写的表单字段,然后提交。

该方法的识别时间不是固定值。服务会在请求发出的那一刻决定这次请求值多少工作量,因此同一个 sitekey 在不同时候的开销可能明显不同。请在轮询时为此留出余量,不要假定一个固定时长。

识别前需要准备什么

有两个值是必需的,第三个只要能拿到就值得一并发送。

参数来源
sitekey加载的 data-sitekey 属性,取自小组件元素,也就是带有以下 class 的元素: class="frc-captcha".
pageurl小组件所在页面的完整 URL。
module_script加载的 src 属性,取自带有以下属性的小组件脚本标签: type="module"。不是必需的,但它能告诉 CapSkip 站点使用的是哪个协议版本,因此页面上若有就发送它。
版本 1 与版本 2

这一节如果跳过,会白白搭进去你一个下午。两个版本都在运行,也都有人在用,而且为其中一个版本注册的 sitekey 在另一个版本的端点上同样会有响应。识别错版本,你会拿到一个格式完全正确、却被目标站点拒绝的 token,而且任何地方都不会提示问题出在版本上。

version小组件包是否支持说明
v1friendly-challenge是最初的开源小组件。其脚本为 widget.module.min.js 或 widget.min.js。在没有其他线索时的默认版本。
v2@friendlycaptcha/sdk是当前的 SDK。其脚本为 site.min.js。加载该脚本的页面就是 v2,不论其他特征看起来如何。

CapSkip 按以下顺序判断版本,并在得到第一个答案时停止:

  1. 加载的 version 参数,如果你发送了的话。 v1 和 v2 是可用的写法;直接写 1 或 2 也可以。
  2. 小组件脚本 URL,取自 module_script 或 nomodule_script。这是现有信号中最可靠的一个,因为它就是站点实际加载的构建版本。
  3. 两者都没有时, v1.

CapSkip 不会向服务查询某个 sitekey 属于哪个版本,因为两个版本它都会作答。请发送 version,或者发送脚本 URL,这个问题就不会出现。

在哪里找到 sitekey

在目标页面上打开浏览器开发者工具,找到小组件元素。sitekey 和脚本在页面源码中彼此相邻,两者合在一起就提供了该方法所需的全部信息。

<!-- Version 1: the friendly-challenge widget -->
<div class="frc-captcha" data-sitekey="FCMEXAMPLE1234AB"></div>
<script type="module" src="https://cdn.example.com/[email protected]/widget.module.min.js"></script>
<script nomodule src="https://cdn.example.com/[email protected]/widget.min.js"></script>

<!-- Version 2: the @friendlycaptcha/sdk widget -->
<div class="frc-captcha" data-sitekey="FCMEXAMPLE1234AB"></div>
<script type="module" src="https://cdn.example.com/@friendlycaptcha/[email protected]/site.min.js"></script>

有些部署会从自有域名而不是 CDN 提供该脚本。起决定作用的是文件名,而不是它来自哪个主机。

POST 请求参数列表
参数类型是否必填说明
key字符串是*你的 CapSkip API 密钥。仅当启用了 API 密钥验证 时才需要。
method字符串是必须为 friendly_captcha。表示你提交的是 Friendly Captcha。
sitekey字符串是加载的 data-sitekey 值,从目标页面的小组件元素上读取。
pageurl字符串是小组件所在页面的完整 URL。
version字符串
默认值:v1
否协议版本,参见上文 v1 或 v2。参见下方的 版本 1 与版本 2 一节。
module_script字符串否加载的 src 属性,取自带有以下属性的小组件脚本标签: type="module"。在未发送 version 时用来判断版本。
nomodule_script字符串否加载的 src 属性,取自带有以下属性的小组件脚本标签: nomodule。出于同样的原因也会读取。
api_server字符串否CapSkip 专有。sitekey 所属的数据驻留端点。可接受 global (默认值)、 eu,或者一个完整 URL。参见 数据驻留 一节。
useragent字符串否随请求发送的 User-Agent。可选,很少需要。
proxy字符串否代理地址。接受 IP:PORT, LOGIN:PASSWORD@IP:PORT 或 IP:PORT:LOGIN:PASSWORD.
proxytype字符串否代理类型: HTTP, HTTPS, SOCKS5 或 SOCKS5H.
json整数
默认:0
否0 以纯文本返回响应。 1 以 JSON 返回响应。

CapSkip 不会强制该方法使用代理,但在本页几乎所有方法中,这里是最早会需要代理的。服务会决定每个请求值多少工作量,并且对已经见过很多次的地址调高这个数值:在单个地址上针对同一个 sitekey 实测,难度设置在一轮测试过程中持续上升;而官方公布的区间显示,全新地址与被大量使用的地址之间,换取同一个 token 所需的工作量相差接近三十倍。用一个地址做测试或偶尔识别没有问题。超出这个范围,就在 CapSkip 中配置代理池让它分摊负载,或者在某次识别必须来自特定网络时,随请求发送单独的代理。

向你的 CapSkip API 端点(/in.php)提交 HTTP GET 或 POST 请求,method 为 method=friendly_captcha。表单字段、查询字符串和 JSON 请求体都以相同的字段名被接受,因为该方法没有图片需要上传。

提交 Friendly Captcha(表单字段):

curl -X POST \
-d "key=YOUR_API_KEY" \
-d "method=friendly_captcha" \
-d "sitekey=FCMEXAMPLE1234AB" \
-d "pageurl=https://www.example.com/signup" \
-d "version=v2" \
-d "json=1" \
http://127.0.0.1:8080/in.php

提交 Friendly Captcha(JSON 请求体,用脚本 URL 代替显式的 version):

curl -X POST \
-H "Content-Type: application/json" \
-d '{
    "key": "YOUR_API_KEY",
    "method": "friendly_captcha",
    "sitekey": "FCMEXAMPLE1234AB",
    "pageurl": "https://www.example.com/signup",
    "module_script": "https://cdn.example.com/@friendlycaptcha/[email protected]/site.min.js",
    "nomodule_script": "https://cdn.example.com/@friendlycaptcha/[email protected]/site.compat.js",
    "json": 1
}' \
http://127.0.0.1:8080/in.php

如果一切正确,CapSkip 会以纯文本形式返回验证码 ID: OK|2122988149。如果发送了 json=1 参数,返回的则是 JSON 封装格式。无论哪种形式,返回的值都是你用于轮询结果的验证码 ID。

{
    "status": 1,
    "request": "2122988149"
}

否则 CapSkip 会返回相应的错误码。缺少 sitekey 或页面 URL 不可用,会在提交请求本身上就被拒绝,而不是等到轮询之后,因此你会在出错的那个请求上就发现问题。

请等待约 5 秒,然后向结果接口(/res.php)发送一个 HTTP GET 请求,并带上你收到的验证码 ID。

GET 请求参数列表
参数类型是否必填说明
key字符串是*你的 CapSkip API 密钥。仅当启用了 API 密钥验证 时才需要。
action字符串是指定 get 以获取验证码答案。
id整数是由以下请求返回的验证码 ID: in.php 请求。
json整数
默认:0
否0 以纯文本返回响应。 1 以 JSON 返回响应。
curl "http://127.0.0.1:8080/res.php?key=YOUR_API_KEY&action=get&id=2122988149&json=1"

如果验证码已被识别,CapSkip 会以 JSON 格式返回 token。token 位于 request,以及 solution.token 也携带同一个字符串,供期望在那里读取它的客户端使用:

{
    "status": 1,
    "request": "c62c4da36bbaf7f253873035832709ef.aqwpWwdbzRWKY/UQAQwwpgAAAAAAAAAAM7hBvJOzqjc=.AAAAAArcCQABAAAAxv8QAAIAAACKYRgA.AgAB",
    "solution": {
        "token": "c62c4da36bbaf7f253873035832709ef.aqwpWwdbzRWKY/UQAQwwpgAAAAAAAAAAM7hBvJOzqjc=.AAAAAArcCQABAAAAxv8QAAIAAACKYRgA.AgAB"
    },
    "cost": "0.00299",
    "createTime": 1789667786,
    "endTime": 1789667807,
    "errorId": 0,
    "solveCount": 1
}

不带 json=1时,同一个 token 会跟在 OK| 前缀之后,以纯文本形式返回,这也是把 res.php 当作文本读取的客户端会拿到的内容:

OK|c62c4da36bbaf7f253873035832709ef.aqwpWwdbzRWKY/UQAQwwpgAAAAAAAAAAM7hBvJOzqjc=.AAAAAArcCQABAAAAxv8QAAIAAACKYRgA.AgAB

如果验证码尚未识别完成,CapSkip 会返回 CAPCHA_NOT_READY,其拼写方式与其他所有方法完全一致。请等待 5 秒后重复该请求。

每个结果只投递一次。第一次成功的轮询会返回 token 并把它丢弃,之后对同一个 ID 的每次轮询都会返回空的响应体,因此请在携带它的那次响应里就把 token 保存下来。

两个版本产生的 token 在形态和大小上差别很大。v1 的 token 由四段以点号分隔的部分组成,长度为几百个字符,如上所示。v2 的 token 则是一个不透明的单一字符串,以 AQQA. 开头,长度约为六千字节,因此请确保承载它的地方,无论是隐藏字段、数据库列还是转发的请求,都按这个长度留足空间。

提交 token

token 要放进小组件本应自己填写的隐藏字段,而两个版本使用的字段名并不相同。把已经跑通的 v1 集成搬到 v2 站点上的人,常在这里栽跟头。

versiontoken 写入的表单字段
v1frc-captcha-solution
v2frc-captcha-response
<!-- 版本 1 -->
<input type="hidden" name="frc-captcha-solution" value="c62c4da3...AgAB">

<!-- 版本 2 -->
<input type="hidden" name="frc-captcha-response" value="AQQA.vW7kd3CujKT8PaQgEcW18QaH...">

原样提交 token。不要裁剪、不要重新编码,也不要去掉看起来像填充符的字符:它的每一部分都会对照签发它的那次挑战进行校验,任何改动都会让它失效。

站点可以自行重命名该字段,也确实有站点这样做。如果你要对接的页面用的是别的名字,请从小组件元素上读出该名字并改用它。如果页面为小组件定义了回调,把 token 作为唯一参数调用该回调也能达到同样的效果。

数据驻留

Friendly Captcha 为不同的数据驻留地区运行各自独立的端点,而一个 sitekey 只属于其中之一。两个端点都会为同一个 sitekey 签发 token,因此把请求发到错误的端点,会得到一个在这边看起来完全有效、却被目标站点拒绝的 token,而且除了校验失败之外拿不到任何更有用的信息。

小组件通过 data-api-endpoint 属性指明自己所属的地区。如果目标页面带有该属性,请把相同的值传给参数 api_server。该参数是 CapSkip 专有的,在别处没有对应项,因此针对其他服务编写的客户端不会发送它:当目标页面使用区域端点时,请自行加上。

api_server它选择的内容
global默认值。属性缺失时使用,这也是常见情况。
eu欧洲端点,对应页面上的 data-api-endpoint="eu".
A full URL自托管或其他自定义部署。请完全按照页面给出的形式发送该端点。

datadome solver DataDome

DataDome 是许多大型商城、新闻网站和交易平台背后的机器人防护系统。当它怀疑某个访客时,网站会返回一个拦截页面而不是正常内容,该页面包含一个滑块或简短的设备检测。通过后会设置一个 datadome Cookie,携带该 Cookie 的请求即可放行。CapSkip 会通过你提交的挑战并返回该 Cookie,这样即使脚本本身没有浏览器,也能继续抓取该网站。

工作原理

被拦截的请求会返回状态码 403 (有时为 401)和一个小型 HTML 页面。该页面带有一个脚本对象 var dd={...},并从 geo.captcha-delivery.com加载 iframe 中的挑战。CapSkip 需要的正是这个 iframe 地址:它被称为 captcha_url 一节。

DataDome 有两种挑战,CapSkip 通过同一个方法处理这两种:

  • 滑块。 iframe 路径为 /captcha/ ,访客需要拖动滑块,有时要拖到拼图块上。
  • 设备检测。 iframe 路径为 /interstitial/ ,页面会显示几秒钟的“Verifying the device”(正在验证设备)。如果检测未通过,DataDome 会转为滑块挑战,CapSkip 也会继续处理。

完整流程分为四步:

  1. 请求目标页面,收到 DataDome 拦截页面。
  2. 根据该页面构建 captcha_url (见下文),并将其提交到 /in.php 连同 method=datadome.
  3. 轮询 /res.php ,直到 Cookie 就绪。
  4. 带上该 Cookie,使用相同的 User-Agent 和相同的 IP 地址重新发送请求。

一次识别通常需要 3 到 20 秒。设备检测耗时较短,滑块耗时较长。

Cookie 只属于一个 IP 和一个浏览器

DataDome 会将 Cookie 绑定到获取它时所用的 IP 地址和 User-Agent。从其他地址或使用不同的 User-Agent 重放 Cookie,通常会被拒绝,拦截页面会再次出现。由此得出两条规则:

  • 识别必须从你之后使用该 Cookie 的地址发起。 如果你的请求通过代理发出,请随任务一起发送该代理。如果请求来自运行 CapSkip 的电脑,则不要发送代理。
  • 发送你将要使用的 User-Agent。 它必须是 Windows 版 Chrome,且主版本号与 CapSkip 用于识别的浏览器相同。任何其他 User-Agent 都会得到被网站拒绝的 Cookie,因此会被直接拒绝,并返回 ERROR_WRONG_USERAGENT。Firefox、Safari、Edge 以及移动端 User-Agent 也因同样原因被拒绝。

如果拦截页面本身表明该地址已被封禁,那么从该地址发出的任何请求都无法通过,因此任务会被立即拒绝,返回 ERROR_PROXY_BANNED ,而不会尝试识别。你可以提前发现这种情况: dd 对象带有 't':'bv' 而不是 't':'fe'。请更换地址后重新请求页面。

构建 captcha_url

如果你收到的拦截页面中已包含 iframe,直接使用其 src 属性即可。大多数拦截页面会改用脚本来构建 iframe,因此你需要根据 dd 对象拼出相同的地址。拦截页面如下所示:

<script>
var dd={'rt':'c','cid':'AHrlqAAAAAMA7KBvCr0n4cEAG330MA==','hsh':'D013AA612AB2224D03B2318D0F5B19','t':'fe','s':45225,'e':'549255a0289eaf17af60822309ab8db4','host':'geo.captcha-delivery.com','cookie':'KzE6_pZBBTHdCdvZkauT9VAshi0wS7~v5_M_MacEtdw8'}
</script>
<script src="https://ct.captcha-delivery.com/c.js"></script>

的可选 action 值。 rt 决定路径: c 即表示 https://geo.captcha-delivery.com/captcha/ 和 i 即表示 https://geo.captcha-delivery.com/interstitial/。查询字符串由其他字段构建:

URL 参数值
initialCid加载的 cid 字段中。
hash加载的 hsh 字段中。
cid加载的 cookie 字段中。
t加载的 t 字段。仅出现在 /captcha/ 中。
referer你所请求页面的 URL。
s加载的 s 字段中。
e加载的 e 字段(如有)。
b加载的 b 字段。仅出现在 /interstitial/ 中。
dm始终为 cd.

所有值都要进行 URL 编码。JavaScript 示例:

const dd = JSON.parse(html.match(/var dd=(\{[^}]*\})/)[1].replace(/'/g, '"'));
const path = dd.rt === 'i' ? 'interstitial' : 'captcha';
const q = new URLSearchParams({ initialCid: dd.cid, hash: dd.hsh, cid: dd.cookie });
if (dd.t) q.set('t', dd.t);
q.set('referer', pageUrl);
q.set('s', dd.s);
if (dd.e) q.set('e', dd.e);
if (dd.b) q.set('b', dd.b);
q.set('dm', 'cd');
const captchaUrl = `https://${dd.host || 'geo.captcha-delivery.com'}/${path}/?${q}`;

每个拦截页面都是一个新的挑战。收到后请尽快提交;处理下一个任务时请重新请求页面,不要复用旧的 captcha_url.

POST 请求参数列表
参数类型是否必填说明
key字符串是*你的 CapSkip API 密钥。仅当启用了 API 密钥验证 时才需要。
method字符串是必须为 datadome。表示你提交的是 DataDome 挑战。
captcha_url字符串是挑战 iframe 的地址,域名为 geo.captcha-delivery.com。参见下方的 构建 captcha_url 一节。
pageurl字符串是返回拦截页面的那个页面的完整 URL。
userAgent字符串否你将随 Cookie 一起发送的 User-Agent:Windows 版 Chrome,主版本号与 CapSkip 用于识别的浏览器相同。如果不发送,CapSkip 会使用该浏览器的 User-Agent,并在回复中注明,你可以原样复制使用。
proxy字符串否代理地址。接受 IP:PORT, LOGIN:PASSWORD@IP:PORT 或 IP:PORT:LOGIN:PASSWORD。请发送你后续请求将使用的代理。
proxytype字符串否代理类型: HTTP, HTTPS, SOCKS5 或 SOCKS5H.
json整数
默认:0
否0 以纯文本返回响应。 1 以 JSON 返回响应。

CapSkip 不强制此方法使用代理。用于测试或偶尔识别时,一个地址就够了。超出这个范围,DataDome 对地址的评分与对浏览器的评分同样重要,来自同一 IP 的持续拦截页面最终会导致该 IP 在该网站被封禁。大批量使用时,请让你的请求和任务都通过住宅代理池发出,每个任务一个代理,并通过获取每个 Cookie 的那个代理来使用它。请使用粘性代理,即至少在几分钟内保持同一出口 IP 的代理。每次连接都更换 IP 的代理无法产生可用的 Cookie:挑战会从多个地址加载,而 Cookie 在下一个地址上会被拒绝。

CapSkip 还有一个专用的 DataDome 代理列表,位于 设置, 验证码类型, DataDome, 配置代理中。未附带代理的任务会通过该列表识别。除非携带 Cookie 的请求也通过这些代理发出,否则请将其留空,因为 Cookie 只能在识别它的地址上使用。回复会注明每个任务所用的代理。随任务发送的代理始终优先于该列表。

向你的 CapSkip API 端点(/in.php)提交 HTTP GET 或 POST 请求,method 为 method=datadome。表单字段、查询字符串和 JSON 请求体均可接受,字段名称相同。

提交 DataDome:

curl -X POST \
--data-urlencode "key=YOUR_API_KEY" \
--data-urlencode "method=datadome" \
--data-urlencode "captcha_url=https://geo.captcha-delivery.com/captcha/?initialCid=AHrlqAAAAAMA7KBvCr0n4cEAG330MA&hash=D013AA612AB2224D03B2318D0F5B19&cid=KzE6_pZBBTHdCdvZkauT9VAshi0wS7~v5_M_MacEtdw8&t=fe&referer=httpswww.example.com&s=45225&e=549255a0289eaf17af60822309ab8db4&dm=cd" \
--data-urlencode "pageurl=https://www.example.com/" \
--data-urlencode "userAgent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.0.0 Safari/537.36" \
--data-urlencode "proxy=login:[email protected]:8080" \
--data-urlencode "proxytype=HTTP" \
--data-urlencode "json=1" \
http://127.0.0.1:8080/in.php

如果一切正确,CapSkip 会以纯文本形式返回验证码 ID: OK|2122988149。如果发送了 json=1 参数,返回的则是 JSON 封装格式。无论哪种形式,返回的值都是你用于轮询结果的验证码 ID。

{
    "status": 1,
    "request": "2122988149"
}

否则,CapSkip 会直接在提交请求中返回错误代码,此时尚未进行任何处理:

错误原因
ERROR_BAD_PARAMETERScaptcha_url 缺失,或不是 DataDome 挑战地址。
ERROR_PAGEURLpageurl 缺失,或不是 http 或 https 地址。
ERROR_WRONG_USERAGENTUser-Agent 不是 Windows 版 Chrome,或其主版本号与 CapSkip 识别所用的版本不同。
ERROR_PROXY_BANNED拦截页面表明该地址已被封禁 (t=bv).
ERROR_PROXY_FORMAT无法读取 proxy 值。

请等待约 5 秒,然后向结果接口(/res.php)发送一个 HTTP GET 请求,并带上你收到的验证码 ID。

GET 请求参数列表
参数类型是否必填说明
key字符串是*你的 CapSkip API 密钥。仅当启用了 API 密钥验证 时才需要。
action字符串是指定 get 以获取验证码答案。
id整数是由以下请求返回的验证码 ID: in.php 请求。
json整数
默认:0
否0 以纯文本返回响应。 1 以 JSON 返回响应。
curl "http://127.0.0.1:8080/res.php?key=YOUR_API_KEY&action=get&id=2122988149&json=1"

如果挑战已通过,CapSkip 会原样返回 DataDome 设置的 Cookie,包括其属性:

{
    "status": 1,
    "request": "datadome=FAPPpli4jkT1MZGtgF8ez7mjSz8MrJ9fGRNE51Gj79cq9fXp5xn~F1P5APWjqvJojf4U9dq0R81Xaj2hpYtjle95mkg; Max-Age=31536000; Domain=.example.com; Path=/; Secure; SameSite=Lax",
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.0.0 Safari/537.36",
    "proxy": "203.0.113.10:8080"
}

userAgent 是获取该 Cookie 时所用的 User-Agent:即你发送的那个,或在你未发送时 CapSkip 使用的那个。每个携带该 Cookie 的请求都要发送它。

proxy 仅在你的任务未发送代理、且 CapSkip 从其 DataDome 代理列表中选用了一个代理时出现。它是该代理的 host:port形式(不含登录信息),Cookie 只能通过它使用。如果任务发送了自己的代理,或 CapSkip 未使用代理识别,则没有此字段。

不带 json=1,Cookie 跟在 OK| 前缀之后以纯文本形式返回,同样的两个值会通过 X-DataDome-User-Agent 和 X-DataDome-Proxy 响应头返回:

OK|datadome=FAPPpli4jkT1MZGtgF8ez7mjSz8MrJ9fGRNE51Gj79cq9fXp5xn~F1P5APWjqvJojf4U9dq0R81Xaj2hpYtjle95mkg; Max-Age=31536000; Domain=.example.com; Path=/; Secure; SameSite=Lax

如果挑战尚未通过,CapSkip 会返回 CAPCHA_NOT_READY,其拼写方式与其他所有方法完全一致。请等待 5 秒后重复该请求。

每个结果只交付一次。第一次成功轮询会返回 Cookie 并将其丢弃,因此请从携带它的那次回复中保存好。

无法通过的任务会返回 ERROR_CAPTCHA_UNSOLVABLE。有两种失败会改为指明地址,因为通过该地址重新提交仍会失败: ERROR_PROXY_BANNED 表示 DataDome 在识别过程中封禁了该地址, ERROR_PROXY_CONNECT_REFUSED 表示无法连接到代理。

使用 Cookie

第一个分号之前的部分就是 Cookie 本身。在下一次请求中发送它,并使用与任务中相同的代理和 User-Agent:

curl "https://www.example.com/" \
-x "http://login:[email protected]:8080" \
-A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.0.0 Safari/537.36" \
-b "datadome=FAPPpli4jkT1MZGtgF8ez7mjSz8MrJ9fGRNE51Gj79cq9fXp5xn~F1P5APWjqvJojf4U9dq0R81Xaj2hpYtjle95mkg"

如果你使用 Cookie 存储(cookie jar),也可以保存整个字符串: Domain, Path 和 Max-Age 是 DataDome 为该网站选定的值。

DataDome 可能会在后续响应中发送新的 datadome Cookie。请像浏览器那样保留最新的一个。如果拦截页面仍然出现,说明网站决定再次发起挑战:请根据它构建新的 captcha_url ,并提交新任务。

mtcaptcha solver MTCaptcha

MTCaptcha 是一种用于登录、注册和联系表单的文字验证码。访问者输入图片中显示的变形字符,小组件随后把一个已验证的 token 放入表单。CapSkip 接收您发送的 sitekey 和页面地址,返回一个有效的 MTCaptcha token,可直接随表单提交。

工作原理

MTCaptcha 小组件在页面上通过一个 sitekey 进行配置,该 sitekey 以 MTPublic-开头。访问者通过挑战后,MTCaptcha 会签发一个以 v1(开头的 token,小组件会将其写入名为 mtcaptcha-verifiedtoken的隐藏表单字段。表单提交时,网站会向 MTCaptcha 校验该 token。

CapSkip 只需要 sitekey 和页面地址。您无需自己加载或识别小组件:浏览器中显示的图片从不发送,CapSkip 返回的 token 本身即可被接受。完整流程分三步:

  1. 将 sitekey 和页面 URL 提交到 /in.php 连同 method=mt_captcha.
  2. 轮询 /res.php 直到 token 就绪。
  3. 提交表单,并将 token 放入 mtcaptcha-verifiedtoken.
查找 Sitekey

大多数页面会在页面源代码中用一个 mtcaptchaConfig 对象来配置小组件:

<script>
var mtcaptchaConfig = {
  "sitekey": "MTPublic-KzqLY1cKH",
  "action": "login"
};
</script>

按原样复制 sitekey。如果该对象还设置了 action,也请一并发送该值。如果小组件已经显示在页面上,同一个 sitekey 也出现在它位于 service.mtcaptcha.com上的 iframe 地址中,即查询字符串里的 sitekey.

POST 请求参数列表
参数类型是否必填说明
key字符串是*你的 CapSkip API 密钥。仅当启用了 API 密钥验证 时才需要。
method字符串是必须为 mt_captcha。表示您提交的是 MTCaptcha 挑战。 mtcaptcha 也同样可用。
sitekey字符串是页面上的 MTCaptcha sitekey,开头为 MTPublic-.
pageurl字符串是小组件所在页面的完整 URL。MTCaptcha 只为该 sitekey 注册的域名签发 token,因此请发送真实的页面地址。
action字符串否页面在其 mtcaptchaConfig 对象中设置的 action 名称(如果有)。
userAgent字符串否向 MTCaptcha 提交的 User-Agent。如果不发送,CapSkip 会使用 Windows 版 Chrome 的 User-Agent。
proxy字符串否代理地址。接受 IP:PORT, LOGIN:PASSWORD@IP:PORT 或 IP:PORT:LOGIN:PASSWORD.
proxytype字符串否代理类型: HTTP, HTTPS, SOCKS4, SOCKS5 或 SOCKS5H.
json整数
默认:0
否0 以纯文本返回响应。 1 以 JSON 返回响应。

CapSkip 不强制此方法使用代理。测试和偶尔识别时,一个地址就足够了。超出这个范围,请配置代理池:每次识别都会从 MTCaptcha 的服务请求新的挑战,而来自同一地址的持续请求正是限速机制要拦截的对象。在以下位置添加代理池: 设置, 验证码类型, MTCaptcha, 配置代理,未附带代理的任务会分散到该池中。当识别必须来自特定网络时,请随任务发送 proxy 。随任务发送的代理始终优先于列表。

向你的 CapSkip API 端点(/in.php)提交 HTTP GET 或 POST 请求,method 为 method=mt_captcha。表单字段、查询字符串和 JSON 请求体均可接受,字段名称相同。

提交 MTCaptcha:

curl -X POST \
--data-urlencode "key=YOUR_API_KEY" \
--data-urlencode "method=mt_captcha" \
--data-urlencode "sitekey=MTPublic-KzqLY1cKH" \
--data-urlencode "pageurl=https://www.example.com/login" \
--data-urlencode "action=login" \
--data-urlencode "json=1" \
http://127.0.0.1:8080/in.php

如果一切正确,CapSkip 会以纯文本形式返回验证码 ID: OK|2122988149。如果发送了 json=1 参数,返回的则是 JSON 封装格式。无论哪种形式,返回的值都是你用于轮询结果的验证码 ID。

{
    "status": 1,
    "request": "2122988149"
}

否则,CapSkip 会直接在提交请求中返回错误代码,此时尚未进行任何处理:

错误原因
ERROR_BAD_PARAMETERSsitekey 缺失,或不是 MTCaptcha sitekey(MTPublic- 后跟字母、数字、 - 或 _).
ERROR_PAGEURLpageurl 缺失,或不是 http 或 https 地址。
ERROR_PROXY_FORMAT无法读取 proxy 值。

请等待约 5 秒,然后向结果接口(/res.php)发送一个 HTTP GET 请求,并带上你收到的验证码 ID。

GET 请求参数列表
参数类型是否必填说明
key字符串是*你的 CapSkip API 密钥。仅当启用了 API 密钥验证 时才需要。
action字符串是指定 get 以获取验证码答案。
id整数是由以下请求返回的验证码 ID: in.php 请求。
json整数
默认:0
否0 以纯文本返回响应。 1 以 JSON 返回响应。
curl "http://127.0.0.1:8080/res.php?key=YOUR_API_KEY&action=get&id=2122988149&json=1"

如果挑战已通过,CapSkip 会返回 MTCaptcha token:

{
    "status": 1,
    "request": "v1(23ed91f2,61d9f4fe,MTPublic-KzqLY1cKH,079c683122559a6f**)"
}

不带 json=1时,token 以纯文本形式跟在 OK| 前缀之后,以纯文本形式返回:

OK|v1(23ed91f2,61d9f4fe,MTPublic-KzqLY1cKH,079c683122559a6f**)

如果挑战尚未通过,CapSkip 会返回 CAPCHA_NOT_READY,其拼写方式与其他所有方法完全一致。请等待 5 秒后重复该请求。

每个结果只交付一次。第一次成功的轮询会返回 token 并将其丢弃,因此请从携带它的那次回复中保存好 token。

无法通过的任务会返回 ERROR_CAPTCHA_UNSOLVABLE。有两种失败会直接指明任务本身的问题,因为原样重新提交仍会失败: ERROR_RECAPTCHA_INVALID_SITEKEY 表示 MTCaptcha 无法识别该 sitekey; ERROR_RECAPTCHA_INVALID_DOMAIN 表示该 sitekey 不允许在以下域名上使用: pageurl。这两个代码保留了历史名称,适用于所有方法。

提交 token

将 token 放入表单字段 mtcaptcha-verifiedtoken ,然后像页面一样提交表单。如果您自己提交表单:

curl -X POST "https://www.example.com/login" \
--data-urlencode "username=alice" \
--data-urlencode "password=YOUR_PASSWORD" \
--data-urlencode "mtcaptcha-verifiedtoken=v1(23ed91f2,61d9f4fe,MTPublic-KzqLY1cKH,079c683122559a6f**)"

在您控制的浏览器中,在提交表单前设置该隐藏字段:

document.querySelector('input[name="mtcaptcha-verifiedtoken"]').value = token;

token 有效期很短且只能使用一次:MTCaptcha 只接受一次,并且只在签发后的几分钟内有效。收到后请立即使用,每次提交表单都请求一个新的 token。网站会在自己的服务器上通过 MTCaptcha 的 checktoken 调用校验 token,该调用会检查 sitekey 和域名,因此 token 只在请求它的网站上有效。

使用代理

对于 reCAPTCHA v2、v3、Invisible、Enterprise、Cloudflare、极验、ALTCHA、CaptchaFox、Capy Puzzle 和 Friendly Captcha,你可以随每个任务发送一个代理。CapSkip 将通过该代理识别验证码,而不使用 CapSkip 应用中配置的代理池。

当目标网站检查验证码 token 是否与你自己的请求来自同一 IP 地址时(例如 Cloudflare 后面的站点、严格的 reCAPTCHA 评分或地理限制页面),这很有用。

POST 请求参数列表
参数 类型 是否必填 说明
proxy 字符串 否 代理地址。IP 认证格式: IP:PORT (示例: 123.123.123.123:3128)。登录/密码认证格式: login:password@IP:PORT
proxytype 字符串 否 代理类型。支持的值: HTTP, HTTPS, SOCKS5, SOCKS5H。默认: HTTP 当提供了 proxy 但省略了 proxytype 时。

错误代码

代码含义
ERROR_KEY_DOES_NOT_EXIST无效的 API 密钥。
ERROR_WRONG_USER_KEYAPI 密钥缺失或为空。
ERROR_WRONG_METHOD无效的 HTTP 方法或 action 参数中定义的自定义字符串。
ERROR_WRONG_ID_FORMAT验证码 ID 格式无效。
ERROR_BAD_PARAMETERS缺少或无效的必需参数。
ERROR_UPLOAD未提供图片数据或上传失败。
ERROR_INVALID_IMAGE图片格式无效或图片数据损坏。
ERROR_INVALID_BASE64无效的 base64 编码。
ERROR_TOO_BIG_CAPTCHA_FILESIZE图片大小超过 600 kB 或尺寸超过 1000px。
ERROR_CAPTCHA_UNSOLVABLE识别验证码失败。请提交新任务并重试。
ERROR_GOOGLEKEY无效的 googlekey 参数中定义的自定义字符串。
ERROR_PAGEURL无效的 pageurl 参数中定义的自定义字符串。
ERROR_ZERO_BALANCE该 API 密钥在此方法上已无可用额度。
ERROR_PROXY_FORMAT加载的 proxy 值无法解析。
ERROR_TASK_NOT_SUPPORTED不支持该任务类型,或其设置已在 CapSkip 中关闭。
ERROR_PROXY_MISSING该任务需要代理,但请求中没有可用的代理。
ERROR_WRONG_USERAGENT请求中的 User-Agent 无法用于此任务。
ERROR_PROXY_BANNED目标网站封禁了该代理地址。请更换代理。
ERROR_PROXY_CONNECT_REFUSED无法通过该代理访问目标。
ERROR_RECAPTCHA_INVALID_SITEKEY加载的 sitekey 缺失,或 CAPTCHA 服务拒绝在此页面使用它。
ERROR_RECAPTCHA_INVALID_SITEKEYCAPTCHA 提供方无法识别该 sitekey。请检查您发送的 sitekey 。
ERROR_RECAPTCHA_INVALID_DOMAIN该 sitekey 不允许在以下域名上使用: pageurl。请发送小组件实际所在页面的地址。
CAPCHA_NOT_READY验证码仍在处理中。请继续轮询。
(空响应)结果已被读取,或该 ID 不存在。