复杂验证码防的是普通 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,接口直接返回计算结果。
提交前要不要先对图片做预处理?
不是必须的,只有噪声很重或对比度很低时才明显有效。
minLen 和 maxLen 具体怎么用?
限定字符长度范围——已知验证码 4-8 位,就设 minLen=4、maxLen=8。
同一类验证码反复识别失败,该怎么办?
用 reportbad 反馈,并补充更具体的 textinstructions。
相关指南
用 CaptchaAI 识别复杂图像验证码——立即开始。