python

轻量级 OCR 服务搭建:ddddocr + 异步 HTTP 接口

2026-07-06 #渗透测试#python

验证码识别是一个刚需——自动化测试、数据采集、安全审计都绕不开。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
# index.py
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)

# OCR 识别
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"}

# Python 调用
import requests
with open('captcha.png', 'rb') as f:
resp = requests.post('http://localhost:9898/ocr', data=f.read())
print(resp.json()['result']) # aB3x

增强版:批量识别 + 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

# 方式 1: Base64 编码的图片
if 'base64' in data:
b64_str = data['base64']
# 去掉可能的 data:image/png;base64, 前缀
if ',' in b64_str:
b64_str = b64_str.split(',')[1]
img_bytes = base64.b64decode(b64_str)

# 方式 2: 图片 URL
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)

# OCR 识别
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', []) # [{"base64": "..."}, {"url": "..."}]

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:
# ... URL 下载
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 生产运行

1
2
python index.py
# 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))

# 1. 灰度化
img = img.convert('L')

# 2. 二值化(去掉噪点)
img = img.point(lambda x: 0 if x < 128 else 255, '1')

# 3. 转回 RGB(ddddocr 期望 RGB)
img = img.convert('RGB')

# 4. 回写字节
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 调用。

评论
分享