如何在 Zapier 中识别验证码而不撞上超时

zapier captcha - How to Solve CAPTCHA in Zapier Without Hitting the Timeout

Zapier 里的验证码步骤失败,原因和验证码本身没什么关系:一个 Code 步骤默认只有 10 秒或 30 秒,而一次识别要更久。把提交和轮询塞进同一个步骤,你就会超时。行得通的结构是两个步骤中间夹一个延迟,再加上一个 Zapier 服务器能访问到的识别工具,也就是要用 Server 模式而不是回环地址。这两件事一旦知道问题出在哪,各花五分钟就能改完。

你需要什么

  • 一个 Zapier 账号,套餐只要包含 Code by Zapier 即可。
  • CapSkip 以 Server 模式运行在一台 Zapier 能访问到的机器上,并配一个固定公网 IP。参见 连接设置.
  • 打开密钥校验,并为这个 Zap 单独签发一个 API key。
  • 受保护表单的 sitekey 和页面 URL,由提供它们的那个触发步骤给出。

这里 Server 模式不是可选项

本博客其他所有教程都是把识别工具起在 127.0.0.1:8080,在你自己的机器上这么做完全正确。可 Zapier 不在你的机器上。你的 Zap 跑在 Zapier 的基础设施里,所以一个发往 127.0.0.1 的请求,从 Code 步骤发出去只会解析到 Zapier 自己的容器,什么都拿不到。

CapSkip 的连接设置里就有为此准备的第二种模式。 本地 模式绑定到 127.0.0.1,只有该设备本身能访问。 服务器 模式绑定到你的内网或公网 IP,这样托管平台就能通过同一套兼容 2captcha 的 API 调用它。建议配一个固定公网 IP,因为这个地址要写进你的 Zap 里,你不会希望它变来变去。

硬件还是你自己的,也依然不按量计费。Server 模式改的是识别工具监听在哪里,不改它归谁所有,也不改它怎么计费。它确实改变的是暴露面,所以在开放端口之前先打开密钥校验,并给这个 Zap 发一个专属密钥,这样吊销它不会打扰到别的东西。

决定你的 Zap 结构的那个超时

Code by Zapier 运行你的 Python 或 JavaScript 时有一个运行时长上限。Starter 上这个上限是 10 秒。Pro、Team 和 Company 上是 30 秒。Zapier 还提供扩展运行时长,能把一个 Code 步骤提高到最多 10 分钟,在步骤自身上配置。

再拿它跟一次识别比一比。图片验证码通常是几秒。reCAPTCHA v2 一般在 15 到 45 秒之间给出答案,而轮询的建议是第一次检查前先等大约 15 到 20 秒。所以「先提交再轮询」的循环塞不进 10 秒,通常也塞不进 30 秒,放进扩展运行时长里则绰绰有余。

于是你有两种设计,选哪一种取决于有没有开启扩展运行时长:

  • 两个步骤加一个延迟。 所有套餐都能用。提交,等待,轮询。Zap 历史记录读起来清楚,一次慢识别也不会把整次运行搞死。
  • 一个步骤配扩展运行时长。 活动部件更少,整个识别过程都在一个地方。需要在那个步骤上开启更长的运行时长。

第 1 步:提交验证码并留住 ID

添加一个 Code by Zapier 动作,选择 Python,把 sitekey 和页面 URL 映射到该步骤的输入字段里。输入值在 input_data里永远是字符串,这里没关系,反正你要发的东西本来就都是字符串。

# Code by Zapier, Python. requests is available; nothing to install.
SOLVER = "http://YOUR_SERVER_IP:8080"

r = requests.post(SOLVER + "/in.php", data={
    "key": input_data["api_key"],
    "method": "userrecaptcha",
    "googlekey": input_data["sitekey"],
    "pageurl": input_data["pageurl"],
    "json": 1,
})
body = r.json()

if body["status"] != 1:
    raise Exception("Submit rejected: " + body["request"])

# Hand the id to the next step.
output = {"captcha_id": body["request"]}

提交被拒时,抛异常是正确做法。Zapier 会把这次运行标记为出错,并在 Zap 历史记录里显示消息,这比把一个错误字符串往后传、三步之后才发现要强。

第 2 步:先延迟,再轮询取 token

添加一个 Delay by Zapier 步骤,用 Delay For 设一个较短的间隔。对 reCAPTCHA v2 来说 20 秒是个合理的下限,因为比这更早去轮询只会返回 CAPCHA_NOT_READY 并把 Code 步骤的时间预算耗在本来就不可能成功的请求上。

然后再加一个负责轮询的 Code 步骤。循环要小:几次尝试、中间夹上 sleep,正好能塞进 30 秒;如果到那时答案还没准备好,Zap 就报错,你可以打开 Autoreplay,而不是干等在一个更长的循环里。

# Code by Zapier, Python. Second step, after Delay For 20 seconds.
import time

SOLVER = "http://YOUR_SERVER_IP:8080"
params = {
    "key": input_data["api_key"],
    "action": "get",
    "id": input_data["captcha_id"],
    "json": 1,
}

token = None
# Five tries at 5s stays inside a 30 second step.
for _ in range(5):
    body = requests.get(SOLVER + "/res.php", params=params).json()
    if body["status"] == 1:
        token = body["request"]
        break
    if body["request"] != "CAPCHA_NOT_READY":
        raise Exception(body["request"])
    time.sleep(5)

