
1. 为什么是pytesseract验证码识别入门的第一选择很多做自动化测试和内部工具开发的朋友第一次接触验证码识别都是被同一个问题逼来的自动化流程跑得好好的一遇到带验证码的登录页就卡壳。手动输入吧脚本就谈不上全自动不处理验证码吧流程永远断在半路。我最早也是被这个问题折腾了很久最后选了 pytesseract 这条性价比最高的路。它是 Python 里调用 Tesseract OCR 引擎最常用的封装库几行代码就能把图片里的字符转成文本足够应付大多数不复杂的验证码场景。1.1 先搞清楚验证码识别到底在解决什么问题验证码的本质是网站用来区分操作者是人还是程序的一道关卡。常见的形态有四位数字、数字加字母、彩色噪点背景、扭曲变形字符等。我们做自动化测试或者写内部效率工具时经常需要在测试环境反复登录、批量录入数据这时候验证码就成了一道绕不过去的坎。注意我说的是测试环境和自有系统——自己开发的登录页、公司内网测试账号、或者公开的 OCR 练习数据集这些都是合法授权的场景。至于绕过别人系统的风控那属于另一码事不在本文讨论范围内。在这个前提下验证码识别要解决的核心问题其实很朴素给定一张验证码图片把它里面的字符准确地提取成纯文本。难点不在识别两个字而在验证码图片本身就是被人为设计来对抗自动识别的。干扰线、噪点、字符粘连、字体变形招数很多。所以真正决定识别率高低的地方往往不是调用识别库那几行代码而是图片预处理做到什么程度。1.2 pytesseract 在 OCR 生态里的位置Python 的 OCR 生态我去翻过一圈大概分三派一派是 Tesseract 这种传统 OCR 引擎通过 pytesseract 封装调用优点是部署简单、不需要 GPU、适合干净规整的印刷体文本另一派是深度学习方法比如训练一个 CNN 或者用 CRNN 做端到端识别效果上限高但需要数据集、训练时间对入门者不太友好还有一派是国内的验证码专用库比如 ddddocr开箱即用对很多复杂验证码的效果比 pytesseract 好但自定义空间小属于黑盒方案。pytesseract 的定位我理解成中间路线。它足够简单pip 装好库、装好引擎就能跑也足够透明图片预处理、识别参数、字符范围全都可以自己控制。我在遇到简单数字验证码时它基本能做到一次通过调试成本最低。如果你已经在用 PIL 或者 OpenCV 做图像处理那么 pytesseract 接入成本几乎为零因为它的输入就是 PIL 的 Image 对象输出就是字符串。1.3 适合 pytesseract 的场景和不适合的场景先说适合的四位纯数字、四位数字加字母、背景干净或者噪声不多、字符没有严重扭曲的验证码这类是 pytesseract 的主场。还有一类常见场景是识别截图里的印刷体文本——比如自动提取票据上的编号、识别软件界面上固定位置的文字这本质上也属于 OCR逻辑一样。不适合的也很明确字符严重扭曲粘连、带复杂背景纹理、字体经过专门反 OCR 设计的验证码pytesseract 的识别率会断崖式下跌。这种时候别硬扛要么换专用识别库要么上深度学习方案。我在第五节会给出实测对比你能直观看到它的能力边界在哪里。先有个预期pytesseract 不是万能的但它是把简单问题快速解决掉的最好工具。2. 环境搭建里的那些坑装好不等于能用环境搭建这一步我能写的经验比想象中多。很多人以为pip install pytesseract就完事了结果一运行报TesseractNotFoundError然后开始怀疑人生。这里面的逻辑我在踩过几次坑之后才彻底弄明白pytesseract 只是一个封装层它本身不包含任何 OCR 能力真正干活的 Tesseract 引擎需要单独安装。2.1 安装 pytesseract 库简单但只是开头先看最简单的部分安装 Python 库pip install pytesseract同时在代码里导入也需要用到 Pillow因为 pytesseract 识别的是 PIL 的 Image 对象pip install Pillow如果是在 VSCode 里跑脚本注意看右下角或者状态栏当前选的是哪个 Python 解释器。我遇到过不止一次终端里pip install装进了 A 环境VSCode 里跑脚本用的是 B 环境结果怎么 import 都报 ModuleNotFoundError。这个问题排查起来很隐蔽先python --version确认路径再用pip show pytesseract看装到了哪里基本就能定位。2.2 Tesseract 引擎安装与 PATH 配置接下来是关键部分。Windows 用户去 GitHub 上找 Tesseract 的官方 Windows 安装包一般用 UB Mannheim 那个维护版本安装的时候注意勾选 Additional language data后面要装简体中文包的话也得在这里一并选上。Linux 用户更简单sudo apt install tesseract-ocr装完之后打开命令行输入tesseract --version能输出版本信息就说明引擎本体已经就位。如果你输入之后提示不是内部或外部命令也不是可运行的程序或批处理文件那和你在 cmd 里敲py报同样的错本质是一回事——程序没在 PATH 里。Windows 下可以手动把 Tesseract 的安装目录加到系统环境变量 Path也可以干脆在代码里写死路径import pytesseract pytesseract.pytesseract.tesseract_cmd rC:\Program Files\Tesseract-OCR\tesseract.exe我建议代码里显式指定路径。为什么因为后续你要是把脚本发给同事跑或者部署到别的机器环境变量往往不可控代码里写死反而可预期。当然路径写死也要考虑不同机器安装位置可能不一样最稳妥的是先在代码里检测import shutil import pytesseract tesseract_path shutil.which(tesseract) if tesseract_path: pytesseract.pytesseract.tesseract_cmd tesseract_path else: pytesseract.pytesseract.tesseract_cmd rC:\Program Files\Tesseract-OCR\tesseract.exe这个写法先用系统 PATH 找找不到再用默认路径兜底两条路都堵死的话再报错说引擎没装体验会好很多。2.3 语言包与版本验证安装完引擎之后强烈建议先跑两条命令确认环境完整tesseract --version tesseract --list-langs第二条命令会列出当前可用的语言包。如果你主要识别英文数字那么eng必须存在。如果需要识别中文验证码或者中文图片得装语言包。Windows 安装包在安装时勾选 Chinese 相关选项就行Linux 下执行sudo apt install tesseract-ocr-chi-sim我在实际项目里遇到过这样的问题识别纯数字验证码明明图片很清楚结果输出一串乱码。后来发现是默认语言包不对某些版本没装eng导致回退逻辑出错。所以每次新配环境--list-langs是我必跑的一条命令看到eng在列表里才算安心。2.4 VSCode 里跑脚本前先想清楚解释器前面说到了 VSCode这里展开讲一下。VSCode 装 Python 扩展之后默认会用右下角选中的解释器来运行代码。很多新手在 VSCode 的终端里装包装的是系统 Python但扩展默认选中的可能是某个虚拟环境两边不一致就会出问题。正确做法是在 VSCode 里按CtrlShiftP选择Python: Select Interpreter找到你安装 pytesseract 的那个环境。这个步骤不花两分钟但能省下半天排查时间。3. 从一张验证码图到文本完整的 OCR 流水线现在环境已经通了我们正式进入识别流水线。完整流程可以拆成四步读图、预处理、调参识别、后处理。每一步都有讲究我把代码和背后的原理一起讲清楚。3.1 三行代码能识别什么不能识别什么先看最原始的写法import pytesseract from PIL import Image image Image.open(captcha.png) text pytesseract.image_to_string(image) print(text)如果你拿一张非常干净的印刷体文字图片来测这三行代码效果还不错。但拿验证码来测大概率是空字符串或者一堆乱码。原因不复杂Tesseract 的设计目标是识别扫描文档、书刊等规整文本它内部有一整套针对印刷体的版面分析逻辑。而验证码图片通常尺寸小、字符排列紧凑、背景还有干扰直接丢给引擎等于让一个习惯读整页书的阅读器去认一张被涂花的小纸条效果可想而知。所以我把这个最简单的版本定义为连通性测试它只用来验证环境没问题。真正要解决识别问题重心必须放在预处理上。预处理的目的说白了只有一句话把验证码变成 Tesseract 最能理解的形式——黑底白字或者白底黑字、字符尽量粗壮、背景尽量纯净。3.2 预处理才是真正的胜负手我见过太多人在调用参数上死磕却忽视了预处理。实际上对于 pyteserract 这类传统 OCR 引擎预处理对识别率的影响远大于参数调整。一张带灰色噪点背景的验证码不做处理直接识别可能是 20% 的成功率做一遍灰度加二值化立刻能跳到 60% 以上。这是我在大量实测里反复验证过的结论。预处理的典型链路是灰度化、二值化、去噪、放大或形态学处理。每一步都有明确目的。灰度化是为了丢掉颜色信息因为 Tesseract 最终只需要明暗关系二值化是把灰度图变成纯黑纯白消除颜色深浅带来的干扰去噪是去掉零散的小点和小块放大则是把小尺寸验证码放大两倍让字符笔画更清晰。这四个操作组合起来绝大多数简单验证码都能被洗干净。3.3 灰度、二值化与去噪的标准组合先用纯 PIL 实现一套最基础的预处理流程from PIL import Image, ImageFilter, ImageOps def preprocess_pil(image_path): img Image.open(image_path) # 1. 灰度化 gray img.convert(L) # 2. 自动对比度增强 gray ImageOps.autocontrast(gray) # 3. 放大两倍让字符笔画更清晰 w, h gray.size gray gray.resize((w * 2, h * 2), Image.LANCZOS) # 4. 二值化大于阈值的变白小于等于阈值的变黑 threshold 150 binary gray.point(lambda p: 255 if p threshold else 0) # 5. 中值滤波去噪 binary binary.filter(ImageFilter.MedianFilter(size3)) return binary这段代码有几个细节值得说。ImageOps.autocontrast会自动拉伸灰度范围这个操作对偏暗、偏亮的验证码特别管用等于先帮图片把对比度拉开后面二值化阈值才好选。放大这一步是我后来才意识到的关键很多验证码原始尺寸只有 60x20 像素字符总共就十几个像素宽Tesseract 根本看不清放大两倍之后字符特征就明显了。二值化阈值 150 不是固定值你可以根据图片明暗微调后面我会讲到怎么用 OTSU 自适应阈值来省掉这个人工调参。如果你想用 OpenCV效果更稳因为 OpenCV 的二值化和连通域分析功能更强大import cv2 import pytesseract def preprocess_cv2(image_path): img cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) # 放大两倍 img cv2.resize(img, None, fx2, fy2, interpolationcv2.INTER_CUBIC) # OTSU 自动阈值二值化 _, binary cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU) # 高斯模糊去噪 binary cv2.GaussianBlur(binary, (3, 3), 0) return binary注意我用的是THRESH_BINARY_INV这样字符是白色、背景是黑色。Tesseract 对白字黑底的图识别效果通常更好因为它的训练数据里白底黑字居多恰恰相反实验下来倒转有时效果反而更好这和引擎内部的二值化逻辑有关。我建议你两种都试试哪个识别率高用哪个这条经验在实操时非常有用。3.4 放大图片和形态学操作经常被忽略的杀手锏放大为什么要单独拎出来说因为它的收益太明显了。Tesseract 的字符识别依赖笔画边缘的梯度信息图片太小梯度信息就稀薄。验证码图片即便放大到 2 倍也就一百多像素宽对 OCR 来说仍然不算大但识别率往往能提升二三十个百分点。放大之后可以配合形态学操作进一步处理粘连字符。形态学操作我重点用两个膨胀和腐蚀。import cv2 import numpy as np kernel cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2)) # 腐蚀让字符变瘦断开细小的粘连 eroded cv2.erode(binary, kernel, iterations1) # 膨胀让字符变粗补上断裂的笔画 dilated cv2.dilate(binary, kernel, iterations1)腐蚀和膨胀的选择要看图片情况。字符笔画太细、断断续续的用膨胀把笔画连通字符之间靠得太近、笔画黏在一起的用腐蚀把它们分开。这两个操作就像给图片做瘦身和增肌需要根据实际识别效果来回试。我在实际项目中通常是各跑一版三版结果都识别一遍哪个通过率最高就固定用哪个。多花两分钟识别率能再上一个台阶。4. 参数调优实战识别率从 40% 提到 90% 的关键配置预处理做得再漂亮参数不对照样识别失败。pytesseract 的核心参数有三个psm、字符白名单、oem 模式。这三个参数组合得当简单验证码的识别率可以从不太能看的 40% 直接拉到 80% 甚至 90% 以上。下面一个一个讲。4.1 psm 页面分割模式告诉引擎图里是什么布局psm 全称 Page Segmentation Mode中文叫页面分割模式。它的作用是告诉 Tesseract 应该怎么理解图片的版面布局。默认模式是自动检测但自动检测对验证码这种小图并不友好它可能把一行字符当成一个文本块或多列文本导致分割错误。我常用的几个 psm 模式如下模式含义适用场景3完全自动的页面分割默认适合文档6假设是一个统一的文本块适合整段文字7假设是单行文本验证码首选8假设是一个单词单个单词识别13单行原始文本不经过版面分析速度最快验证码绝大多数是单行字符所以--psm 7是我最常用的选择。它跳过了复杂的版面分析直接把整张图按一行文本来处理识别速度和准确率都很理想。个别情况下验证码字符之间间距很大用--psm 7可能会把间距误判成单词边界输出结果里带空格这时候可以换成--psm 8试试。参数通过 pytesseract 的 config 传入text pytesseract.image_to_string(img, config--psm 7)4.2 字符白名单把答案锁死在合法范围内这是最容易被忽略但效果立竿见影的参数。大多数验证码的字符集合是有限的比如纯数字验证码只会出现 0-9四位数字加字母验证码最多也就是 0-9 加 A-Z。如果提前告诉 Tesseract你只需要从这些字符里选择答案它就不会把 0 认成 O把 1 认成 l把 8 认成 B 了。白名单用法config --psm 7 -c tessedit_char_whitelist0123456789 text pytesseract.image_to_string(img, configconfig)数字加字母则是config --psm 7 -c tessedit_char_whitelist0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ我把这招叫做把答案锁死在合法范围内。它背后的逻辑很直接Tesseract 在字符分类时会给每个可能的字符打分白名单之外的字符直接被排除它只能在白名单里选得分最高的那个。这个约束大大降低误识别率。实测中纯数字验证码用了白名单之后识别率提升幅度经常超过 20%。白名单之外还有个黑名单参数tessedit_char_blacklist用来排除绝对不会出现的字符。比如白名单你写了一大串黑名单可以补充排除某些容易误识别的字符。不过在验证码场景里白名单通常比黑名单更管用。4.3 oem 引擎模式怎么选oem 全称 OCR Engine Mode指定用哪个 OCR 引擎来干活。Tesseract 5.x 的 oem 模式有两套引擎一套是传统的 Legacy 引擎另一套是 LSTM 神经网络引擎。默认的 oem 3 会让引擎自动选择通常优先 LSTM。我之前遇到过一种情况纯数字验证码在 oem 3 下识别出O0O0这种离谱结果改成 oem 1 强制使用 LSTM 之后反而稳定了。不过这种差异不是绝对的验证码风格不同引擎偏好也不同。我建议您默认用 oem 3如果识别结果明显混乱再切换到 oem 1 对比一次config --psm 7 --oem 1 -c tessedit_char_whitelist0123456789多数场景下 oem 3 的自动选择已经够用不要在这上面花太多时间。真正重要的是预处理和白名单。4.4 一条可以直接抄走的调优模板综合前面的经验我整理了一套调优模板直接把图片路径传进去就能用import cv2 import pytesseract def recognize_captcha(image_path, whitelist0123456789): img cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) img cv2.resize(img, None, fx2, fy2, interpolationcv2.INTER_CUBIC) _, binary cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU) config f--psm 7 -c tessedit_char_whitelist{whitelist} text pytesseract.image_to_string(binary, configconfig) return text.strip().replace( , )这段代码做了三件事放大、OTSU 二值化、单行模式加白名单识别。输出时去掉首尾空白和中间空格。对于绝大多数简单验证码这个模板的一次识别成功率就能到不错的水平。如果识别失败我会基于这个模板做几个变体换 psm 8、换白名单大小写、换二值化方式各跑一遍取票数最多的结果。经验是多跑几个配置比死磕单个配置有效得多。5. 实测四种风格验证码哪些能打哪些要绕理论讲完了我用四类典型验证码做了实测分别看 pytesseract 的能力边界。这些测试图片都是我本地生成和收集的公开测试样例不涉及任何真实系统。结论应该能帮你快速判断手里的验证码属于哪一类。5.1 纯数字四位最理想的情况纯数字验证码是 pytesseract 最擅长处理的类型。四位数字、白底、字符大小均匀、无扭曲这类验证码用上面模板一次识别成功率极高。我用 100 张批量实测模板直接识别成功率大约在 92% 到 98% 之间。失败的那几个基本都是 0 和 8 这种圆润字符在低分辨率下被误判或者字符间距不均匀导致分割错误。处理这类验证码我的建议是白名单只保留数字预处理做放大加 OTSU就已经足够了。如果还嫌不够可以再加一步识别结果用正则校验一下是否真的是四位数字不是就重新识别一次。对于纯数字验证码pytesseract 完全够用没必要上深度学习。5.2 数字字母混合白名单的用武之地数字加字母的混合验证码难度会明显上一个台阶。常见问题是 0 和 O、1 和 I、8 和 B 互相混淆。这时候白名单的作用就体现出来了。比如验证码只用大写字母加数字白名单设成0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZTesseract 至少在 O 和 0 之间只用区分一个理论上还是会混淆但比没有约束时好很多。实测中这类验证码的识别率在 60% 到 80% 之间视字体和背景干扰而定。如果图片里只出现小写字母白名单就写小写如果大小写都有可能我建议统一转大写之后再比对新旧白名单减少字符类别也有助于提高识别率。另一个技巧是识别后过滤套一个正则只保留白名单里的字符其他都删掉。5.3 彩色噪点背景预处理要有选择性带彩色噪点背景的验证码是实战里最常见的类型之一。红红绿绿的小点铺满背景字符本身颜色也不固定。这种情况下直接灰度化再二值化噪点和字符会混在一起识别率很难看。关键思路是先想办法把背景噪点滤掉再进入标准流水线。我常用的手段有两种。第一种是利用颜色信息做过滤如果验证码的字符颜色和背景颜色差异较大可以先把图片转到 HSV 空间只保留字符对应的颜色区域再灰度化。第二种是连通域过滤二值化之后把面积特别小的连通区域直接抹掉。噪点通常是零散小点连通域面积远小于字符笔画按面积阈值筛选很有效。import cv2 import numpy as np def remove_noise_by_area(binary, min_area20): num_labels, labels, stats, _ cv2.connectedComponentsWithStats(binary) for i in range(1, num_labels): if stats[i, cv2.CC_STAT_AREA] min_area: binary[labels i] 0 return binary实测中这类验证码经过颜色过滤加连通域去噪后识别率能接近纯数字的水平。核心经验是预处理手段要根据背景干扰类型来选择不是标准流水线就能通吃所有情况。5.4 扭曲变形的验证码pytesseract 的边界字符扭曲、带波浪线、背景还有复杂纹理的验证码是 pytesseract 过不去的坎。这类验证码字符形状已经被严重破坏LSTM 引擎虽然比老引擎抗造但本质上是为规整文本设计的扭曲字符的特征提取很难做好。我实测这种风格识别率通常在 10% 到 30% 之间而且没有明显的调参空间。白名单、放大、去噪全上也就是这个水平。对这类验证码我的结论很直接别在 pytesseract 上耗时间。要么换 ddddocr 这类专门针对验证码训练的库要么自己做深度学习识别。有朋友问过能不能用图像矫正把扭曲字符拉直理论上可以但工程量和稳定性都不划算。先评估你手里的验证码属于哪一类比盲目优化来得更重要。5.5 高频报错和排查清单最后把运行时最常见的几个报错汇总一下这些坑我基本都踩过报错信息原因解决办法TesseractNotFoundError没装引擎或引擎不在 PATH安装 Tesseract 并在代码里指定路径TesseractError: Failed loading language eng语言包缺失安装对应语言包用 --list-langs 检查TesseractError: Image too large放大后尺寸过大控制放大倍数一般 2 到 3 倍足够UnicodeDecodeErrorWindows 控制台编码问题设置 PYTHONIOENCODINGutf-8 或用 print(repr(text))识别结果为空字符串预处理过度或图片不清晰降低阈值、减少滤波强度、尝试反色其中Image too large这个报错比较隐蔽我一开始没意识到是放大倍数导致的。Tesseract 对超大图有自己的内部限制验证码图片放大太多反而会触发保护逻辑。2 倍到 3 倍是安全区间别贪。6. 把识别能力接到真实项目批量、命令行和自动化识别单张图片只是起步。真实的项目里验证码识别通常要嵌入到一条完整的自动化流程中。这一节讲三个实际场景批量处理、命令行传参、配合请求库完成登录流程。6.1 批量识别遍历整个文件夹如果你有一堆验证码图片需要识别写个循环遍历文件夹就行import os import pytesseract from PIL import Image folder captchas results {} for filename in os.listdir(folder): if not filename.lower().endswith((.png, .jpg, .jpeg)): continue path os.path.join(folder, filename) text pytesseract.image_to_string(Image.open(path), config--psm 7 -c tessedit_char_whitelist0123456789) results[filename] text.strip() print(f{filename}: {results[filename]})批量识别的时候我会顺手把识别结果和原图保存下来方便后续手动校对。做验证码识别评估时这个脚本能快速统计出识别率到底是多少非常实用。6.2 命令行传参让脚本可以被其他程序调用实际开发中验证码识别脚本经常不是单独运行的而是被另一个 Python 脚本或者自动化工具调用。这时候把脚本写成支持命令行参数的形式就很重要。用sys.argv可以接收外部传进来的图片路径甚至允许一次传多张import sys import pytesseract from PIL import Image def recognize(path, whitelist0123456789): img Image.open(path).convert(L) img img.resize((img.width * 2, img.height * 2)) config f--psm 7 -c tessedit_char_whitelist{whitelist} return pytesseract.image_to_string(img, configconfig).strip() if __name__ __main__: if len(sys.argv) 2: print(用法: python captcha_ocr.py 图片路径1 [图片路径2] ...) sys.exit(1) for path in sys.argv[1:]: print(f{path}: {recognize(path)})这样写好之后其他脚本可以通过subprocess调用它也可以在你的自动化流程里直接 import 这个函数。命令行传参的好处是解耦识别逻辑和调用方互不干扰图片路径作为参数传入输出结果打印到标准输出方便被外部程序捕获。如果你想把脚本打包成 exe 分发给没有 Python 环境的同事注意 pytesseract 依赖的 Tesseract 引擎并不会被 PyInstaller 自动打包进去目标机器上还是得单独安装引擎这个坑我在打包时踩过一次。6.3 与 requests 搭配的登录流程雏形最常见的落地场景是配合 requests 做登录自动化。流程是先访问登录页拿到验证码图片识别出文本再带着验证码和账号密码提交登录请求。核心代码如下import requests import io import pytesseract from PIL import Image session requests.Session() # 1. 访问登录页服务端会下发一个 session cookie login_page session.get(https://your-system.example.com/login) # 2. 请求验证码图片接口 captcha_resp session.get(https://your-system.example.com/captcha) img Image.open(io.BytesIO(captcha_resp.content)).convert(L) # 3. 识别验证码 code pytesseract.image_to_string(img, config--psm 7 -c tessedit_char_whitelist0123456789).strip() # 4. 提交登录 resp session.post(https://your-system.example.com/login, data{ username: test_user, password: test_pass, captcha: code, }) print(resp.status_code, resp.text[:200])这里有个很关键的细节验证码图片的请求必须和登录请求复用同一个 session因为服务端会把验证码的正确答案和 session 绑定换 session 的话识别出来也是白搭。这个坑我在第一次联调时卡了很久分享出来给后来人避雷。6.4 识别率见顶之后下一步怎么走当你发现 pytesseract 无论如何调优识别率都上不去时说明你手里的验证码已经超出了它的能力范围。这时候我的经验是三条路。第一条路换专用验证码识别库比如 ddddocr它对很多复杂验证码的识别率明显优于 pytesseract接口也简单几分钟就能完成替换。第二条路自己造数据训练深度学习模型收集几千张验证码图片做标注用 CNN 或者 CRNN 训练一个识别模型这条路效果上限最高但需要的时间和算力也最多。第三条路如果验证码是你自己系统里的直接调整验证码生成策略降低验证码难度换一个更清晰的生成方案从源头上避免识别难题。我个人在项目里的选择是分层处理简单验证码用 pytesseract 白名单方案中等难度的用专用库实在搞不定的才上深度学习。这个策略帮我省下了大量开发时间。验证码识别这件事准确识别出结果固然重要但更关键的是知道每个工具有多长的能力边界在合适的场景用合适的工具这是我在多个项目里切切实实踩出来的体会。