API Tutorials

多字符图像验证码解决策略

复杂验证码防的是普通 OCR:字符粘连、扭曲变形、多字体、背景噪声,都是冲着固定分割的识别方案去的。政务、教务等老牌国内站点至今仍常见这类验证码。

CaptchaAI 换了思路——base64 提交图片,配合 hints 参数说明字符特征,准确率通常比裸提交高不少。下面按类型拆解,附 Python 示例。

测试素材请用你自己拥有或已获授权的账号与站点,不要针对未授权的第三方表单批量测试。


常见复杂图像验证码类型与识别难度

先分清验证码类型,再决定要不要加 hints:

类型 说明 难度
干净文字 无形变,字体统一 简单
扭曲文字 字符旋转、缩放 中等
字符粘连 字符重叠或接触 较难
多字体混排 每字符不同字体 较难
噪声+干扰线 背景噪点、删除线 中等
颜色变化 每字符不同颜色 中等
算术表达式 数字+运算符,求计算结果 中等

如何提交复杂验证码:base64 + hints 参数

标准的多字符验证码,通过 base64 提交图片并附带合适的 hints 参数即可:

import requests
import base64
import time
import os

API_KEY = os.environ["CAPTCHAAI_API_KEY"]


def solve_complex_image(image_b64, hints=None):
    """Solve a complex multi-character image CAPTCHA."""
    payload = {
        "key": API_KEY,
        "method": "base64",
        "body": image_b64,
        "json": 1,
    }

    if hints:
        payload.update(hints)

    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"]

    time.sleep(8)
    for _ in range(24):
        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("Solve timeout")

hints 的每个字段对应一种验证码特征,接口按特征针对性处理。


什么时候该加 hints,什么时候不用

不是每次提交都需要 hints

判断标准很简单:

  • 干净文字、颜色单一、没有粘连——直接裸提交即可,加 hints 反而多一次调参成本。
  • 出现粘连、多字体、大小写敏感或明显噪声——加对应 hints 字段,准确率提升更明显。
  • 同一站点反复识别错——先加 textinstructions 描述具体特征,而不是急着加 minLen/maxLen

下面按三种常见复杂场景拆解具体用法。


场景一:字符粘连怎么处理

字符粘连(字母彼此重叠、笔画连在一起)对普通 OCR 是噩梦,对 CaptchaAI 只是一个 hint 的事:

def solve_connected_letters(image_path):
    """Solve CAPTCHA with connected/overlapping characters."""
    with open(image_path, "rb") as f:
        b64 = base64.b64encode(f.read()).decode("ascii")

    return solve_complex_image(b64, hints={
        "textinstructions": "Characters may be connected or overlapping",
        "minLen": 4,
        "maxLen": 8,
    })

textinstructions 说明字符可能粘连。

配合 minLen/maxLen 限定长度,减少误判。

适用信号:

  • 相邻字符笔画相连、边界模糊
  • 已知验证码长度范围,能给出 minLen/maxLen

场景二:大小写混合 + 背景噪声

def solve_noisy_mixed(image_path):
    """Solve CAPTCHA with background noise and mixed case."""
    with open(image_path, "rb") as f:
        b64 = base64.b64encode(f.read()).decode("ascii")

    return solve_complex_image(b64, hints={
        "regsense": 1,         # Case-sensitive
        "language": 2,         # Latin characters
        "textinstructions": "Ignore background lines and noise",
    })

regsense 保留大小写,language 指定字符集。

textinstructions 说明忽略背景噪声。

适用信号:

  • 大小写混排且大小写本身是答案的一部分
  • 背景有干扰线、噪点或水印

场景三:多字体、多样式文本

def solve_multi_font(image_path):
    """Solve CAPTCHA using multiple fonts per character."""
    with open(image_path, "rb") as f:
        b64 = base64.b64encode(f.read()).decode("ascii")

    return solve_complex_image(b64, hints={
        "textinstructions": "Each character may use a different font or style",
        "minLen": 5,
        "maxLen": 7,
    })

字体逐字符变化时也不用额外处理。

textinstructions 说明这一点即可。


进阶技巧:预处理图片,提高图像验证码识别准确率

提交前对图片做一次预处理,有时能进一步提升准确率:

# preprocess.py
from PIL import Image, ImageFilter, ImageEnhance
import io
import base64


def preprocess_for_ocr(image_path):
    """Preprocess image to improve OCR accuracy."""
    img = Image.open(image_path)

    # Convert to grayscale
    img = img.convert("L")

    # Increase contrast
    enhancer = ImageEnhance.Contrast(img)
    img = enhancer.enhance(2.0)

    # Sharpen
    img = img.filter(ImageFilter.SHARPEN)

    # Binarize (threshold)
    threshold = 128
    img = img.point(lambda p: 255 if p > threshold else 0)

    # Encode back to base64
    buffer = io.BytesIO()
    img.save(buffer, format="PNG")
    return base64.b64encode(buffer.getvalue()).decode("ascii")

灰度化、对比度增强、锐化、二值化,这几步做完基本够用。

对噪声重、对比度低的图片,提升最明显。


失败重试与结果反馈

# retry_strategy.py


def solve_with_retry(image_b64, hints, max_retries=3):
    """Retry solving with fallback strategies."""
    strategies = [
        hints,                                          # Original hints
        {**hints, "textinstructions": ""},              # Without instructions
        {**hints, "numeric": 0, "regsense": 0},        # Relaxed constraints
    ]

    for i, strategy in enumerate(strategies[:max_retries]):
        try:
            result = solve_complex_image(image_b64, strategy)
            return {"text": result, "strategy": i, "success": True}
        except RuntimeError:
            continue

    return {"text": None, "strategy": -1, "success": False}


def report_bad_answer(task_id):
    """Report incorrect answer for quality feedback."""
    requests.get("https://ocr.captchaai.com/res.php", params={
        "key": API_KEY,
        "action": "reportbad",
        "id": task_id,
    }, timeout=10)

单一策略失败不代表识别不了。

去掉 textinstructions 或放宽约束,往往能换个思路成功。


故障排查对照表

遇到识别不准,先对照下表定位问题类型。

再决定加哪个 hint 字段,比盲目重试更快:

问题 原因 处理方式
缺字符 粘连字符被识别少了 补充 textinstructions 说明粘连
多字符 噪声被当成了文字 提交前先降噪预处理
大小写错误 大小写没有保留 设置 regsense=1
算术结果被当成表达式返回 缺少 calc=1 开启计算模式
同一站点反复出错 该站点用了特殊字体 reportbad 反馈

小贴士:优先用 reportbad 反馈错误结果,再叠加更具体的 textinstructions——两步一起调整,比一次性加满所有 hints 字段更容易定位到底是哪个特征在干扰识别。


常见问题

算术类验证码(如 3+5=?)需要额外设置什么?

加上 calc=1,接口直接返回计算结果。

提交前要不要先对图片做预处理?

不是必须的,只有噪声很重或对比度很低时才明显有效。

minLenmaxLen 具体怎么用?

限定字符长度范围——已知验证码 4-8 位,就设 minLen=4maxLen=8

同一类验证码反复识别失败,该怎么办?

reportbad 反馈,并补充更具体的 textinstructions


相关指南


用 CaptchaAI 识别复杂图像验证码——立即开始

该文章已禁用评论。