API Tutorials

图像验证码 Base64 编码最佳实践

调用 CaptchaAI 图片验证码接口返回 ERROR_WRONG_FILE_EXTENSION,或识别结果总是错?问题多半不在图片,而在 base64 编码这一步。

CaptchaAI 只接受“纯” base64——不带 data URI 前缀,不重复编码,不能用文本模式读二进制文件。排查顺序建议按下面这张单子走:

  • 提交格式对不对——有没有多带 data:image/... 前缀
  • 图片来源是文件、URL 还是 Selenium 截图,编码方式各不相同
  • 有没有踩中重复编码、文本模式读文件这两个高频坑
  • 提交前有没有跑一遍自查函数

Base64 提交格式:CaptchaAI 到底要什么样的数据

CaptchaAI 通过 method=base64 参数接收图片验证码,body 字段就是编码后的字符串本身,不需要额外包一层 JSON,也不需要加任何前缀:

import requests
import base64
import os


def submit_image_captcha(image_base64):
    """Submit base64-encoded image to CaptchaAI."""
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": os.environ["CAPTCHAAI_API_KEY"],
        "method": "base64",
        "body": image_base64,
        "json": 1,
    }, timeout=30)
    return resp.json()

格式怎么选:PNG、JPEG、GIF、WEBP

先定格式,再看下面三种获取方式:

格式 最适合 体积 画质
PNG 文本验证码、页面截图 较大 无损
JPEG 基于照片的验证码 较小 有损(建议质量 ≥ 85)
GIF 动画验证码 不固定 色彩数量有限
WEBP 现代浏览器场景 最小 画质不错

建议: 文本验证码优先用 PNG,只有验证码本身是照片时 JPEG 才划算。


方法一:读取本地图片文件并编码

最常见场景:图片已经落盘。用二进制模式打开文件,直接编码即可:

# from_file.py
import base64


def encode_from_file(filepath):
    """Read an image file and return base64 string."""
    with open(filepath, "rb") as f:
        raw = f.read()
    return base64.b64encode(raw).decode("ascii")


# Usage
b64 = encode_from_file("captcha.png")
print(f"Encoded length: {len(b64)} chars")

方法二:直接从 URL 下载并编码

不少站点的验证码图片是独立 URL,不用先落盘。用 requests 拿到响应内容直接编码,顺带校验 Content-Type,避免把 404 页面当图片提交:

# from_url.py
import requests
import base64


def encode_from_url(image_url):
    """Download image and return base64 string."""
    resp = requests.get(image_url, timeout=15)
    resp.raise_for_status()

    # Verify it's actually an image
    content_type = resp.headers.get("Content-Type", "")
    if not content_type.startswith("image/"):
        raise ValueError(f"Not an image: {content_type}")

    return base64.b64encode(resp.content).decode("ascii")


# Usage
b64 = encode_from_url("https://example.com/captcha.png")

方法三:从 Selenium 截图编码

验证码是页面 DOM 元素、没有独立图片 URL 时,对元素截图更稳。screenshot_as_base64 已经是 base64,不用再编码;只有裁剪整页截图时才需要 Pillow(国内网络建议加镜像:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pillow):

# from_selenium.py
import base64
from selenium.webdriver.common.by import By


def encode_from_element(driver, selector):
    """Screenshot a specific element and return base64."""
    element = driver.find_element(By.CSS_SELECTOR, selector)
    screenshot_b64 = element.screenshot_as_base64
    return screenshot_b64


def encode_from_page_crop(driver, selector):
    """Crop a specific region from the page screenshot."""
    from PIL import Image
    import io

    element = driver.find_element(By.CSS_SELECTOR, selector)
    location = element.location
    size = element.size

    # Full page screenshot
    png = driver.get_screenshot_as_png()
    img = Image.open(io.BytesIO(png))

    # Crop to element bounds
    left = location["x"]
    top = location["y"]
    right = left + size["width"]
    bottom = top + size["height"]
    cropped = img.crop((left, top, right, bottom))

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

3 个最容易踩的编码坑

下面三个问题覆盖了 base64 提交失败的大多数原因,按出现频率排列。

坑 1:多带了 data URI 前缀

浏览器 <img>src、Canvas 的 toDataURL() 返回的字符串通常带 data:image/png;base64, 前缀,CaptchaAI 只要逗号后面的部分:

# WRONG — includes data URI prefix
bad = "data:image/png;base64,iVBORw0KGgo..."

# RIGHT — raw base64 only
good = "iVBORw0KGgo..."

