如何在 Retool Workflows 中识别验证码(REST 块)

在 Retool Workflows 里识别一个验证码就是三个块:提交、等待、读取。把它们做成 REST resource query block,而不是 JavaScript 或 Python 的 code block,因为 Retool 会在一个单独的沙箱服务里运行 code block,而那个服务的默认防火墙规则会拒绝私有地址。resource query 是配置而不是自定义代码,所以它不经过那个服务,能直接访问你自己网络里的识别工具,完全省掉了那些争论。
你需要什么
- 一个启用了 Workflows 的 Retool 组织,Retool Cloud 或自托管都行。两者都能用,只是网络配置不同。
- CapSkip 跑在一台 Windows 机器上。如果那台机器同时也跑着自托管的 Retool,就用 Local mode(本地模式);其他情况一律用 Server mode(服务器模式)。
- 你要识别的那个挑战的 sitekey 和页面 URL。
- 一个放 API 密钥的地方。Retool secrets 在 code block 里和资源配置里都能读到,所以不需要把密钥粘贴进任何一个块。
CapSkip 在 8080 端口上提供 2captcha 兼容 API,所以 Retool 既不需要连接器,也不需要自定义集成。它就是一个指向你自己那台机器的普通 REST 资源。
第 1 步:把一个 REST 资源指向识别工具
创建一个 REST API 资源,base URL 填运行 CapSkip 的那台机器。认证留空。API 密钥作为普通参数随每个请求一起发送,2captcha 兼容协议就是这么工作的。
# Base URL for the resource. Loopback only works when Retool # is self-hosted on the same Windows box as the solver. http://127.0.0.1:8080 # Server mode, which is what you want everywhere else. http://192.168.1.40:8080
之后每一个需要识别验证码的工作流都复用这同一个资源。两个查询块就足以完成整件事。
第 2 步:提交挑战
添加一个 resource query block,命名为 submitCaptcha,把 action type 设为 POST,path 设为提交端点。请求体是一个很小的 JSON 对象。
{
"key": "YOUR_KEY",
"method": "userrecaptcha",
"googlekey": "YOUR_SITEKEY",
"pageurl": "https://example.com/page-with-recaptcha",
"json": 1
}响应里带着你后面用来轮询的 id。
{ "status": 1, "request": "2122988149" }那个请求体是 reCAPTCHA v2 的。CapSkip 支持的其他类型是同一个调用换不同参数:加上 invisible 或 enterprise 设为 1,或者把 version 设为 v3 并给一个 action 名,或者把 method 换成 turnstile 或 geetest。完整的参数列表见 CapSkip API 文档.
一个 resource query block 会向下游的所有块交出三个属性。响应体放在 data 里,失败信息放在 error 里,其余的放在 metadata 里。所以你刚拿到的那个 id,在下一个块里就是 submitCaptcha.data.request。
第 3 步:等待,然后把 token 读一次
添加一个 Wait block。对 reCAPTCHA v2 复选框来说,十五秒是个合理的首次等待时长。图片验证码大约一秒就回来,v3 是十到十五秒,极验(GeeTest)大约五秒。Wait block 接受一个数字或一个 JavaScript 表达式,单位可以配成秒、分钟、小时或天,上限是六十天,而且它只暂停直接位于它下游的那些块。
然后再加第二个 resource query block,命名为 readResult,方法设为 GET。
# GET, with the id from step 2 in the query string.
/res.php?key=YOUR_KEY&action=get&id={{ submitCaptcha.data.request }}&json=1可能的答复只有两种。已经就绪的结果,status 是 1,token 在 request 字段里。还在识别中的结果,status 是 0,request 字段里是字符串 CAPCHA_NOT_READY,拼写里没有那个 T,它的意思是继续等,而不是出了什么问题。这个拼写的来龙去脉见 一篇关于 CAPCHA_NOT_READY 响应的完整说明.
用一个 Branch block 处理这两种情况。条件就是针对上游块写的普通 JavaScript,所以判断写成 readResult.data.status === 1。If 分支把 token 往下传。Else 分支接一个二十秒的 Wait block 和第二次读取。
别想着用 Loop block 去替代它。有两个理由,真正致命的是第二个。Loop block 的默认超时是十秒,上限是两分钟,远低于 CapSkip 给一次 reCAPTCHA 识别的三百秒,所以循环本来也覆盖不了那条慢尾巴。更重要的是,CapSkip 的结果只能读一次。一个循环去重复读它已经取过的 id,第二次是拿不到 token 的。
第 4 步:决定一切的那条网络规则
这是 Retool 特有的部分,也是本指南为什么要用 resource query block 来搭这套识别流程的原因。
Retool 会在一个单独的 code executor 服务里运行你的 JavaScript 和 Python,用 NsJail 做沙箱。在自托管部署里,那个服务自带的 iptables 规则覆盖了链路本地地址和整个 192.168.0.0/16,而 Retool 文档里只给出一个把它们关掉的开关,DISABLE_IPTABLES_SECURITY_CONFIGURATION。Retool 也明确说过,它建议以特权方式运行 code executor,好让自定义代码始终被沙箱隔离。所以,让一个 code block 去访问 192.168.1.40 上的识别工具,等于要求你为整个实例削弱沙箱。而 resource query 不是自定义代码,也不在那里运行。
除此之外,识别工具本身还得能被访问到。CapSkip 为此提供两种连接模式。Local mode 绑定 127.0.0.1,只服务本机。Server mode 绑定你的内网地址或公网 IP,于是另一台机器、一个容器宿主机或者一个托管平台,都能通过 API 访问同一台 Windows 机器。两种模式都说明在 连接设置,而 Server 模式只改变识别程序监听在哪个地址上。硬件还是你自己的,识别也依然不计量。
| Retool 跑在哪里 | 用哪种模式,还要做什么 |
|---|---|
| 自托管在与 CapSkip 相同的那台 Windows 机器上 | Local mode,base URL 保持在回环地址上 |
| 自托管在另一台机器上,或在你网络里的 Docker 中 | Server mode,填识别工具的内网地址。用 resource query block,不要用 code block |
| Retool Cloud | Server mode,配一个固定公网 IP,再加一条针对 Retool 出站地址的入站防火墙规则 |
Retool Cloud 从一组固定的、已公布的地址来调用你的资源,文档里写明云端实例必须确保所配置的资源允许来自这些地址的访问。默认区域是 AWS us-west-2。
# Retool Cloud outbound ranges, us-west-2, the default region. 35.90.103.132/30 44.208.168.68/30 # eu-central-1 3.77.79.248/30
在 8080 端口前面的防火墙上放行这些地址,其余一律拒绝。这个开口比看上去小得多,而这就是 Retool Cloud 这条路的全部内容。
第 5 步:决定一次慢识别能否活下来的那些超时
Retool 为不同类型的块公布了不同的上限,而它们在这里很重要,因为识别天生就慢。
| 哪个限制 | 值 | 它为什么会影响一次识别 |
|---|---|---|
| resource query block,异步运行 | 最长 10 分钟 | 很宽裕。单次读取远不到一秒就返回 |
| resource query block,同步运行 | 最长 2 分钟 | 对读取来说仍然够用,因为等待是在 Wait block 里完成的 |
| Loop block | 默认 10 秒,最多 2 分钟 | 这正是轮询循环在这里形态不对的原因 |
| 整次运行,异步 | 30 小时,用上 Wait block 则不受限制 | 识别怎么都够不着这个数 |
| 整次运行,同步 | 到第一个 webhook Response block 为止 15 分钟 | 陷阱就在这里。见下面一段 |
| 每个工作流的并发外部请求数 | 同时 50 个 | 这才是一次运行里批量识别的真实上限 |
| 定时触发间隔 | 最短一分钟 | 够用,而且跑得这么勤也不花钱 |
要围着同步那个数字来规划。一个保持连接打开、用 Response block 应答的 webhook 触发器给你十五分钟,听起来很宽裕,直到你想起来:让调用方挂在一条打开的 HTTP 连接上两分钟去等一个 reCAPTCHA,本身就是个糟糕的设计。要么异步触发工作流,让它把 token 投递到需要的地方,要么立刻应答 webhook,把识别放到后面去做。
resource query block 自己也带重试次数和指数退避的设置。给 submitCaptcha 打开它们,给 readResult 关掉,理由就是上面说的只能读一次。
如果你更愿意写代码
在自托管实例上,只要 code executor 能访问到识别工具,整个流程就能收缩成一个 Python block,因为 SDK 会替你轮询。先在 Libraries 标签页里把 capskip 加进这个工作流的 requirements.txt。
# pip install capskip - add it in the Libraries tab instead.
from capskip import CapSkip
# host is the solver machine. Keep 127.0.0.1 only when Retool
# runs on the same Windows box as CapSkip.
solver = CapSkip(host="192.168.1.40", port=8080)
result = solver.recaptcha(
sitekey="YOUR_SITEKEY",
url="https://example.com/page-with-recaptcha",
)
# Python blocks serialize their output as JSON, so return
# the token rather than the client object.
{"token": result["code"]}SDK 从 250 毫秒开始轮询,逐步退避到五秒,而不是按固定间隔去睡,所以这个版本通常比基于 Wait 的流程更早返回。它对 reCAPTCHA、Turnstile 和极验(GeeTest)的上限是三百秒,落在 code block 异步十分钟的超时之内。Retool Cloud 默认跑 Python 3.10,Node.js、PHP 和 C# 的等效单次调用版本见 验证码识别 SDK 页面.
要在紧接着的那个块里提交 token。reCAPTCHA token 的有效期大约两分钟,所以一个先识别、再停在一个长 Wait block 上、最后才提交的工作流,会栽在一个生成时明明有效的 token 上。这种失败模式值得读一读指南 reCAPTCHA token 过期.
常见错误及其含义
| 你所看到的 | 原因 | 修复 |
|---|---|---|
| code block 访问内网地址时超时 | 自托管 code executor 的默认 iptables 规则覆盖了 192.168.0.0/16 | 把这个调用挪进一个 REST resource query block |
| 8080 端口连接被拒绝 | CapSkip 绑定在回环地址上,而 Retool 在别的地方 | 切换到 Server 模式,并使用识别程序的网络地址 |
| Retool Cloud 根本访问不到这个资源 | 防火墙没有放行 Retool 的出站地址 | 在 8080 端口上放行你所在区域已公布的地址段 |
| readResult 每次都返回 CAPCHA_NOT_READY | Wait block 比识别实际花的时间短 | 把首次等待调长,或者在 Else 分支上再加一次等待和一次读取 |
| 对同一个 id 的第二次读取返回空 | CapSkip 的结果只能读取一次 | 把 token 留在块的输出里,绝不要再去读那个 id |
| 响应里出现 ERROR_WRONG_USER_KEY | key 参数解析成了空字符串 | 检查 secret 的名字,包括大小写是否完全一致 |
| 有效的 token 被目标站点拒绝 | 它在识别和提交之间过期了 | 在下一个块里提交,中间不要放 Wait block |
| 一批识别做到一半卡住了 | 一个工作流最多只能有 50 个外部请求在途 | 给 Loop block 分批,或者把工作拆到多次运行里 |
常见问题
我能从 Retool Cloud 用 CapSkip 吗?
能,用 Server mode。Retool Cloud 是从它自己的基础设施来调用你的资源的,所以识别工具必须监听在一个它们能访问到的地址上:一个公网 IP,最好是固定的。Retool 公布了它发起调用的出站地址段,所以防火墙规则可以很窄,而不是对全世界敞开。识别工具本身没有任何变化,也不会变成计次的。唯一的区别就是它监听的那个地址。
为什么不干脆用一个带 axios 的 JavaScript 块?
因为那段代码运行的位置。Retool 在一个用 NsJail 做沙箱的单独服务里执行 code block,而在自托管部署里,那个服务会装上覆盖私有地址段的防火墙规则。把它们关掉确实是一个文档里写明的开关,但那是为了让一个工作流省事而做出的、影响整个实例的决定,而且 Retool 建议不要这么做。resource query block 访问的是同一个识别工具,却完全不用面对这些问题。用 code block 写逻辑,用 resource query block 走网络。
工作流应该一直循环到 token 到达吗?
不该。Loop block 的上限是两分钟,远远不够一次 reCAPTCHA 识别被允许的三百秒,而且它会把你给它的每一轮都跑完,不会提前停下。除此之外,一个结果只能读一次,所以对同一个 id 反复读取都是白跑一趟。长度合适的一个 Wait block 加上一次读取,既正确又便宜,后面再挂一个 Branch block,里面放第二次等待和读取,就覆盖了那些慢的情况。
这和 n8n 或 Pipedream 比起来怎么样?
这两个请求在三个工具里完全一样。不一样的是每个工具摆在它们前面的那道障碍。
- 在 n8n 里,障碍是容器网络,详细过程见 n8n 工作流指南.
- 在 Pipedream 里,障碍是步骤运行时和 secrets 放在哪里,相关内容见 Pipedream 指南.
- 在 Retool 里,障碍是 code executor 自带的防火墙,这也是上面那套流程始终不把 HTTP 调用放进 code block 的原因。
简短版结论
把一个 REST 资源指向识别工具,POST 提交挑战,Wait 十五秒,GET 取结果,再用 Branch 判断它是否就绪。把 HTTP 留在 resource query block 里而不是 code block 里,因为 code executor 的默认防火墙规则覆盖了私有地址,而放松它们是一个影响整个实例的决定。只要 Retool 不在识别工具本机上,就把 CapSkip 切到 Server mode;在 Retool Cloud 上,只放行已公布的出站地址段访问 8080 端口。绝不要在同步 webhook 里等一次识别。
- reCAPTCHA v2 复选框本身的说明见 reCAPTCHA v2 识别页面.
- code block 那条路用到的 Python 客户端,文档见 Python 验证码识别页面.
在给它挂上每分钟一次的定时工作流之前,有一点值得掂量:CapSkip 是一款 本地验证码识别工具 ,跑在你已经拥有的硬件上,所以每分钟触发一次的工作流和一天触发两次的工作流,成本完全相同。
