API Tutorials

BLS CAPTCHA 指令和代码参数深入探讨

sitekeypageurl 传对了,结果却还是不准?问题往往出在两个容易被忽略的可选字段——instructionscode。本文按接入顺序讲清楚提取、提交、排查。

BLS CAPTCHA 参数速查表

参数 必需 类型 说明
method 字符串 固定值 bls
sitekey 字符串 目标网站的 BLS CAPTCHA 密钥
pageurl 字符串 展示验证码的页面 URL
instructions 字符串 从验证码图片区域提取的文字说明
code 字符串 BLS CAPTCHA 的代码/类型标识
json 整数 需要 JSON 格式响应时设为 1

三个必填参数决定了请求能不能提交成功,两个可选参数决定了识别准不准:

  • methodsitekeypageurl 缺一不可,漏传会直接返回 ERROR_BAD_PARAMETERS
  • instructions 建议在挑战文字单独展示、没有嵌进图片时补上,能明显减少误判
  • code 只在部分站点区分挑战子类型时才有意义,多数场景可以不传

第一步:从页面提取 sitekey、instructions 和 code

# extract_bls.py
import re
from selenium import webdriver
from selenium.webdriver.common.by import By


def extract_bls_params(url):
    """Extract BLS CAPTCHA parameters from a page."""
    driver = webdriver.Chrome()
    driver.get(url)

    params = {"pageurl": url}

    # Extract sitekey
    captcha_el = driver.find_element(By.CSS_SELECTOR, "[data-sitekey], .bls-captcha")
    sitekey = captcha_el.get_attribute("data-sitekey")
    if sitekey:
        params["sitekey"] = sitekey

    # Extract instructions if visible
    try:
        instructions_el = driver.find_element(
            By.CSS_SELECTOR, ".captcha-instructions, .captcha-text"
        )
        params["instructions"] = instructions_el.text.strip()
    except Exception:
        pass

    # Extract code from hidden input or script
    page_source = driver.page_source
    code_match = re.search(r'captcha_code["\']?\s*[:=]\s*["\']([^"\']+)', page_source)
    if code_match:
        params["code"] = code_match.group(1)

    driver.quit()
    return params


# Usage
params = extract_bls_params("https://bls-example.com/appointment")
print(params)

两点提醒:

  • 上面的选择器只是兜底方案,接入前务必用开发者工具核对目标页面的真实 DOM 结构
  • 国内环境装 Selenium、requests 慢的话,pip install-i 指向清华 TUNA 镜像能省不少时间

提示:如果同一个页面同时出现 reCAPTCHA 和 BLS CAPTCHA,先确认当前展示的是哪一种再提交,method 传错会直接拿到无效结果。

第二步:把参数提交给 CaptchaAI

基础请求示例

# solve_bls_basic.py
import requests
import time
import os


def solve_bls(sitekey, pageurl, instructions=None, code=None):
    """Solve BLS CAPTCHA via CaptchaAI API."""
    api_key = os.environ["CAPTCHAAI_API_KEY"]

    payload = {
        "key": api_key,
        "method": "bls",
        "sitekey": sitekey,
        "pageurl": pageurl,
        "json": 1,
    }

    # Add optional parameters for higher accuracy
    if instructions:
        payload["instructions"] = instructions
    if code:
        payload["code"] = code

    resp = requests.post(
        "https://ocr.captchaai.com/in.php",
        data=payload,
        timeout=30,
    )
    result = resp.json()

    if result.get("status") != 1:
        raise RuntimeError(f"Submit failed: {result.get('request')}")

    task_id = result["request"]

    # Poll for result
    time.sleep(10)
    for _ in range(30):
        resp = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": api_key,
            "action": "get",
            "id": task_id,
            "json": 1,
        }, timeout=15)
        data = resp.json()

        if data.get("status") == 1:
            return data["request"]
        if data["request"] != "CAPCHA_NOT_READY":
            raise RuntimeError(data["request"])
        time.sleep(5)

    raise TimeoutError("BLS solve timeout")


# Usage
solution = solve_bls(
    sitekey="your-bls-sitekey",
    pageurl="https://bls-example.com/appointment",
    instructions="Select images in the correct order",
)
print(f"Solution: {solution}")

instructions 参数:什么时候必须传,为什么能提升准确率

instructions 告诉 CaptchaAI 挑战在问什么。文字单独展示、不嵌在图片里时,一并提交能明显减少误判。

# Common BLS instruction patterns:
instructions_examples = [
    "Select images in the correct order",
    "Click the images in order from left to right",
    "Arrange the images by number",
    "Select the matching image",
    "Click in the order shown",
]

