如何用 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 带有 captchaId 和 code (适用于每种类型), 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 验证码识别 页面介绍了浏览器自动化方面的内容。

小结

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

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