如何在 n8n 中用 HTTP Request 节点识别验证码

n8n 并没有专门的验证码节点,你也不需要它。两个 HTTP Request 节点加一个 Wait 节点就能识别任何受支持的验证码类型:第一个提交任务,第二个轮询直到 token 返回。把两者都指向 127.0.0.1:8080 而整个流程都运行在与 n8n 相同的机器上,因此没有任何数据离开你的网络,也不存在按次计费。CapSkip 同样可以跑在服务器上,通过网络接收完全相同的 API 调用,这正是从 n8n Cloud 使用它的方式。本指南会逐个节点搭建这条工作流,其中也包括大多数人第一次运行时都会踩的 Docker 网络坑。
你需要什么
- n8n,自托管版、桌面版或云端版均可。三者都能用,只是云端版需要 CapSkip 能通过网络访问。参见下文的服务器一节。
- CapSkip 应用已在运行,且服务已启动。端口、密钥以及 Local 或 Server 模式,请见 连接设置.
- 你要自动化的那个站点的 sitekey 和页面 URL。
key 校验默认是关闭的,因此任何非空字符串都可以作为 key 参数。但一定要传值,不要留空:空的 key 会返回 ERROR_WRONG_USER_KEY.
为什么用 HTTP Request 而不是社区节点
社区节点必须安装到实例上,跟不上 API 的变化,而在 n8n Cloud 上还受到限制。这里的 API 兼容 2captcha,而且只有两个端点,社区节点无非是把六行左右的配置包装一下。内置的 HTTP Request 节点做的是同一件事,会随 n8n 一起升级,并且在任何类型的实例上都能用。
| 端点 | 作用 | 返回 |
|---|---|---|
/in.php | 提交任务 | 一个数字形式的验证码 ID |
/res.php | 查询该 ID 是否已完成 | token,或者 CAPCHA_NOT_READY 字符串 |
第 1 步:提交验证码
添加一个 HTTP Request 节点,命名为 Submit CAPTCHA。把方法设为 POST,URL 设为 http://127.0.0.1:8080/in.php。打开 Send Body,选择 Form Urlencoded,然后添加以下字段。
| 姓名 | 值 |
|---|---|
| key | 任意非空字符串 |
| method | userrecaptcha |
| googlekey | 你的 sitekey |
| pageurl | 小组件所在的页面 |
| json | 1 |
务必发送 json 字段。不带它时,响应只是一个类似 OK|2122988149 这样的裸字符串,还得你自己手动切分。带上它,你拿到的是 n8n 可以直接引用的对象。
// Response from in.php with json=1
{"status": 1, "request": "2122988149"}
// The captcha ID is now available downstream as:
// {{ $json.request }}其他验证码类型只需改动 method 字段以及它旁边的参数。Turnstile 使用 turnstile,配合 sitekey 和 pageurl;极验(GeeTest)使用 geetest,配合 gt 和 challenge;图片验证码使用 base64,把图片字节放在 body 字段里。 API 参考 列出了每种类型的全部参数。
第 2 步:首次轮询前先等待
在 Submit CAPTCHA 之后添加一个 Wait 节点。把 Resume 设为 After Time Interval,Wait Amount 设为 15,Wait Unit 设为 seconds。
15 秒不是随便定的。reCAPTCHA v2 任务很少能更快就绪,所以立刻轮询只会白白消耗一次工作流执行,换回一个必然的 CAPCHA_NOT_READY。Turnstile 和极验完成得更快,这两种 5 秒就够了。图片验证码通常一秒左右就能完成。
第 3 步:轮询获取 token
再添加第二个 HTTP Request 节点,命名为 Poll Result。方法为 GET,URL 为 http://127.0.0.1:8080/res.php,并打开 Send Query Parameters。
| 姓名 | 值 |
|---|---|
| key | 与第 1 步中使用的字符串相同 |
| action | get |
| id | {{ $('Submit CAPTCHA').item.json.request }} |
| json | 1 |
请按名称引用提交节点,而不要使用 $json.request。一旦 Wait 节点夹在两者之间,传入的数据项就属于 Wait 节点;而在循环的第二轮里,它又属于 IF 节点。显式指定来源节点,可以让这个 ID 无论循环多少次都保持稳定。
第 4 步:循环直到结果就绪
在 Poll Result 之后添加一个 IF 节点。条件是一次字符串比较:左值为 {{ $json.request }},运算符为 “is not equal to”,右值为 CAPCHA_NOT_READY.
把 false 输出连回 Wait 节点。这样就闭合了循环,n8n 会一直转下去,直到结果返回。true 输出则把 token 继续向下传递。
// res.php while the job is still running
{"status": 0, "request": "CAPCHA_NOT_READY"}
// res.php once it is solved
{"status": 1, "request": "03AGdBq26..."}注意拼写。API 返回的是 CAPCHA_NOT_READY,少了第一个 T,这是从它所兼容的 2captcha 传输格式沿袭下来的。如果你按看起来正确的拼法输入,IF 节点将永远匹配不上,循环会一直跑到工作流超时为止。
结果只能读取一次。对同一个 ID 读第二次会返回错误,而不是再给你一次 token,所以请把 token 直接送入下一个节点,不要先轮询一次做检查、再轮询一次取值。
第 5 步:使用 token
IF 节点的 true 分支现在带着 token。把它作为 g-recaptcha-response 表单字段,提交到之前被拦下的那个请求上,这一步用第三个 HTTP Request 节点完成。
// Code node, Mode: Run Once for All Items.
// Builds the form payload for the final request.
const token = $input.first().json.request;
return [
{
json: {
email: "[email protected]",
"g-recaptcha-response": token,
},
},
];token 在签发后大约两分钟就会过期,所以要立即提交。如果你的工作流在识别和提交之间还有审批步骤或另一个 Wait 节点,请把识别环节挪到它之后。
Docker 陷阱:localhost 并不是你的机器
这是最常见的一种失败,而且错误信息毫无指向价值。在容器内部, 127.0.0.1 指的是容器自身,而不是运行识别工具的宿主机。n8n 会报连接被拒绝,工作流在 Submit CAPTCHA 处中断。
在 Mac 和 Windows 版 Docker Desktop 上,把主机名换成 host.docker.internal ,两个 URL 里都要换。在 Linux 上这个名字默认无法解析,需要在启动容器时显式添加。
# docker pull docker.n8n.io/n8nio/n8n # Linux: map host.docker.internal to the host gateway. docker run -it --rm \ --add-host=host.docker.internal:host-gateway \ -p 5678:5678 \ -v n8n_data:/home/node/.n8n \ docker.n8n.io/n8nio/n8n # Both node URLs then become: # http://host.docker.internal:8080/in.php # http://host.docker.internal:8080/res.php
如果 n8n 和识别工具运行在同一个 Docker 网络中,就改用服务名。无论哪种方式,规则都一样:这个 URL 必须能从容器内部解析,而不是从你的终端解析。
改为在服务器上运行 CapSkip
CapSkip 不必和 n8n 待在同一台机器上。 连接设置 提供两种模式,其中第二种正是让它能配合托管版 n8n 使用的关键。
| 模式 | 监听地址 | 适用场景 |
|---|---|---|
| 本地 | 127.0.0.1,仅限该设备 | n8n 和 CapSkip 运行在同一台电脑上 |
| 服务器 | 你的内网或公网 IP | n8n 运行在别处:另一台机器、一台 VPS,或者 n8n Cloud |
把 CapSkip 以 Server 模式放到一台 VPS 上,团队里的每台机器都指向同一个实例。节点里的 URL 就变成 http://YOUR_SERVER_IP:8080/in.php 和 http://YOUR_SERVER_IP:8080/res.php,工作流里的其他部分一概不用改。最好准备一个静态公网 IP,因为这些 URL 是写死在节点里的,地址一变就会失效。
这依然是你自己的硬件,依然不按量计费。Server 模式改变的只是识别工具运行的位置,而不是它归谁所有,所以无论用哪种模式,你都不会按次付费。
常见错误及其含义
| 响应 | 原因 | 修复 |
|---|---|---|
ECONNREFUSED | 识别工具没有运行,或者容器看不到宿主机 | 启动本地服务,然后套用上面的 Docker 解决办法 |
ERROR_WRONG_USER_KEY | key 字段为空或缺失 | 发送任意非空字符串 |
ERROR_GOOGLEKEY | sitekey 错误、被截断,或者来自另一个页面 | 重新从线上页面读取 data-sitekey 属性 |
ERROR_PAGEURL | pageurl 字段缺少协议头,或者指向了别处 | 发送包含 https 在内的完整 URL |
| 循环永远退不出来 | IF 节点比对的就绪字符串拼写有误 | 要与 CAPCHA_NOT_READY 的拼写完全一致 |
ERROR_CAPTCHA_UNSOLVABLE | 任务是失败了,而不是超时 | 用新的 challenge 值重新提交 |
API 可能返回的每一个错误码都列在 API 文档,其中标注了导致每种错误的参数。
常见问题
这在 n8n Cloud 上能用吗?
可以,只要把 CapSkip 设为 Server 模式。云端执行节点运行在 n8n 的基础设施中,因此看不到你桌面上的 127.0.0.1 这个地址,而它是 Local 模式唯一允许的地址。把连接设置切换到 Server 模式,在一台拥有静态公网 IP 的 VPS 上运行 CapSkip,再把各个节点指向该地址。如果你更愿意把所有东西都放在一台机器上,那就在同一台机器上自托管 n8n,继续使用 Local 模式。
轮询循环会消耗多少次执行?
一次。工作流内部的循环无论转多少圈,仍然只算一次执行,所以 Wait 和 IF 这一对不会让你的执行次数翻倍。但它确实会让这次执行一直处于打开状态,如果你在小实例上并发运行很多工作流,这一点就值得注意。
我可以在一次运行中识别多个验证码吗?
可以。HTTP Request 节点会对每个输入项各运行一次,所以给它一组 sitekey 与 URL 的配对列表,就会把它们全部提交,并为每一项返回一个 captcha ID。在整个循环中让这些 ID 与各自的来源项保持配对;如果想限制同时在途的数量,就加一个 Split In Batches 节点。
我需要代理吗?
通常不需要。只有当目标站点拒绝那些在与加载页面不同的网络上识别出的 token 时,才给提交节点加上 proxy 和 proxytype 字段。代理适用于 reCAPTCHA、Turnstile 和极验,对图片验证码则会被忽略。
把它接入真实的工作流
上面这五个节点可以放进任何需要访问受保护表单的工作流:抓取销售线索的爬虫、每晚一次的价格检查、需要登录老旧门户的内部工具。由于识别工具是一个 验证码识别工具 ,运行在你自己的硬件上,你喂给它的 sitekey 和页面 URL 永远不会离开这台机器,而且循环本身每次运行都不产生任何费用。如果你更想在 Code 节点里用 JavaScript 或 Python 调用,而不是用 HTTP Request 节点,那么 官方 SDK 封装的正是同样这两个端点;而 reCAPTCHA v2 的具体细节 则另有专文介绍。