# Extract instructions from the CAPTCHA image area
def get_instructions_from_page(driver):
    """Try multiple selectors to find instruction text."""
    selectors = [
        ".captcha-instructions",
        ".bls-captcha-text",
        "#captcha-prompt",
        ".challenge-text",
    ]

    for sel in selectors:
        try:
            el = driver.find_element(By.CSS_SELECTOR, sel)
            text = el.text.strip()
            if text:
                return text
        except Exception:
            continue

    return None

code 参数:识别具体是哪一种 BLS 挑战

部分 BLS 实现用内嵌代码值区分挑战子类型,code 就是把这个标识传给 CaptchaAI。它藏得比 sitekey 深,通常在隐藏 input 或内联脚本里,需要用正则提取。

# Detect BLS CAPTCHA code from page
def detect_bls_code(page_source):
    """Detect which BLS CAPTCHA code/type is being used."""
    patterns = [
        (r'captchaType["\']?\s*[:=]\s*["\'](\w+)', "captchaType"),
        (r'data-captcha-code["\']?\s*=\s*["\'](\w+)', "data attribute"),
        (r'bls_code["\']?\s*[:=]\s*["\'](\w+)', "bls_code"),
    ]

    for pattern, source in patterns:
        match = re.search(pattern, page_source)
        if match:
            return match.group(1)

    return None

用 Selenium 串联完整流程

把前面几步串成一个端到端脚本,顺序是:

  1. 先填好验证码之外的表单字段
  2. 提取 sitekeyinstructions
  3. 调用 solve_bls 拿到 token
  4. 把 token 注入页面并提交表单

不同项目需要调整的通常只是选择器和表单字段本身。

# full_bls_flow.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import os
import re


def solve_bls_with_selenium(url, form_data=None):
    """Complete BLS CAPTCHA flow using Selenium."""
    driver = webdriver.Chrome()
    driver.get(url)

    wait = WebDriverWait(driver, 15)

    # Fill any form fields before CAPTCHA
    if form_data:
        for field_id, value in form_data.items():
            el = wait.until(EC.presence_of_element_located((By.ID, field_id)))
            el.clear()
            el.send_keys(value)

    # Extract CAPTCHA parameters
    captcha_container = wait.until(
        EC.presence_of_element_located((By.CSS_SELECTOR, "[data-sitekey], .bls-captcha"))
    )
    sitekey = captcha_container.get_attribute("data-sitekey")

    # Get instructions
    instructions = None
    try:
        inst_el = driver.find_element(By.CSS_SELECTOR, ".captcha-instructions")
        instructions = inst_el.text.strip()
    except Exception:
        pass

    # Solve via API
    solution = solve_bls(
        sitekey=sitekey,
        pageurl=driver.current_url,
        instructions=instructions,
    )

    # Inject solution
    driver.execute_script("""
        var input = document.querySelector('input[name="captcha-response"], #captcha-response');
        if (input) {
            input.value = arguments[0];
        } else {
            var hidden = document.createElement('input');
            hidden.type = 'hidden';
            hidden.name = 'captcha-response';
            hidden.value = arguments[0];
            document.forms[0].appendChild(hidden);
        }
    """, solution)

    # Submit form
    submit_btn = driver.find_element(By.CSS_SELECTOR, "button[type='submit'], #submit")
    submit_btn.click()

    # Wait for confirmation
    wait.until(EC.url_changes(url))
    result_url = driver.current_url
    driver.quit()

    return result_url

生产环境使用建议

跑通 demo 之后,上线前建议再检查几点:

  • 轮询间隔别低于 5 秒,压得太紧容易触发限流
  • solve_bls 包一层重试逻辑,遇到网络抖动或超时就退避重试,而不是直接抛异常中断整个流程
  • timeout 按目标站点的实际响应速度调整,默认 30 秒能覆盖大多数场景,响应慢的站点适当调大
  • 出错时把 task_id 一起打进日志,方便回头定位具体是哪次提交失败

常见问题

instructions 参数必须每次都传吗?

不是必须的。挑战文字单独展示、不嵌在图片里时,传上能明显减少误判。

没有 code 参数会影响识别成功率吗?

一般不会,它只在部分实现区分挑战子类型时才有意义,提取不到就不传。

提取不到 sitekey 该怎么排查?

先确认验证码是否异步加载,是的话用 WebDriverWait 等元素出现后再提取,再核对选择器是否匹配真实结构。

常见报错与排查

问题 原因 处理方式
ERROR_BAD_PARAMETERS 缺少 sitekeypageurl 检查两个必填字段是否都提取到了值
结果被判定为错误 挑战含糊,未传 instructions 补上 instructions,尤其是文字提示单独展示的挑战
识别出的类型不对 目标其实不是 BLS CAPTCHA 确认不是 reCAPTCHA 或站点自定义组件
提取不到 sitekey 验证码动态加载 显式等待元素渲染完成后再提取

相关指南

instructionscode 用对,是 BLS CAPTCHA 识别准确率的关键一步——从 CaptchaAI 开始

该文章已禁用评论。