API Tutorials

Node.js 识别图片验证码:CaptchaAI OCR API 实战

先给结论:在 Node.js 里识别一张扭曲字符的图片验证码只有两个动作——把图片以 base64 或文件形式 POST 到 in.php,拿到任务 ID 后每 5 秒轮询 res.php 直到拿回文本。核心逻辑不到 30 行,花时间的是截图、字符集约束和错误处理。

这类老式验证码在国内格外常见:政务平台、发票查验、社保查询,还有大量没重构的企业后台,用的仍是 4~6 位扭曲字符加干扰线。国际站点多是 reCAPTCHA 或 Turnstile,遗留系统里则是它。CaptchaAI 把图片读成文本,你负责填回表单。


开始前的准备

项目 要求
CaptchaAI API Key CaptchaAI 控制台 注册获取
Node.js 14+
依赖库 axiosfs
图片格式 JPG/PNG/GIF(100 字节 – 100 KB)
  • 装依赖:npm install axios form-data,国内走 npmmirror 源更快。
  • API Key 从环境变量读;示例里的 YOUR_API_KEY 只是占位符。

方法 A:base64 提交(图片已在内存里)

适合 Puppeteer 刚截完图或图片来自接口响应:编码后直接提交,省掉落盘。

const axios = require('axios');
const fs = require('fs');

const API_KEY = 'YOUR_API_KEY';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

// Read and encode the image
const imageB64 = fs.readFileSync('captcha.png').toString('base64');

// Submit to CaptchaAI
const { data: submitData } = await axios.post('https://ocr.captchaai.com/in.php', null, {
  params: {
    key: API_KEY,
    method: 'base64',
    body: imageB64,
    json: 1,
  },
});

if (submitData.status !== 1) throw new Error(submitData.request);
const taskId = submitData.request;
console.log(`Task submitted: ${taskId}`);

status1 时,request 就是轮询用的任务 ID;带上 json: 1 可省去解析纯文本。

方法 B:以文件形式上传

图片就在磁盘上、或体积偏大时,走 multipart 更直接,省掉一长串 base64。

const FormData = require('form-data');

const form = new FormData();
form.append('key', API_KEY);
form.append('method', 'post');
form.append('json', '1');
form.append('file', fs.createReadStream('captcha.png'));

const { data: submitData } = await axios.post('https://ocr.captchaai.com/in.php', form, {
  headers: form.getHeaders(),
});

const taskId = submitData.request;

两种耗时相同,只看图片怎么来:截图用 A,读文件用 B,除 method 外参数一致。

轮询 res.php 取回识别文本

提交后先等 5 秒再进循环,能省掉一批无效请求。

await sleep(5000);

let captchaText;
for (let i = 0; i < 30; i++) {
  const { data: pollData } = await axios.get('https://ocr.captchaai.com/res.php', {
    params: { key: API_KEY, action: 'get', id: taskId, json: 1 },
  });

  if (pollData.status === 1) {
    captchaText = pollData.request;
    console.log(`CAPTCHA text: ${captchaText}`);
    break;
  }
  if (pollData.request !== 'CAPCHA_NOT_READY') {
    throw new Error(pollData.request);
  }
  await sleep(5000);
}

CAPCHA_NOT_READY 表示还在队列里(接口原样返回的拼写,不是笔误),继续等即可;其他字符串一律抛错:多半是参数或图片有问题,重试也是白搭。

用参数约束字符集,提升识别准确率

摸清目标验证码的规律后,比如固定 4 位纯数字,一定要带上约束参数。

// Digits only, 4-6 characters
const { data } = await axios.post('https://ocr.captchaai.com/in.php', null, {
  params: {
    key: API_KEY,
    method: 'base64',
    body: imageB64,
    numeric: 1,      // digits only
    min_len: 4,       // minimum length
    max_len: 6,       // maximum length
    json: 1,
  },
});
参数 取值 作用
numeric 1 = 纯数字,2 = 纯字母 限定字符类型
min_len / max_len 整数 限定长度范围
calc 1 计算图中的算式
regsense 1 区分大小写

