Integrations

Airtable + CaptchaAI:数据库触发的验证码解决

Airtable 表里的记录一多,总有几条要先过验证码才能继续处理——不如直接接上 CaptchaAI:Automation 触发器和 Scripting 扩展都能调用它的识别接口,记录一变状态就自动提交、轮询、把 token 写回表里。本文演示两种接法:Automation 在 Status 变为 pending(待处理)时触发单条识别,Scripting 扩展批量处理积压记录。


建表:CAPTCHA Tasks 需要哪些字段

新建一个名为 CAPTCHA Tasks 的表,字段名要和脚本里用到的完全一致(脚本按字段名精确取值):

字段名 类型 用途
URL URL 目标页面地址
Sitekey 单行文本 reCAPTCHA 的 sitekey
Status 单选 可选值:pending / solving / solved / failed
Token 长文本 已识别的验证码 token
Solved At 日期/时间 识别完成的时间戳
Error 单行文本 识别失败时的错误信息

提示: Status 的选项要用英文小写 pendingsolvingsolvedfailed,写成中文选项会导致触发器和脚本都失效。

典型场景:跨境团队追踪海外供应商页面的验证码

跨境电商或供应链团队常用 Airtable 跟踪几十个海外供应商门户,这些页面大多挂着 reCAPTCHA v2(国内站点更常见的是 GeeTest(极验),面向海外的页面则几乎都是 reCAPTCHA/Turnstile)。每加入一条新 URL,都要先过验证码才能抓取或提交数据:

  • Automation 检测到新记录
  • 脚本把验证码参数提交给 CaptchaAI
  • CaptchaAI 返回已识别的 token
  • token 写回对应记录

第一步:创建 Automation 触发器

在 Airtable 里:

  1. 打开 Automations 标签
  2. 点击 Create automation
  3. 命名为:"Solve CAPTCHA on New Record"

触发条件

选择 When record matches conditions

项目
CAPTCHA Tasks
条件 Status 等于 pending

记录的 Status 变为 pending,无论是新记录还是重新提交的失败记录,都会激活触发器。

第二步:编写脚本动作

在触发器后添加一个动作 → Run a script

// Airtable Automation Script — Solve CAPTCHA via CaptchaAI

// Input configuration (set in the left panel):
// - recordId: Record ID from trigger
// - sitekey: Sitekey field from trigger
// - pageurl: URL field from trigger
const config = input.config();
const recordId = config.recordId;
const sitekey = config.sitekey;
const pageurl = config.pageurl;

const API_KEY = 'YOUR_API_KEY'; // Use input.config() for security

// Update status to "solving"
const table = base.getTable('CAPTCHA Tasks');
await table.updateRecordAsync(recordId, {
  'Status': { name: 'solving' },
});

try {
  // Step 1: Submit task to CaptchaAI
  const submitUrl = `https://ocr.captchaai.com/in.php?key=${API_KEY}&method=userrecaptcha&googlekey=${encodeURIComponent(sitekey)}&pageurl=${encodeURIComponent(pageurl)}&json=1`;

  const submitResponse = await fetch(submitUrl);
  const submitResult = await submitResponse.json();

  if (submitResult.status !== 1) {
    throw new Error(`Submit failed: ${submitResult.request}`);
  }

  const taskId = submitResult.request;
  console.log(`Task submitted: ${taskId}`);

  // Step 2: Poll for result (wait 15 seconds first)
  await new Promise(resolve => setTimeout(resolve, 15000));

  let token = null;
  for (let i = 0; i < 20; i++) {
    const pollUrl = `https://ocr.captchaai.com/res.php?key=${API_KEY}&action=get&id=${taskId}&json=1`;
    const pollResponse = await fetch(pollUrl);
    const pollResult = await pollResponse.json();

    if (pollResult.status === 1) {
      token = pollResult.request;
      break;
    }

    if (pollResult.request !== 'CAPCHA_NOT_READY') {
      throw new Error(`Solve failed: ${pollResult.request}`);
    }

    await new Promise(resolve => setTimeout(resolve, 5000));
  }

  if (!token) {
    throw new Error('Polling timeout — CAPTCHA not solved in time');
  }

  // Step 3: Update record with solved token
  await table.updateRecordAsync(recordId, {
    'Status': { name: 'solved' },
    'Token': token,
    'Solved At': new Date().toISOString(),
    'Error': '',
  });

  console.log(`CAPTCHA solved for record ${recordId}`);

} catch (error) {
  // Update record with error
  await table.updateRecordAsync(recordId, {
    'Status': { name: 'failed' },
    'Error': error.message,
  });
  console.error(`Failed: ${error.message}`);
}

配置脚本输入变量

在左侧输入面板里映射好这几个变量:

变量 来源
recordId 触发记录的 Record ID
sitekey 触发记录的 Sitekey 字段
pageurl 触发记录的 URL 字段