# Fix: Strip the prefix
def clean_base64(b64_string):
    if "," in b64_string:
        return b64_string.split(",", 1)[1]
    return b64_string

坑 2:重复编码

screenshot_as_base64 已经是编码结果,再调用一次 base64.b64encode(),CaptchaAI 收到的就是无法解析的乱码:

# WRONG — encoding an already-encoded string
already_b64 = element.screenshot_as_base64
double_encoded = base64.b64encode(already_b64.encode()).decode()  # BAD

# RIGHT — use as-is
correct = element.screenshot_as_base64  # Already base64

坑 3:用文本模式读二进制文件

"r" 模式打开图片文件,字节会被当作文本解码,原始数据直接损坏。必须用 "rb"

# WRONG — reading as text
with open("captcha.png", "r") as f:  # Text mode
    content = f.read()  # Corrupted binary data

# RIGHT — reading as bytes
with open("captcha.png", "rb") as f:  # Binary mode
    content = f.read()
encoded = base64.b64encode(content).decode("ascii")

提交前自查:一个校验函数搞定

与其等报错再排查,不如提交前先自查——检查前缀、能否解码、体积是否超限、文件头是否匹配已知格式:

# validate.py
import base64
import io


def validate_captcha_image(b64_string):
    """Validate base64 image before submitting to CaptchaAI."""
    errors = []

    # Check for data URI prefix
    if b64_string.startswith("data:"):
        errors.append("Contains data URI prefix — strip it")
        b64_string = b64_string.split(",", 1)[1]

    # Try decoding
    try:
        decoded = base64.b64decode(b64_string)
    except Exception as e:
        return {"valid": False, "errors": [f"Invalid base64: {e}"]}

    # Check size
    size_kb = len(decoded) / 1024
    if size_kb < 1:
        errors.append(f"Image too small ({size_kb:.1f} KB) — likely corrupt")
    if size_kb > 500:
        errors.append(f"Image large ({size_kb:.1f} KB) — consider resizing")

    # Check image format
    if decoded[:8] == b'\x89PNG\r\n\x1a\n':
        fmt = "PNG"
    elif decoded[:3] == b'\xff\xd8\xff':
        fmt = "JPEG"
    elif decoded[:4] == b'GIF8':
        fmt = "GIF"
    elif decoded[:4] == b'RIFF':
        fmt = "WEBP"
    else:
        errors.append("Unknown image format")
        fmt = "unknown"

    return {
        "valid": len(errors) == 0,
        "format": fmt,
        "size_kb": round(size_kb, 1),
        "errors": errors,
    }


# Usage
result = validate_captcha_image(b64_string)
if not result["valid"]:
    print(f"Issues: {result['errors']}")
else:
    print(f"Valid {result['format']}, {result['size_kb']} KB")

报错排查对照表

报错 原因 处理方式
ERROR_WRONG_FILE_EXTENSION base64 数据无效,多半是编码方式错了 先用 validate_captcha_image() 自查
ERROR_TOO_BIG_CAPTCHA_FILESIZE 图片超过 600 KB 编码前先压缩或缩小尺寸
ERROR_ZERO_CAPTCHA_FILESIZE 图片为空或已损坏 检查下载/截图步骤是否真的成功
识别结果不对 JPEG 压缩过度,字符边缘发糊 换成 PNG,或把 JPEG 质量调到 ≥ 85

常见问题

base64 编码的图片超过 600 KB 会怎样?

CaptchaAI 会返回 ERROR_TOO_BIG_CAPTCHA_FILESIZE。截图类验证码经常超限,编码前先缩放或压缩,控制在 600 KB 以内。

为什么截图编码之后,识别成功率反而下降了?

最常见原因是重复编码——screenshot_as_base64 已经是 base64,再包一层 base64.b64encode() 就会得到乱码。其次是 JPEG 压缩过度让字符边缘变糊,建议改用 PNG。

文本验证码该用 PNG 还是 JPEG?

优先用 PNG。JPEG 的有损压缩会模糊字符边缘,影响识别准确率;PNG 无损,能保留精确像素。

ERROR_WRONG_FILE_EXTENSION 具体是哪里出的错?

九成情况是字符串里还带着 data:image/png;base64, 前缀,或者读取图片时用了文本模式("r")而非二进制模式("rb")。提交前跑一遍 validate_captcha_image() 就能提前发现。


相关指南


把 base64 编码这一步做对,从 CaptchaAI 开始识别图片验证码

该文章已禁用评论。