如何用 Node.js 识别 reCAPTCHA v2(含隐形版)

solve recaptcha v2 in node.js - How to Solve reCAPTCHA v2 in Node.js, Including Invisible

reCAPTCHA v2 有三种变体,它们都对应同一个 Node.js 方法。复选框是最简单的调用,隐形和 Enterprise 是第三个参数中的选项,两者可以组合。该包还自带 TypeScript 定义,因此这一切都不需要单独安装 @types 安装。

设置

npm install capskip

CapSkip 在你自己的机器上识别,因此必须先运行桌面应用。然后将客户端指向其设置中显示的端口:

const { CapSkip } = require('capskip');

const solver = new CapSkip({
  apiKey: 'capskip',        // any string when key validation is off
  host: '127.0.0.1',
  port: 8080,
  recaptchaTimeout: 300,    // seconds
});

在生产环境中,从环境变量读取连接信息:

const solver = new CapSkip({
  apiKey: process.env.CAPSKIP_API_KEY || 'capskip',
  host: process.env.CAPSKIP_HOST || '127.0.0.1',
  port: parseInt(process.env.CAPSKIP_PORT || '8080', 10),
});

三种变体

变体需添加的选项
复选框
隐形{ invisible: 1 }
Enterprise{ enterprise: 1 }
隐形 Enterprise两个键都加
// Checkbox: sitekey and page URL, nothing else.
const result = await solver.recaptcha(
  '6Lc...YOUR_SITEKEY',
  'https://example.com/login',
);

console.log(result.code);   // g-recaptcha-response token

// Invisible.
await solver.recaptcha(sitekey, pageUrl, { invisible: 1 });

// Enterprise, and both at once.
await solver.recaptcha(sitekey, pageUrl, { enterprise: 1 });
await solver.recaptcha(sitekey, pageUrl, { enterprise: 1, invisible: 1 });

注意参数的写法。Node 按位置传入 sitekey 和 URL,这与使用关键字的 Python SDK 不同。在两者之间移植代码时最容易出错。

TypeScript

类型定义随包一起提供,因此无需额外安装即可使用:

import { CapSkip, SolveResult } from 'capskip';

const solver = new CapSkip({ host: '127.0.0.1', port: 8080 });
const result: SolveResult = await solver.recaptcha(sitekey, pageUrl);

SolveResult 带有 captchaIdcode (适用于每种类型), userAgent 用于 Turnstile 的字段,以及三个 极验(GeeTest)字段。对于 reCAPTCHA,只有 code 才重要。

正确获取 sitekey

它是 data-sitekey 小组件容器上的属性,或者是传给 grecaptcha.render 的第一个参数——在隐形模式下没有可见容器时。它始终以 6L 开头,且是公开的。

URL 必须是小组件实际渲染所在的页面。传入表单处理程序或登录后的重定向,是 token 识别顺利却随后验证失败的常见原因。

提交 token

const body = new URLSearchParams({
  'g-recaptcha-response': result.code,
  username: '...',
  password: '...',
});

const response = await fetch('https://example.com/login', {
  method: 'POST',
  body,
});

token 只能使用一次,有效期约两分钟,因此请尽可能晚地识别。如果网站把 token 传给 JavaScript 回调而不是表单字段,识别方式相同但提交方式不同,我们的 reCAPTCHA v2 回调识别工具 页面有说明。

同时识别多个

const [a, b] = await Promise.all([
  solver.recaptcha(sitekeyA, 'https://a.example.com'),
  solver.recaptcha(sitekeyB, 'https://b.example.com'),
]);

console.log(a.code, b.code);

该包还导出了 AsyncCapSkip,但在 Node 中它只是 CapSkip的别名。Node 的 I/O 本就是异步的,每个方法都已返回 Promise,因此它的存在纯粹是为了让从 Python SDK 移植的代码继续工作。切换到它没有任何好处。

错误

const {
  ValidationException, NetworkException, ApiException, TimeoutException,
} = require('capskip');

try {
  const result = await solver.recaptcha(sitekey, pageUrl);
} catch (err) {
  if (err instanceof ValidationException)      { /* bad arguments */ }
  else if (err instanceof NetworkException)    { /* CapSkip not running */ }
  else if (err instanceof ApiException)        { /* bad sitekey or url */ }
  else if (err instanceof TimeoutException)    { /* polling timed out */ }
  else throw err;
}

这四个都继承自同一个基类,因此捕获 CapSkipError 即可在一个分支中处理全部情况,如果你更喜欢这样。

常见问题

我需要轮询结果吗?

不需要。客户端会在内部轮询,Promise 会在 token 完成时被解析。它在 250 毫秒后开始检查,并逐渐退避到 pollingInterval,这通常比手写的循环更快。

它支持 ESM 和 import 语法吗?

可以。这里的示例使用 require 以求简洁,但 import { CapSkip } from 'capskip' 同样可用,随附的 TypeScript 定义也会一起提供。

我可以将它与 Puppeteer 或 Playwright 一起使用吗?

可以。用此 SDK 识别出 token,然后用 page.evaluate 在提交前将其注入页面。我们的 Node.js 验证码识别 页面介绍了浏览器自动化方面的内容。

小结

一个方法、三种变体,通过 invisibleenterprise选择。参数是按位置传递的,token 会落在 result.code,以及 Promise.all 就是并发所需的全部,因为 AsyncCapSkip 在这里只是一个别名。

更完整的 Node 接口见 Node.js 验证码识别 页面,其他语言见 reCAPTCHA v2 识别 页面,还有一个 在线 v2 演示 可供测试。CapSkip 采用 本地验证码识别工具,因此不会按次识别收费。