if not token:
    raise Exception("Not solved yet. Lengthen the delay ahead of this step.")

output = {"token": token}

CAPCHA_NOT_READY 拼写里没有 T,这是 API 本身的拼法,不是本文的笔误。你拿回来的其他任何响应都是真的错误,应该让 Zap 停下来。完整列表见 CapSkip API 文档.

单步骤版本

在步骤上启用扩展运行时长之后,两半就合并成一个动作,Delay 也随之消失。代码就是上面那两段,只是把轮询循环放宽;唯一要盯住的是循环自身的上限要低于你配置的运行时长,这样步骤抛出的是你自己写的消息,而不是在请求中途被杀掉。

有一点要提醒,别把这个窗口开得太大:识别出来的 token 自己也有寿命。reCAPTCHA 的 token 是一次性的,大约两分钟后就不再被接受,所以一个识别完之后又在队列里坐了五分钟的 Zap,提交上去的东西站点早就不认了。 Token 有效期 一文给出了准确的数字。在 Zap 里尽可能晚地做识别,并把用到 token 的那个步骤紧挨着放在它后面。

改用 Webhooks by Zapier

提交这一步你可以用 Webhooks by Zapier 来做,一行代码都不用写。选 Custom Request 或 POST,把它指向 /in.php,并以表单数据的形式发送同样的字段。响应会以 {"status": 1, "request": "<id>"} 这样的形式返回,Zapier 会把它解析成你可以往后映射的字段。

{
  "key": "YOUR_API_KEY",
  "method": "userrecaptcha",
  "googlekey": "YOUR_SITEKEY",
  "pageurl": "https://example.com/page-with-recaptcha",
  "json": 1
}

别扭的地方在轮询。一个 webhook 只发一次请求,所以你还得靠 Delay 和 Filter 来判断答案有没有回来,而 Zap 又不太容易绕回去重试。这一半用 Code 步骤读起来更顺。如果你更喜欢点鼠标而不是敲键盘,提交就用 Webhooks;轮询无论如何都用 Code。

常见错误及其含义

你所看到的原因修复
你的代码超时了在 10 秒或 30 秒的上限里把提交和轮询放进了同一个步骤拆成两个步骤中间加 Delay,或者启用扩展运行时长
连接被拒绝,或者请求一直挂着步骤调用的是 127.0.0.1,那是 Zapier 自己的容器把识别工具切到 Server 模式,改用它的公网 IP
ERROR_KEY_DOES_NOT_EXIST密钥校验已打开,而这个 Zap 的密钥没有登记在应用设置里为这个 Zap 签发一个密钥
每次尝试都返回 CAPCHA_NOT_READY轮询开始得太早,或者循环太短把轮询步骤前面的 Delay 拉长
ERROR_GOOGLEKEY从触发步骤传过来的 sitekey 字段是空的检查该步骤输入字段里的映射
token 被目标站点拒绝它在识别完成到被使用之间过期了把识别挪到更靠后,紧挨着提交它的那个步骤

常见问题

我能在 Code 步骤里安装 CapSkip SDK 吗?

不能。Code by Zapier 跑在一个库固定、不能装包的沙箱里,所以 Python 和 Node SDK 都用不了。它内置了 requests 库,而这套 API 兼容 2captcha,只有两个端点,所以上面的代码就是 SDK 本来会替你做的全部事情。如果你的流水线有一部分跑在你自己掌控的地方, 验证码识别 SDK 页面 介绍了四个官方客户端。

Zapier 是托管的,它怎么访问我机器上的识别工具?

靠 Server 模式。识别工具会绑定到你的内网或公网 IP,而不是回环地址,Zapier 就像调用任何其他公开 API 那样调它。给它配一个固定公网 IP,好让 Zap 里写的地址一直有效;在开放端口之前打开密钥校验,并给这个 Zap 签发专属密钥。

Delay 步骤该设多长?

reCAPTCHA v2 从 20 秒起步,极验(GeeTest)大约 5 秒。图片验证码回得够快,一个步骤配一秒的轮询通常就够了。更长并不自动等于更安全,因为 token 从签发那一刻就开始变老,所以延迟要往下调,调到偶尔看到一次重试为止,而不是往上调到再也见不到重试。

这件事我该用 Zapier 还是自托管的工具?

当工作流的其余部分本来就在 Zapier 上时,用 Zapier 是对的。如果验证码这块才是主角,那么一个你自己托管的工具能完全避开运行时长上限,还能通过内网地址跟识别工具通信。 n8n 教程 讲的就是这种结构,而且它的 Wait 节点没有对应的限制。

简短版结论

让识别工具以 Server 模式运行,好让 Zapier 能访问到它;在一个 Code 步骤里提交,延迟大约 20 秒,再用第二个步骤轮询。如果你更想只用一个步骤,就打开扩展运行时长。整条链路要紧凑,因为 token 的寿命远短于你的 Zap 历史记录。不管选哪种设计,成本都是平的:一个 验证码识别工具 跑在你已经拥有的机器上,不会按次收费,所以一天触发四百次的 Zap,和一天触发四次的 Zap,花的钱完全一样。