逻辑分三步:提交到 in.php、轮询 res.php(先等 15 秒),成功后写回 token 并置 Status 为 solved;失败则标记 failed 并写入 Error,方便按状态筛选重跑。

第三步:批量处理用 Scripting 扩展

如果表里已经积压了一批 pending 记录,没必要一条条触发 Automation——用 Scripting 扩展(在 Apps 面板里)一次跑完:

// Batch CAPTCHA Solver — Airtable Scripting Extension
const API_KEY = 'YOUR_API_KEY';
const table = base.getTable('CAPTCHA Tasks');

// Get all pending records
const query = await table.selectRecordsAsync({
  fields: ['URL', 'Sitekey', 'Status'],
});

const pendingRecords = query.records.filter(
  r => r.getCellValueAsString('Status') === 'pending'
);

output.text(`Found ${pendingRecords.length} pending CAPTCHAs`);

for (const record of pendingRecords) {
  const sitekey = record.getCellValueAsString('Sitekey');
  const pageurl = record.getCellValueAsString('URL');

  if (!sitekey || !pageurl) {
    output.text(`Skipping ${record.id} — missing sitekey or URL`);
    continue;
  }

  output.text(`Solving for: ${pageurl}`);

  await table.updateRecordAsync(record.id, {
    'Status': { name: 'solving' },
  });

  try {
    // Submit
    const submitResp = await fetch(
      `https://ocr.captchaai.com/in.php?key=${API_KEY}&method=userrecaptcha&googlekey=${encodeURIComponent(sitekey)}&pageurl=${encodeURIComponent(pageurl)}&json=1`
    );
    const submitData = await submitResp.json();

    if (submitData.status !== 1) throw new Error(submitData.request);

    // Poll
    await new Promise(r => setTimeout(r, 15000));
    let token = null;

    for (let i = 0; i < 20; i++) {
      const pollResp = await fetch(
        `https://ocr.captchaai.com/res.php?key=${API_KEY}&action=get&id=${submitData.request}&json=1`
      );
      const pollData = await pollResp.json();

      if (pollData.status === 1) { token = pollData.request; break; }
      if (pollData.request !== 'CAPCHA_NOT_READY') throw new Error(pollData.request);
      await new Promise(r => setTimeout(r, 5000));
    }

    if (!token) throw new Error('Timeout');

    await table.updateRecordAsync(record.id, {
      'Status': { name: 'solved' },
      'Token': token,
      'Solved At': new Date().toISOString(),
    });
    output.text(`✓ Solved: ${pageurl}`);

  } catch (e) {
    await table.updateRecordAsync(record.id, {
      'Status': { name: 'failed' },
      'Error': e.message,
    });
    output.text(`✗ Failed: ${e.message}`);
  }
}

output.text('Batch processing complete');

这段脚本会先把所有 Status 为 pending 的记录取出来,逐条提交、轮询,成功就把 token 和完成时间写回去,失败就记录错误原因,跑完后在控制台打印一行汇总。


常见问题

Airtable 自动化脚本的执行时长上限是多少?

都是 30 秒上限(部分付费计划可延长)。接近超时时,缩短轮询次数、加长首次等待更管用。

免费版 Airtable 能跑通这一整套流程吗?

可以,主要受限于每月运行次数和记录数上限,用量大了再升级,不影响搭建方式。

图片验证码(Image CAPTCHA)能用同一套逻辑处理吗?

可以,把 method 改成 base64 并提交图片数据即可,Base64 图片可存进 Airtable 附件字段,脚本里再读出来提交。

sitekey 应该从哪里提取,容易踩什么坑?

常见来源和坑点:

  • 目标页面的 data-sitekey 属性,或对应的 JS 初始化代码
  • 开发者工具里搜索 sitekey 通常能直接定位
  • 最容易踩的坑:把测试环境和线上环境的 sitekey 搞混,导致轮询一直拿不到有效 token

多个 Base 或多人共用同一个 API Key 会有问题吗?

不会,Key 和 Base 无关,可以复用。真正要注意的是别把 Key 明文写进脚本——用输入变量传入。

排查清单

  • Automation 没有触发:Status 不等于 pending → 核对单选字段的值是否完全一致
  • fetch is not defined:部分脚本环境要用 remoteFetchAsync → 把 fetch 换成 remoteFetchAsync
  • 脚本超时:脚本动作有 30 秒执行上限 → 减少轮询次数,加长首次等待
  • 记录更新失败:字段名和表里的对不上 → 核对 updateRecordAsync 里的字段名
  • API Key 出现在脚本里:Key 被硬编码在代码中 → 改用 input.config() 输入变量

相关文章

下一步

把验证码识别交给自动化,团队就能专心做数据本身——获取 CaptchaAI 的 API Key,按上面的步骤接好 Automation 或 Scripting 扩展。

相关指南

该文章已禁用评论。