BLS 验证码是 3×3 九宫格加一串数字指令码,指令码决定该点哪几格。Node.js 里只有一条路径:九张格子图连同指令码交给识别接口,拿回单元格索引,再让浏览器按索引点击。下面拆成四步,用 CaptchaAI 的 bls 方法,提交走 in.php,取结果走 res.php。
动手前需要准备什么
| 项目 | 要求 |
|---|---|
| CaptchaAI API Key | 注册后在控制台获取 |
| Node.js | 14+ |
| 依赖库 | axios 与 puppeteer |
Puppeteer 首次安装要下载 Chromium,走 npm 镜像能省掉大半等待。
CaptchaAI 按线程计费而非按次:BASIC $15/月 5 线程,STANDARD $30/月 15 线程,ADVANCE $90/月 50 线程,套餐内识别次数不限,价格按美元计价。
九宫格加指令码:BLS 的判定逻辑
格子从左到右、从上到下编号:
1 | 2 | 3
---------
4 | 5 | 6
---------
7 | 8 | 9
页面同时给出一串数字指令(例如 “664”),决定哪几格是答案。脚本把指令码和九张图一起发出去,接口返回索引数组,比如 [1, 4, 7, 8]。
BLS 的识别结果是坐标而不是 token,没有隐藏字段可填,最后一步必须由浏览器真实点击完成。
步骤 1:抓取指令码与九张格子图
用 Puppeteer 打开表单页,读出指令码,收集九个 img 的地址。格子图可能内联,也可能是普通 URL,下面的循环对后者用 axios 拉回二进制转 base64。
const axios = require('axios');
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com/bls-form');
// Get instruction code
const instruction = await page.$eval('.bls-instruction', (el) => el.textContent.trim());
// Get all 9 cell image URLs and convert to base64
const cellImages = await page.$$eval('.bls-grid img', (imgs) =>
imgs.map((img) => img.src)
);
const images = [];
for (const src of cellImages) {
if (src.startsWith('data:')) {
images.push(src);
} else {
const { data } = await axios.get(src, { responseType: 'arraybuffer' });
const b64 = Buffer.from(data).toString('base64');
images.push(`data:image/png;base64,${b64}`);
}
}
如果 cellImages 长度不是 9,说明页面还没渲染完,goto 后补一句 waitForSelector 再取。
步骤 2:带指令码提交到 CaptchaAI
POST 到 in.php,method 固定为 bls,九张图放进 image_base64_1 到 9,指令码放进 instructions,加 json=1 让返回体是 JSON。
const API_KEY = 'YOUR_API_KEY';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const params = new URLSearchParams({
key: API_KEY,
method: 'bls',
instructions: instruction,
json: '1',
});
// Add all 9 images
images.forEach((img, i) => {
params.append(`image_base64_${i + 1}`, img);
});
const { data: submitData } = await axios.post(
'https://ocr.captchaai.com/in.php',
params.toString()
);
if (submitData.status !== 1) throw new Error(submitData.request);
const taskId = submitData.request;
console.log(`Task submitted: ${taskId}`);
返回的 request 字段是任务 ID。YOUR_API_KEY 请改成从环境变量读取,别把密钥写死在仓库里。
步骤 3:轮询识别结果
先等 5 秒再开始查,之后每 5 秒查一次 res.php,最多 30 轮。
await sleep(5000);
let selectedCells;
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) {
selectedCells = JSON.parse(pollData.request);
console.log('Selected cells:', selectedCells);
break;
}
if (pollData.request !== 'CAPCHA_NOT_READY') {
throw new Error(pollData.request);
}
await sleep(5000);
}
CAPCHA_NOT_READY 是正常状态;其他字符串都是真实错误,要抛出而不是继续循环,否则会静默空转两分半。
步骤 4:按索引点击并提交表单
把 selectedCells 映射回页面上的九个 img。编号从 1 开始,DOM 下标从 0 开始,故要 cellNum - 1。
// Click each identified cell
const gridCells = await page.$$('.bls-grid img');
for (const cellNum of selectedCells) {
await gridCells[cellNum - 1].click();
}
// Submit the form
await page.click('.bls-submit');
console.log(`Solved: clicked cells ${JSON.stringify(selectedCells)}`);
await browser.close();
正常运行时你会看到:
Selected cells: [1, 4, 7, 8]
Solved: clicked cells [1,4,7,8]
建议把这四步封装成 solveBls(page) 并允许重试一次——图片拉取被网络抖动打断,比识别本身失败更常见。
实战场景:预约表单的回归测试
BLS 九宫格常见于签证预约一类的表单。合规的典型用法是:机构自己运营预约系统,上线前要验证“填表 → 过验证码 → 提交”在高并发下是否稳定,脚本在 staging 环境反复跑,验证码环节交给上面四步。用 STANDARD 套餐(15 线程),最多 15 个用例可同时停在这一步互不排队。
请只在你自有或已获授权的系统上运行脚本,并留意《网络安全法》《数据安全法》的采集范围要求。
错误码排查
| 错误 | 原因 | 处理方式 |
|---|---|---|
ERROR_BAD_PARAMETERS |
图片不足 9 张或缺指令码 | 确认九张图齐全,instructions 非空 |
CAPCHA_NOT_READY |
任务仍在处理 | 正常状态,继续轮询 |
ERROR_ZERO_BALANCE |
余额不足 | 到控制台充值 |
两类问题不体现为错误码:base64 里混进懒加载占位图,以及指令码带空格。前者靠 waitForSelector,后者用 .trim()。
常见问题
BLS 返回的是 token 还是坐标?
是坐标。返回索引数组(如 [1, 4, 7, 8]),要在浏览器里逐个点击,而不是填进隐藏字段——这和 reCAPTCHA v2 返回 g-recaptcha-response 的流程不同。
九张图必须全部提交吗?只发指令命中的那几张行不行?
必须全部提交。要看到完整九宫格才能判断哪几格匹配指令,缺图直接返回 ERROR_BAD_PARAMETERS。
能用 Playwright 替代 Puppeteer 吗?
可以。axios 提交与轮询一行都不用改,只需把 page.click 换成 Playwright 的 locator().click() 写法。
一个 BLS 任务大概多久出结果?
通常 5–15 秒。这只是参考区间,实际耗时随图片体积与排队情况变化,请实测后再定超时阈值。
CaptchaAI 还支持哪些九宫格类的验证码?
还支持 Grid Image(网格图片)与图片 / OCR 验证码。BLS 固定 3×3,4×4 等变体请改用 Grid Image 方法。