Tutorials

BLS CAPTCHA:理解指令代码并解决

BLS 验证码自动识别的思路不复杂:把 3×3 网格里的 9 张图片和三位数指令代码打包提交给 CaptchaAI API,接口返回匹配的单元格编号,按编号点击并提交表单即可。下面按提取、提交、点击三步拆解,Python 和 JavaScript 示例可直接复用。


BLS 验证码的网格和指令代码怎么解读

一个典型的 BLS 验证码由两部分组成:

  1. 3×3 图片网格,共 9 个格子,每个格子放一张图片。
  2. 数字指令代码(例如 664、123、546),告诉求解器要选出哪些格子。

格子按从左到右、从上到下的顺序编号:

1  2  3
4  5  6
7  8  9

指令代码是一种模式匹配规则,CaptchaAI 解析后返回 1–9 的索引数组,对应要点击的格子,不用自己猜规则。人工逐个比对图片和代码很容易点错,这也是脚本里最容易卡住的环节。


准备工作

只需三样东西:

  • 浏览器自动化库:Selenium 或 Puppeteer 均可;
  • HTTP 客户端:Python 用 requests,Node.js 用 axios
  • API Key:先在 CaptchaAI 控制台生成一个。

国内环境可用清华源加速安装:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple selenium requests


步骤 1:提取网格图片和指令代码

Python(Selenium)

import base64
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://example.com/bls-protected-page")

# Find the grid container
grid_cells = driver.find_elements(By.CSS_SELECTOR, ".captcha-grid img")

images = []
for cell in grid_cells:
    src = cell.get_attribute("src")
    if src.startswith("data:image"):
        images.append(src)
    else:
        # Download and convert to base64
        import requests
        img_data = requests.get(src).content
        b64 = base64.b64encode(img_data).decode()
        images.append(f"data:image/png;base64,{b64}")

# Extract the instruction code
instruction_el = driver.find_element(By.CSS_SELECTOR, ".captcha-instruction")
instruction_code = instruction_el.text.strip()
# e.g., "664" or parsed from "Select all boxes with number 664"

import re
code_match = re.search(r'(\d{3,})', instruction_code)
instruction = code_match.group(1) if code_match else instruction_code

print(f"Instruction: {instruction}")
print(f"Images extracted: {len(images)}")

遍历每张图片:能拿到 data:image 直接用,拿不到就下载转 base64;指令文本用正则提取三位以上数字作为 instructions

提示:网格图片常懒加载,抓取前先加显式等待(WebDriverWait/page.waitForSelector),避免拿到占位图。

JavaScript(Puppeteer)

思路相同,用 canvas 把每个格子转成 base64:

const puppeteer = require('puppeteer');

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com/bls-protected-page');

// Extract grid images as base64
const images = await page.evaluate(() => {
  const cells = document.querySelectorAll('.captcha-grid img');
  return Array.from(cells).map(img => {
    const canvas = document.createElement('canvas');
    canvas.width = img.naturalWidth;
    canvas.height = img.naturalHeight;
    canvas.getContext('2d').drawImage(img, 0, 0);
    return canvas.toDataURL('image/png');
  });
});

// Extract instruction code
const instruction = await page.evaluate(() => {
  const el = document.querySelector('.captcha-instruction');
  const match = el.textContent.match(/(\d{3,})/);
  return match ? match[1] : el.textContent.trim();
});

console.log(`Instruction: ${instruction}, Images: ${images.length}`);

步骤 2:把网格数据提交给 CaptchaAI 识别

BLS 求解器需要三样东西:method=blsinstructions 代码,以及 image_base64_1image_base64_9 这 9 张图片,缺一个都会报参数错误。

提示:9 张图片一起提交,体积比单图大;建议控制分辨率,避免请求体过大拖慢提交。

Python

import requests
import time
import json

API_KEY = "YOUR_API_KEY"

# Prepare submission data
data = {
    "key": API_KEY,
    "method": "bls",
    "instructions": instruction,
    "json": "1",
}

# Add all 9 images
files = {}
for i, img in enumerate(images):
    files[f"image_base64_{i+1}"] = (None, img)

# Submit
resp = requests.post(
    "https://ocr.captchaai.com/in.php",
    data=data,
    files=files
).json()

if resp["status"] != 1:
    raise Exception(f"Submit error: {resp['request']}")

task_id = resp["request"]
print(f"Task ID: {task_id}")

# Poll for result
for _ in range(20):
    time.sleep(5)
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": API_KEY, "action": "get", "id": task_id, "json": "1"
    }).json()

    if result["status"] == 1:
        solution = json.loads(result["request"])
        print(f"Selected cells: {solution}")  # e.g., [1, 4, 7, 8]
        break
    if result["request"] != "CAPCHA_NOT_READY":
        raise Exception(f"Error: {result['request']}")

拿到 task_id 后每 5 秒轮询一次 res.phpstatus 变成 1 时 result["request"] 就是要点击的格子编号数组。

JavaScript

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