搜索空间越小,结果可用率越高——零成本,却最容易被跳过。

完整示例:截图 → 识别 → 填表

整条流水线如下。

const axios = require('axios');
const puppeteer = require('puppeteer');
const fs = require('fs');

const API_KEY = 'YOUR_API_KEY';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

async function solveImageCaptcha() {
  // 1. Load page and screenshot CAPTCHA
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com/register');

  const captchaEl = await page.$('#captcha-image');
  await captchaEl.screenshot({ path: 'captcha.png' });

  // 2. Encode and submit
  const imageB64 = fs.readFileSync('captcha.png').toString('base64');
  const { data: submit } = await axios.post('https://ocr.captchaai.com/in.php', null, {
    params: { key: API_KEY, method: 'base64', body: imageB64, json: 1 },
  });
  const taskId = submit.request;

  // 3. Poll for text
  await sleep(5000);
  let text;
  for (let i = 0; i < 30; i++) {
    const { data: poll } = await axios.get('https://ocr.captchaai.com/res.php', {
      params: { key: API_KEY, action: 'get', id: taskId, json: 1 },
    });
    if (poll.status === 1) { text = poll.request; break; }
    if (poll.request !== 'CAPCHA_NOT_READY') throw new Error(poll.request);
    await sleep(5000);
  }

  // 4. Type and submit
  await page.type('#captcha-input', text);
  await page.click('form [type="submit"]');
  console.log(`Solved: ${text}`);
  await browser.close();
}

solveImageCaptcha().catch(console.error);

预期输出:

Solved: ABC123

接进流程时注意两点:

  • 超时和重试套在整段上:识别失败意味着这一轮表单要重来。
  • 确认选择器命中的是验证码图片,截到空白是最常见的失败原因。

常见错误码与处理

错误码 原因 处理方式
ERROR_WRONG_FILE_EXTENSION 格式不支持 改用 JPG/PNG/GIF
ERROR_TOO_BIG_CAPTCHA_FILESIZE 超过 100 KB 先压缩
ERROR_ZERO_CAPTCHA_FILESIZE 小于 100 字节 检查截图
CAPCHA_NOT_READY 仍在识别 每 5 秒轮询一次
  • 结果不对时,用 action=reportbad 带任务 ID 请求 res.php 上报。
  • 体积最容易踩坑:截图后先判断大小,超了用 sharp 压一次;不足 100 字节说明截图逻辑有问题。

并发怎么估:一个日常回归的例子

假设一套发票查验系统的回归脚本,每天 2000 次表单提交,每次一道图片验证码。CaptchaAI 按并发线程计费,不按次数计费:串行跑用 BASIC($15/月,5 线程)够用;要半小时内跑完,并发拉到几十,看 ADVANCE($90/月,50 线程)。

合规提醒:这套流程只跑在你自有或已获授权的系统上——网络安全法、数据安全法和《个人信息保护法》对未授权访问都划了边界。


常见问题

识别一次要多久?超时该怎么设?

图片验证码是最轻的一类任务,多数几秒返回。代码里留 150 秒上限即可;30 秒还没结果,重新截图比继续等更划算。

中文汉字验证码也能提交吗?

提交方式完全一样,都走图片/OCR 这一类,接口不区分语种。但非拉丁字符效果差异较大,上线前用自有样本测一轮。

CaptchaAI 能识别 hCaptcha 或 GeeTest v4 吗?

都不在支持范围内。正式支持 reCAPTCHA v2/v3 系列、Cloudflare Turnstile 与 Challenge、GeeTest v3、图片/OCR、九宫格和 BLS,另有 CaptchaFox、Friendly Captcha、Lemin 三类测试版。GeeTest v4 的官方口径是即将支持。


相关阅读


注册 CaptchaAI,跑通第一次图片验证码识别 →

该文章已禁用评论。