验证码识别是一个刚需——自动化测试、数据采集、安全审计都绕不开。Tesseract 太重、百度 OCR API 要钱。ddddocr 是个纯 Python 的 OCR 库,轻量、免费、专攻验证码。这篇把它包装成一个异步 HTTP 服务,一行 curl 搞定验证码识别。
ddddocr 是什么
ddddocr(带带弟弟 OCR)是一个专门为验证码设计的识别库:
- 纯 Python,无需安装 Tesseract
- 不依赖 GPU,CPU 推理
- 支持英文+数字验证码、滑块验证码
- 一行代码识别:
ocr.classification(img_bytes)
1
| pip install ddddocr aiohttp
|
单文件版本(最简单的 API)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| import ddddocr from aiohttp import web
ocr = ddddocr.DdddOcr()
async def handle_ocr(request): """接收图片,返回 OCR 识别结果""" try: img_bytes = await request.read()
if not img_bytes: return web.json_response({'error': '未提供图片'}, status=400)
result = ocr.classification(img_bytes)
return web.json_response({ 'success': True, 'result': result, })
except Exception as e: return web.json_response({'error': str(e)}, status=500)
async def handle_health(request): """健康检查""" return web.json_response({'status': 'ok'})
app = web.Application() app.router.add_post('/ocr', handle_ocr) app.router.add_get('/health', handle_health)
if __name__ == '__main__': web.run_app(app, host='0.0.0.0', port=9898)
|
使用方式
1 2 3 4 5 6 7 8 9 10 11 12
| curl -X POST http://localhost:9898/ocr \ --data-binary @captcha.png
{"success": true, "result": "aB3x"}
import requests with open('captcha.png', 'rb') as f: resp = requests.post('http://localhost:9898/ocr', data=f.read()) print(resp.json()['result'])
|
增强版:批量识别 + Base64 支持
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| import base64 import io from PIL import Image
async def handle_ocr_enhanced(request): """支持 URL 图片和 Base64 图片""" data = await request.json()
img_bytes = None
if 'base64' in data: b64_str = data['base64'] if ',' in b64_str: b64_str = b64_str.split(',')[1] img_bytes = base64.b64decode(b64_str)
elif 'url' in data: import aiohttp async with aiohttp.ClientSession() as session: async with session.get(data['url'], timeout=10) as resp: img_bytes = await resp.read()
if not img_bytes: return web.json_response({'error': '请提供 base64 或 url'}, status=400)
result = ocr.classification(img_bytes)
return web.json_response({ 'success': True, 'result': result, })
async def handle_batch_ocr(request): """批量识别""" data = await request.json() images = data.get('images', [])
results = [] for img in images: try: img_bytes = None if 'base64' in img: b64 = img['base64'].split(',')[-1] img_bytes = base64.b64decode(b64) elif 'url' in img: pass
if img_bytes: result = ocr.classification(img_bytes) results.append({'success': True, 'result': result}) else: results.append({'success': False, 'error': '无效图片'}) except Exception as e: results.append({'success': False, 'error': str(e)})
return web.json_response({'results': results})
|
生产环境部署
Gunicorn 启动(不支持 aiohttp)
ddddocr 的 aiohttp 服务无法直接用 Gunicorn 启动。两种方案:
方案一:直接用 aiohttp 生产运行
方案二:改用 Flask + 多进程 Gunicorn
1 2 3 4 5 6 7 8 9 10 11 12 13
| from flask import Flask, request, jsonify import ddddocr
app = Flask(__name__) ocr = ddddocr.DdddOcr()
@app.route('/ocr', methods=['POST']) def ocr_api(): img_bytes = request.get_data() if not img_bytes: return jsonify({'error': '未提供图片'}), 400 result = ocr.classification(img_bytes) return jsonify({'success': True, 'result': result})
|
1 2
| pip install gunicorn gunicorn -w 4 -b 0.0.0.0:9898 app:app
|
Dockerfile
1 2 3 4 5 6 7 8 9
| FROM python:3.11-slim
WORKDIR /app RUN pip install ddddocr flask gunicorn
COPY app.py . EXPOSE 9898
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:9898", "app:app"]
|
ddddocr 的局限性
| 限制 |
表现 |
应对 |
| 复杂验证码 |
汉字、扭曲严重的不行 |
用打码平台或训练专用模型 |
| 滑块验证码 |
需用 slide_match 方法 |
ocr.slide_match(target, background) |
| 不定长验证码 |
可能漏识别或多识别 |
设置 show_ad=False 关闭广告检测 |
| 并发性能 |
单实例 QPS 约 20-50 |
多进程 Gunicorn 或多个实例 |
实用技巧:预处理提升识别率
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| from PIL import Image, ImageFilter
def preprocess_for_ocr(img_bytes): """对验证码图片做预处理""" img = Image.open(io.BytesIO(img_bytes))
img = img.convert('L')
img = img.point(lambda x: 0 if x < 128 else 255, '1')
img = img.convert('RGB')
buf = io.BytesIO() img.save(buf, format='PNG') return buf.getvalue()
preprocessed = preprocess_for_ocr(raw_bytes) result = ocr.classification(preprocessed)
|
总结
ddddocr 是目前最简单的 Python 验证码识别方案——零配置、零依赖(不需要 Tesseract 或 GPU)、一行代码出结果。包装成 HTTP 服务后,任何语言都可以一行 curl 调用。