同样是点选图片,BLS CAPTCHA 和 reCAPTCHA 九宫格的识别路径却完全不同:BLS 必须把每张图片抠出来、编码成 base64 再发给接口,reCAPTCHA 只要拿一个 token 回填即可。BLS CAPTCHA 是 BLS International 签证系统专用的定制实现,reCAPTCHA 则是 Google 全球通用的方案。先把两条路径的核心区别摆出来:
- BLS: 图片直接嵌在页面 DOM 里,必须逐张抠图、编码成 base64 上传,离不开浏览器,走
method=bls。 - reCAPTCHA 九宫格: 走 token 路线,拿 sitekey 换到
g-recaptcha-response回填即可,可纯 HTTP,走method=userrecaptcha。
如何选型:先决定用哪种方法
看到图片验证码,先判断它是 BLS 品牌还是 reCAPTCHA,再决定走哪条路径。
| 场景 | 建议方法 |
|---|---|
| BLS 签证预约 | 用 method=bls,走图片提取 |
| 任意带 reCAPTCHA 的站点 | 用 method=userrecaptcha,带上 sitekey |
| BLS 页面内嵌 reCAPTCHA | 先判断是哪一个在拦截提交——BLS 站点有时两者都用 |
| 不确定的图片验证码 | 先看是不是 BLS 品牌;不是就试 reCAPTCHA 或 OCR 方法 |
在中国申请申根、英国等签证时,BLS International 是常见的签证代传中心,其预约页面用的就是这套自定义图片验证码;国际站点和表单里遇到的则多半是 reCAPTCHA。批量处理 reCAPTCHA 时,token 请求无状态、可并发,配合线程套餐更划算——CaptchaAI 按并发线程计费,从 BASIC($15/月,5 线程)到 ADVANCE($90/月,50 线程),线程内不限识别次数。
并排对比
| 对比项 | BLS CAPTCHA | reCAPTCHA 九宫格 |
|---|---|---|
| 提供方 | BLS International(定制实现) | |
| 图片形式 | 3–9 张独立图片 | 一整张图切成 3×3 或 4×4 网格 |
| 动态图块 | 无,图片集固定 | 有,选中后会淡入新图块 |
| 行为分析 | 很少 | 广泛(鼠标轨迹、计时、IP) |
| 风险评分 | 无 | 每次请求都做风险分析 |
| API 方法 | method=bls |
method=userrecaptcha |
| 必填参数 | instructions、image_base64_1..9 |
googlekey、pageurl |
| 返回内容 | 图片索引(如 1,3,5) |
token 字符串 |
| 是否 token 化 | 否,必须直接识别图片 | 是,token 方法内部处理网格 |
| 是否需要浏览器 | 需要(要从 DOM 抠图) | 可选(token 方法无需浏览器) |
| 出现场景 | 仅 BLS 签证预约站点 | 全球数百万站点 |
| 典型识别耗时 | 5–15 秒 | 10–30 秒 |
用 CaptchaAI 识别 BLS CAPTCHA
- 用浏览器读取验证码的指令文本(如“选出所有摩托车”)。
- 逐张抠出图片,编码成 base64。
- 带上
method=bls和指令,把所有图片一起提交到接口。 - 每 5 秒轮询一次结果,拿到图片索引(如
1,3,5)。 - 按返回的索引在页面上依次点选。
Python:
import requests
import base64
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example-bls-site.com/appointment")
# Step 1: Extract instruction text
instruction = driver.find_element(By.CSS_SELECTOR, ".captcha-instruction").text
# Example: "Select all images with a motorcycle"
# Step 2: Extract and encode each image
images = driver.find_elements(By.CSS_SELECTOR, ".captcha-image img")
image_data = {}
for i, img in enumerate(images, 1):
# Get image source and convert to base64
src = img.get_attribute("src")
if src.startswith("data:image"):
b64 = src.split(",")[1]
else:
img_bytes = requests.get(src).content
b64 = base64.b64encode(img_bytes).decode()
image_data[f"image_base64_{i}"] = b64
# Step 3: Submit to CaptchaAI
payload = {
"key": "YOUR_API_KEY",
"method": "bls",
"instructions": instruction,
**image_data
}
resp = requests.post("https://ocr.captchaai.com/in.php", data=payload)
task_id = resp.text.split("|")[1]
# Step 4: Poll for result
for _ in range(30):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": "YOUR_API_KEY", "action": "get", "id": task_id
})
if result.text.startswith("OK|"):
indices = result.text.split("|")[1] # e.g., "1,3,5"
break
# Step 5: Click the correct images
for idx in indices.split(","):
images[int(idx) - 1].click()
time.sleep(0.3) # Small delay between clicks
Node.js:
const axios = require("axios");
const puppeteer = require("puppeteer");
(async () => {
const browser = await puppeteer.launch({ headless: "new" });
const page = await browser.newPage();
await page.goto("https://example-bls-site.com/appointment");
// Extract instruction text
const instruction = await page.$eval(
".captcha-instruction",
(el) => el.textContent,
);
// Extract images as base64
const imageElements = await page.$$(".captcha-image img");
const imageData = {};
for (let i = 0; i < imageElements.length; i++) {
const src = await imageElements[i].evaluate((el) => el.src);
let b64;
if (src.startsWith("data:image")) {
b64 = src.split(",")[1];
} else {
const resp = await axios.get(src, { responseType: "arraybuffer" });
b64 = Buffer.from(resp.data).toString("base64");
}
imageData[`image_base64_${i + 1}`] = b64;
}
// Submit to CaptchaAI
const formData = new URLSearchParams({
key: "YOUR_API_KEY",
method: "bls",
instructions: instruction,
...imageData,
});
const submitResp = await axios.post(
"https://ocr.captchaai.com/in.php",
formData.toString(),
);
const taskId = submitResp.data.split("|")[1];
// Poll for result
let indices;
for (let i = 0; i < 30; i++) {
await new Promise((r) => setTimeout(r, 5000));
const result = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: "YOUR_API_KEY", action: "get", id: taskId },
});
if (result.data.startsWith("OK|")) {
indices = result.data.split("|")[1];
break;
}
}
// Click correct images
for (const idx of indices.split(",")) {
await imageElements[parseInt(idx) - 1].click();
await new Promise((r) => setTimeout(r, 300));
}
await browser.close();
})();
用 CaptchaAI 识别 reCAPTCHA 九宫格
- 从页面读取
data-sitekey。 - 带上
method=userrecaptcha、googlekey、pageurl提交任务。 - 每 5 秒轮询一次,拿到
g-recaptcha-responsetoken。 - 把 token 回填到隐藏字段,或直接随表单 POST 提交。
Python:
import requests
import time
# Step 1: Extract sitekey from page
# <div class="g-recaptcha" data-sitekey="6Le-wvkS..."></div>
sitekey = "6Le-wvkSAAAAAPBMRTvw..."
page_url = "https://example.com/form"
# Step 2: Submit to CaptchaAI
resp = requests.get("https://ocr.captchaai.com/in.php", params={
"key": "YOUR_API_KEY",
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url
})
task_id = resp.text.split("|")[1]
# Step 3: Poll for token
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": "YOUR_API_KEY", "action": "get", "id": task_id
})
if result.text.startswith("OK|"):
token = result.text.split("|")[1]
break
# Step 4: Inject token (no clicking needed)
# Option A: Hidden field injection
driver.execute_script(
f'document.getElementById("g-recaptcha-response").value = "{token}";'
)
# Option B: Pure HTTP submission
requests.post(page_url, data={"g-recaptcha-response": token, "other_field": "value"})
Node.js:
const axios = require("axios");
async function solveRecaptchaGrid(sitekey, pageUrl) {
// Submit
const submitResp = await axios.get("https://ocr.captchaai.com/in.php", {
params: {
key: "YOUR_API_KEY",
method: "userrecaptcha",
googlekey: sitekey,
pageurl: pageUrl,
},
});
const taskId = submitResp.data.split("|")[1];
// Poll for token
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 5000));
const result = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: "YOUR_API_KEY", action: "get", id: taskId },
});
if (result.data.startsWith("OK|")) {
return result.data.split("|")[1];
}
}
throw new Error("Solve timed out");
}
集成复杂度对比
两者最大的差距在集成成本:BLS 每一步都得自己动手,reCAPTCHA 的 token 方法把网格处理都藏在了接口里。
| 环节 | BLS | reCAPTCHA 九宫格 |
|---|---|---|
| 图片提取 | 必须手动从 DOM 逐张抠出 | 不需要,token 方法内部处理 |
| 指令解析 | 必须从页面读取提示文本 | 不需要 |
| Base64 编码 | 每张图片都要 | 不需要 |
| 结果回填 | 按返回索引逐个点选图片 | 把 token 提交到隐藏字段或 HTTP POST |
| 浏览器自动化 | 必需,没有无头替代方案 | 可选,可纯 HTTP 请求 |
| 失败恢复 | 重新抠图后重试 | 重新请求 token |
| 并发识别 | 受限,图片与浏览器会话绑定 | 简单,token 请求无状态 |
| 典型代码量 | 40–60 行 | 15–25 行 |
常见问题排查
两者排查思路不同:BLS 多半错在抠图和指令,reCAPTCHA 多半错在参数和回填。
| 问题 | BLS 排查 | reCAPTCHA 排查 |
|---|---|---|
| 返回答案错误 | 核对指令文本与页面显示是否一致 | 确认 sitekey 和 pageurl 是否正确 |
| 图片加载不出来 | 抓取图片 URL 时补上 User-Agent 和 cookie |
不涉及——没有需要抓取的图片 |
| token 被拒 | 按正确顺序点选页面上的索引 | 回填 g-recaptcha-response 并执行回调 |
| 识别太慢 | 减少图片数量,只发可见图片 | 在目标站点就近部署代理以降低延迟 |
常见问题
为什么 BLS CAPTCHA 必须开浏览器,reCAPTCHA 却不用?
BLS 的图片直接嵌在页面 DOM 里,只能靠浏览器把每张图抠出来再编码上传,所以必须有浏览器会话。reCAPTCHA 用的是 token 方法,只要页面上的 sitekey 和 pageurl 就能提交,纯 HTTP 请求即可,不依赖浏览器。
reCAPTCHA 九宫格识别该选哪个套餐?
CaptchaAI 按并发线程计费,不按次数。token 请求无状态、可以并发,量不大时 BASIC($15/月,5 线程)就够用;并发需求高再往 ADVANCE($90/月,50 线程)以上升级,线程内识别次数不限。
CaptchaAI 能识别 hCaptcha 和 FunCaptcha 吗?
不支持。CaptchaAI 目前覆盖 reCAPTCHA、Cloudflare Turnstile、GeeTest v3、BLS 以及图片/OCR 类验证码;hCaptcha 和 FunCaptcha 暂不在支持范围内。选型时先确认目标站点用的是哪种验证码。
BLS 预约页面同时出现两种验证码怎么办?
分别处理。有些 BLS 站点用自定义图片验证码做初次校验,再用 reCAPTCHA 守住表单提交。先分别检测两种类型,各自调用对应的 CaptchaAI 方法即可。
相关指南
- BLS CAPTCHA 分步识别教程 — 完整的 BLS 集成流程
- 用 API 识别 reCAPTCHA v2 — reCAPTCHA v2 完整教程
- reCAPTCHA 九宫格挑战原理 — 网格挑战如何运作
- BLS 验证码报错与排查 — BLS 专属错误修复