QQ 图片 OCR 为什么一直失败:从 PaddleOCR 到反盗链的完整排查

问题

7 月 27 号凌晨,噗噗(我的 QQ Bot)突然开始对图片消息“视而不见”。

症状:

  • 群里发了图片,OCR 返回空
  • 偶尔返回乱码,像是识别了 HTML 而不是图片
  • daemon.log 里 PaddleOCR 报错 ModuleNotFoundError: paddleocr

第一层:PEP 668 隔离

ModuleNotFoundError: No module named 'paddleocr'

PaddleOCR 装在 .test-venv 里,daemon 跑在 /usr/bin/python3(系统 Python)。PEP 668 禁止系统 pip 装包,所以 daemon 根本看不到 paddleocr。

修复ocr.py 引导 sys.path

# 优先 NAPCAT_OCR_SITE_PACKAGES,其次 VIRTUAL_ENV,最后 .test-venv
for p in [os.environ.get("NAPCAT_OCR_SITE_PACKAGES"),
          os.environ.get("VIRTUAL_ENV"),
          os.path.join(REPO_ROOT, ".test-venv", "lib", "python3.11", "site-packages")]:
    if p and os.path.isdir(p):
        sys.path.insert(0, p)

第二层:反盗链

OCR 能启动了,但识别结果还是乱码。

排查发现:urllib.urlretrieve 下载 QQ 多媒体 URL 时,返回的是反盗链 HTML 页面(几 KB),不是真正的图片。PaddleOCR 识别的是 HTML 文本。

修复_download_image() 加浏览器 UA + Referer:

req = urllib.request.Request(url, headers={
    "User-Agent": "Mozilla/5.0 ...",
    "Referer": "https://m.qzone.qq.com/",
})

验证:下载 66KB PNG,OCR 正常识别。

第三层:wake prompt 里的裸数字

修复 OCR 的同时,顺手把 wake prompt 里的裸数字都标了字段名:

# 之前
NEW_FRIEND: 12345678

# 之后
NEW_FRIEND: user_id=12345678

所有 reason 分支(BOT_BANNED、GROUP_ADMIN_CHANGE、NEW_POKE 等)都加了字段名。

数据

commit a9c40db + 126dc8a

  • ocr.py 85 行重写
  • watch.py 10 行修改
  • 验证:系统 Python 下 PaddleOCR 正常加载,图片识别准确率恢复

经验

  1. PEP 668 是真实的坑 —— 系统 Python 和 venv 的包隔离不是“建议”,是强制
  2. QQ 图片有反盗链 —— 直接下载会拿到 HTML,必须带 UA + Referer
  3. OCR 失败时先验证输入 —— 识别乱码时第一反应应该是“输入是不是图片”

🎵 Listening companion