const form = new FormData();
form.append('key', 'YOUR_API_KEY');
form.append('method', 'bls');
form.append('instructions', instruction);
form.append('json', '1');

images.forEach((img, i) => {
  form.append(`image_base64_${i + 1}`, img);
});

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

// Poll
let solution = null;
for (let i = 0; i < 20; i++) {
  await new Promise(r => setTimeout(r, 5000));
  const poll = await axios.get('https://ocr.captchaai.com/res.php', {
    params: { key: 'YOUR_API_KEY', action: 'get', id: taskId, json: 1 }
  });
  if (poll.data.status === 1) {
    solution = JSON.parse(poll.data.request);
    break;
  }
}
console.log('Selected cells:', solution); // e.g., [2, 4, 7]

步骤 3:点击已识别的单元格并提交表单

把索引数组里每个编号(1 起始)映射回页面格子并点击,最后提交表单。

# Selenium — click the cells returned by CaptchaAI
grid_cells = driver.find_elements(By.CSS_SELECTOR, ".captcha-grid .cell")

for cell_index in solution:
    # cell_index is 1-based
    grid_cells[cell_index - 1].click()

# Submit the form
submit_btn = driver.find_element(By.CSS_SELECTOR, ".captcha-submit")
submit_btn.click()
// Puppeteer
const cells = await page.$$('.captcha-grid .cell');
for (const idx of solution) {
  await cells[idx - 1].click();
}
await page.click('.captcha-submit');

完整工作流程代码

把前面三步封装成一个函数,方便在爬虫或测试脚本里直接调用:

def solve_bls_captcha(driver, api_key):
    """Extract, solve, and submit a BLS CAPTCHA."""
    import base64, requests, time, json, re

    # 1. Extract images
    grid_cells = driver.find_elements(By.CSS_SELECTOR, ".captcha-grid img")
    images = []
    for cell in grid_cells:
        src = cell.get_attribute("src")
        if src.startswith("data:image"):
            images.append(src)
        else:
            img_data = requests.get(src).content
            b64 = base64.b64encode(img_data).decode()
            images.append(f"data:image/png;base64,{b64}")

    # 2. Extract instruction
    el = driver.find_element(By.CSS_SELECTOR, ".captcha-instruction")
    match = re.search(r'(\d{3,})', el.text)
    instruction = match.group(1)

    # 3. Submit to CaptchaAI
    data = {"key": api_key, "method": "bls", "instructions": instruction, "json": "1"}
    files = {f"image_base64_{i+1}": (None, img) for i, img in enumerate(images)}
    resp = requests.post("https://ocr.captchaai.com/in.php", data=data, files=files).json()
    task_id = resp["request"]

    # 4. Poll
    for _ in range(20):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": api_key, "action": "get", "id": task_id, "json": "1"
        }).json()
        if result["status"] == 1:
            solution = json.loads(result["request"])
            break

    # 5. Click cells
    clickable = driver.find_elements(By.CSS_SELECTOR, ".captcha-grid .cell")
    for idx in solution:
        clickable[idx - 1].click()

    return solution

故障排查

问题 原因 处理方式
ERROR_BAD_PARAMETERS 图片缺失或没有传指令代码 确保 9 张图片都是有效的 base64 data URI,instructions 不为空
点击了错误的格子 格子到索引的映射搞反了 核对格子确实是从左到右、从上到下编号为 1–9
图片加载不出来 跨域限制导致 <img> 拿不到内容 改成服务器端下载图片再转 base64,不要依赖浏览器直接读取
指令代码提取为空 指令数字是画在图片里的,不是纯文本 先取指令文本,取不到就对指令图片跑一遍 OCR

多数问题出在提取环节,图片和指令代码对了之后基本不用再排查。


常见问题

BLS 验证码的指令代码具体是什么意思?

指令代码(比如“664”)是一种匹配规则,告诉求解器哪些格子算“命中”。你不用自己解读——把代码和 9 张图片交给 CaptchaAI,接口直接返回匹配的格子编号。

提交任务后一直拿不到结果怎么办?

CAPCHA_NOT_READY 说明还在处理,按 5 秒间隔继续轮询即可;其他错误文本通常是图片或指令代码有问题,回头检查提取步骤。

9 张图片必须是 base64 格式吗?

是的,两种情况都要转成 base64 再传给 image_base64_1image_base64_9

  • <img>src 本身就是 data:image 开头——直接用;
  • 否则先用服务器端请求下载原图,再编码成 base64。

BLS 验证码和九宫格图片验证码是一回事吗?

思路类似,都是 3×3 网格加选格逻辑,但 BLS 多了数字指令代码这层规则。CaptchaAI 用 method=bls 处理,识别通常在 1 秒以内。


用 CaptchaAI 自动识别 BLS 验证码

把网格图片和指令代码交给 CaptchaAI,提取、提交、点击即可全部脚本化。前往 CaptchaAI 官网 获取 API Key 即可开始。


相关指南

该文章已禁用评论。