1. 项目概述最近在做一个自动化脚本需要处理一个带验证码的登录环节手动输入太费劲了就琢磨着能不能用Python自动识别。网上搜了一圈发现验证码识别这块水还挺深从传统的图像处理到现在的深度学习方案都有。对于咱们这种只是想快速解决问题、不想折腾复杂模型训练的开发者来说一个开箱即用、识别率还不错的库就是刚需。我试了好几个最后锁定了ddddocr江湖人称“带带弟弟OCR”。这名字挺有意思但东西是真不错它主打的就是一个“通用”和“简单”基本上常见的数字字母验证码、滑块验证码都能对付而且依赖极简几行代码就能跑起来。这篇文章我就结合自己的踩坑经验聊聊怎么用Python和ddddocr快速实现一个能自动识别并填写验证码的流程。无论你是想自动化测试、数据采集还是单纯想解放双手这套方案应该都能给你提供直接的参考。2. 核心思路与技术选型2.1 为什么选择 ddddocr在动手之前得先想清楚用什么工具。验证码识别方案很多有自己从头训练CNN模型的有用Tesseract做OCR的也有调用第三方API的。我选择ddddocr主要基于下面几个考虑第一是离线可用。这是最重要的。很多云服务API虽然识别率高但要么收费要么有调用频率限制对于需要稳定、高频次运行的自动化任务来说本地离线方案是首选。ddddocr把训练好的模型直接打包进库装好就能用数据不出本地隐私和稳定性都有保障。第二是泛化能力强。它号称“通用验证码识别”背后的模型是用海量随机生成的验证码数据训练出来的。这意味着它没见过你的特定验证码也可能猜个八九不离十。我实测过一些网站的数字字母混合验证码识别率比我预想的高对于干扰线、扭曲、粘连不严重的常规验证码效果很稳。第三是功能全面且简单。它不止能做传统的字符识别OCR还内置了目标检测和滑块匹配算法。一个库就能覆盖“点选”、“滑块”等多种验证码场景。API设计也非常简洁主要就classification分类识别和slide_match滑块匹配几个方法学习成本极低。第四是性能与生态。纯Python实现依赖少安装方便。支持CPU和GPU加速处理速度能满足大部分实时性要求不高的场景。社区活跃遇到问题比较容易找到解决方案。当然它也不是万能的。对于极其复杂、动态变化的验证码比如需要逻辑推理的或者对识别率要求达到99.9%以上的商业场景可能需要更定制化的方案。但对于绝大多数“能自动识别就省事识别不了再手动”的辅助性需求ddddocr的性价比非常高。2.2 自动化流程设计我们的目标不只是识别还要“填写”。所以整个流程是一个闭环获取验证码图片这是起点。可能是从网页上截图也可能是直接下载图片链接。关键是要能稳定、准确地拿到那张包含验证码的图片数据。图片预处理可选原始图片可能带有噪声、背景干扰或者尺寸不合适。预处理就像给图片“美颜”去掉无关信息突出验证码主体能有效提升后续识别的准确率。调用 ddddocr 进行识别这是核心步骤。根据验证码类型字符型、滑块型调用对应的识别函数得到识别结果字符串或坐标。结果后处理与校验识别结果可能包含杂讯或格式不对。比如识别出“O0”不分或者长度不符合预期。这里需要一些简单的规则进行清洗和校验。模拟填写与提交将处理好的识别结果通过自动化工具如Selenium、Playwright、requests等填充到网页或应用对应的输入框中并触发提交动作。整个流程的难点往往不在ddddocr本身而在第一步和第五步——如何稳定获取图片以及如何精准模拟交互。不同的网站或应用反爬和防护策略千差万别这部分需要具体问题具体分析。3. 环境搭建与基础识别3.1 安装与初体验安装ddddocr非常简单一条pip命令搞定。建议在虚拟环境里操作避免包冲突。pip install ddddocr安装完成后我们来写一个最简单的识别脚本感受一下它的威力。假设我们有一张名为captcha.jpg的验证码图片。import ddddocr # 初始化识别器。这一步会加载模型首次运行稍慢后续调用就快了。 ocr ddddocr.DdddOcr() # 以二进制模式读取图片 with open(captcha.jpg, rb) as f: image_bytes f.read() # 调用classification方法进行识别 result ocr.classification(image_bytes) print(f识别结果是: {result})就这么四五行代码一个基础的验证码识别功能就完成了。ddddocr支持传入图片的二进制字节流所以无论你是从文件读取还是从网络请求的response.content获取都可以直接喂给它。注意初始化DdddOcr()对象是个相对耗时的操作因为它要加载预训练的模型文件。务必避免在循环或每次识别时都重新初始化。正确的做法是在脚本开头初始化一次然后在整个程序生命周期内复用这个实例。3.2 处理不同类型的验证码图片实际项目中验证码图片的来源五花八门。下面列举几种常见情况及处理方法。情况一从网络URL获取这是爬虫场景中最常见的。我们可以使用requests库下载图片。import ddddocr import requests from io import BytesIO ocr ddddocr.DdddOcr() # 假设验证码图片的URL captcha_url https://example.com/captcha.jpg # 发送请求获取图片注意可能需要处理cookies或headers以通过反爬 session requests.Session() # 这里可能需要添加必要的headers如User-Agent headers {User-Agent: Mozilla/5.0} response session.get(captcha_url, headersheaders) if response.status_code 200: # 直接将响应的二进制内容传给ddddocr image_bytes response.content result ocr.classification(image_bytes) print(f在线验证码识别结果: {result}) else: print(下载验证码图片失败)情况二处理Base64编码的图片有些网站会将验证码图片以Base64格式直接嵌入在HTML或JSON响应里。import ddddocr import base64 ocr ddddocr.DdddOcr() # 假设base64_str是包含data:image/png;base64,前缀的字符串 base64_str data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA... # 去掉前缀只保留编码部分 if base64, in base64_str: base64_str base64_str.split(base64,)[1] # 解码为二进制字节流 try: image_bytes base64.b64decode(base64_str) result ocr.classification(image_bytes) print(fBase64验证码识别结果: {result}) except Exception as e: print(fBase64解码或识别失败: {e})情况三从Selenium等自动化工具截取元素如果你用Selenium控制浏览器需要先定位到验证码图片元素然后截图。from selenium import webdriver from selenium.webdriver.common.by import By import ddddocr import time ocr ddddocr.DdddOcr() driver webdriver.Chrome() driver.get(https://example.com/login) # 等待验证码图片加载 time.sleep(2) # 定位验证码图片元素 captcha_element driver.find_element(By.ID, captcha_image) # 方法1获取图片src并下载如果src是独立URL # image_url captcha_element.get_attribute(src) # ... 然后用requests下载 # 方法2直接对元素截图适用于动态生成的canvas或图片 # 先截图整个元素 element_png captcha_element.screenshot_as_png # 这是二进制数据 result ocr.classification(element_png) print(fSelenium截图识别结果: {result}) driver.quit()情况四处理带透明通道的PNG图片有些验证码背景是透明的这可能会干扰识别。ddddocr提供了png_fix参数来处理。import ddddocr ocr ddddocr.DdddOcr() with open(transparent_captcha.png, rb) as f: image_bytes f.read() # 使用png_fix参数优化透明背景图片的识别 result ocr.classification(image_bytes, png_fixTrue) print(f透明PNG识别结果: {result})4. 提升识别率的实战技巧直接调用classification可能无法应对所有情况尤其是当验证码干扰较强时。ddddocr提供了一些高级参数和技巧来提升识别率。4.1 限定识别字符范围如果你明确知道验证码只包含数字或者只包含字母那么限定识别范围可以大幅提升准确率因为模型不需要在无关字符上浪费“注意力”。import ddddocr ocr ddddocr.DdddOcr() with open(captcha.jpg, rb) as f: image_bytes f.read() # 方法1使用内置的字符集范围常量 # 0: 纯数字 0-9 # 1: 纯小写英文 a-z # 2: 纯大写英文 A-Z # 3: 小写大写英文 # 4: 小写英文数字 # 5: 大写英文数字 # 6: 小写大写英文数字 ocr.set_ranges(0) # 告诉识别器我只认数字 result ocr.classification(image_bytes) print(f限定为数字后的识别结果: {result}) # 方法2自定义字符范围字符串 ocr.set_ranges(ABCDEFGHJKLMNPQRSTUVWXYZ23456789) # 排除容易混淆的I,1,O,0 result2 ocr.classification(image_bytes) print(f自定义字符集后的识别结果: {result2})这个功能非常实用。比如很多网站的验证码会避免使用0和O、1和I你就可以在自定义字符集中把它们去掉减少误判。4.2 启用Beta模型或尝试颜色过滤ddddocr内置了两套OCR模型。默认使用的是common_old.onnx。你可以通过betaTrue参数尝试另一套common.onnx模型对于某些验证码可能效果更好。# 尝试使用Beta模型 ocr_beta ddddocr.DdddOcr(betaTrue) result_beta ocr_beta.classification(image_bytes) print(fBeta模型识别结果: {result_beta}) # 与默认模型结果对比 ocr_default ddddocr.DdddOcr() # 默认即 oldFalse, betaFalse result_default ocr_default.classification(image_bytes) print(f默认模型识别结果: {result_default})对于颜色对比鲜明的验证码可以使用颜色过滤功能只识别特定颜色的字符。ocr ddddocr.DdddOcr() # 假设验证码是红色字符 result ocr.classification(image_bytes, colors[red]) print(f只识别红色的结果: {result}) # 支持多种颜色 result2 ocr.classification(image_bytes, colors[red, blue]) print(f识别红色和蓝色的结果: {result2})支持的颜色关键词包括red,green,blue,yellow,orange,purple,pink,brown。这个功能对于背景复杂但字符颜色单一的验证码有奇效。4.3 自定义预处理管道如果上述方法还不够我们可以祭出大招在调用ddddocr之前先对图片进行自定义预处理。这需要用到OpenCV或PIL库。一个常见的预处理流程包括灰度化、二值化、降噪、形态学操作。import ddddocr import cv2 import numpy as np from io import BytesIO def preprocess_captcha(image_bytes): 对验证码图片进行预处理 # 将字节流转换为OpenCV图像格式 nparr np.frombuffer(image_bytes, np.uint8) img cv2.imdecode(nparr, cv2.IMREAD_COLOR) # 1. 转换为灰度图 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 2. 二值化 (阈值处理将灰度图转为黑白) # 自适应阈值有时比固定阈值更好 # binary cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)[1] binary cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2) # 3. 降噪 (去除小的白点或黑点) kernel np.ones((1, 1), np.uint8) opening cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel, iterations1) # 4. 膨胀/腐蚀操作用于连接断裂的笔画或分离粘连字符 (根据情况选择) # kernel2 np.ones((2,2), np.uint8) # dilated cv2.dilate(opening, kernel2, iterations1) # 将处理后的图像编码回字节流 is_success, buffer cv2.imencode(.png, opening) if is_success: return BytesIO(buffer).getvalue() else: # 如果编码失败返回原图 return image_bytes # 使用示例 ocr ddddocr.DdddOcr() with open(noisy_captcha.jpg, rb) as f: raw_image_bytes f.read() processed_image_bytes preprocess_captcha(raw_image_bytes) result ocr.classification(processed_image_bytes) print(f预处理后的识别结果: {result}) # 可以保存处理前后的图片对比效果 # with open(processed.png, wb) as f: # f.write(processed_image_bytes)预处理没有银弹不同的验证码需要不同的参数组合。多试试不同的阈值方法和形态学操作cv2.erode,cv2.dilate观察处理后的图像找到最适合当前验证码的方案。5. 处理滑块验证码除了字符验证码滑块验证码也越来越常见。ddddocr提供了slide_match方法专门处理这类问题。它的原理通常是比对滑块图和背景图的差异找到滑块需要移动到的缺口位置。5.1 滑块验证码识别原理滑块验证码一般由两部分组成背景图background一张带有缺口阴影或凹槽的图片。滑块图target一张需要被拖拽到正确位置的、带有凸起或图案的图片。ddddocr的slide_match函数通过图像匹配算法计算滑块图在背景图中的最佳匹配位置返回滑块左上角和右下角的坐标(x1, y1, x2, y2)。通常我们只需要X轴的坐标来计算滑动距离。5.2 代码实现与距离计算假设我们已经下载了背景图bg.jpg和滑块图target.png。import ddddocr import cv2 import numpy as np # 初始化滑块识别器注意参数 detFalse, ocrFalse slide ddddocr.DdddOcr(detFalse, ocrFalse) # 读取图片 with open(target.png, rb) as f: target_bytes f.read() with open(bg.jpg, rb) as f: background_bytes f.read() # 进行匹配 match_result slide.slide_match(target_bytes, background_bytes) print(f匹配结果: {match_result}) # 结果是一个字典例如{target: [x1, y1, x2, y2]} if target in match_result: x1, y1, x2, y2 match_result[target] # 通常滑块的滑动距离就是缺口左上角的x坐标 (x1) slide_distance x1 print(f滑块需要滑动的距离像素: {slide_distance}) else: print(未能匹配到滑块位置)关键点解析ddddocr.DdddOcr(detFalse, ocrFalse)这个参数组合是专门用于滑块匹配的模式。slide_match返回的坐标是滑块在背景图中匹配到的区域的左上角和右下角。对于最常见的从左向右滑动滑动的水平位移通常就是x1的值。有些滑块图本身带有完整的背景simple_targetTrue参数可以尝试优化匹配。5.3 模拟滑动操作识别出距离后下一步是用自动化工具模拟滑动。这里以Selenium为例演示如何计算轨迹并执行滑动。from selenium import webdriver from selenium.webdriver import ActionChains from selenium.webdriver.common.by import By import time import random # 假设我们已经获得了滑动距离 slide_distance (单位像素) # 以及网页上的滑块元素 slider_element driver webdriver.Chrome() driver.get(your_login_page_url) slider driver.find_element(By.ID, slider) # 根据实际情况定位滑块元素 # 创建动作链 actions ActionChains(driver) # 点击并按住滑块 actions.click_and_hold(slider).perform() # **关键模拟人的滑动轨迹而不是匀速移动** # 基本轨迹先快后慢最后可能有点抖动 total_distance slide_distance # 将总距离分成若干段 tracks [] current 0 # 设置一个阈值比如留出最后5-10像素进行微调或直接释放 mid_distance total_distance * 0.8 remain_distance total_distance - mid_distance # 前半段加速滑动 while current mid_distance: # 每次移动的距离是随机值模拟人手的不确定性 move random.randint(3, 6) if current move mid_distance: move mid_distance - current tracks.append(move) current move # 后半段减速滑动并加入小范围抖动 while current total_distance: move random.randint(1, 3) if current move total_distance: move total_distance - current tracks.append(move) current move # 可能还需要一点过冲再拉回 # tracks.append(random.randint(-2, 2)) # 执行滑动轨迹 for track in tracks: actions.move_by_offset(track, 0).perform() # 水平移动y轴偏移为0 time.sleep(random.uniform(0.01, 0.05)) # 每次移动后短暂停顿 # 释放滑块 actions.release().perform() time.sleep(1) # 等待结果验证 driver.quit()重要经验直接move_by_offset(slide_distance, 0)会被很多网站的反爬机制识别为机器行为。模拟人类滑动轨迹是成功的关键。轨迹应包含变速和随机停顿。此外有些网站会验证滑动轨迹的总时间太快或太慢都不行需要根据实际情况调整time.sleep的参数。6. 目标检测模式与复杂验证码处理对于一些更复杂的验证码比如点选图中特定文字或物体ddddocr的目标检测模式detTrue就能派上用场。它不识别具体文字而是找出图片中可能是“目标”的多个区域框。6.1 启用目标检测import ddddocr import cv2 import numpy as np # 初始化目标检测器 detector ddddocr.DdddOcr(detTrue, ocrFalse) # detTrue 启用检测模式 with open(complex_captcha.jpg, rb) as f: image_bytes f.read() # 进行检测返回一个列表每个元素是一个边界框 [x1, y1, x2, y2] bboxes detector.detection(image_bytes) print(f检测到 {len(bboxes)} 个目标框:) for i, bbox in enumerate(bboxes): print(f 框{i1}: 左上({bbox[0]}, {bbox[1]}), 右下({bbox[2]}, {bbox[3]}))6.2 结合OCR进行二级识别目标检测通常用于定位我们可以把检测到的每个框裁剪出来再用OCR模式去识别框内的文字。import ddddocr import cv2 import numpy as np from PIL import Image import io # 初始化两个实例一个用于检测一个用于识别 detector ddddocr.DdddOcr(detTrue, ocrFalse) recognizer ddddocr.DdddOcr() # 默认OCR模式 # 读取图片并转换为OpenCV格式用于裁剪 nparr np.frombuffer(image_bytes, np.uint8) img_cv cv2.imdecode(nparr, cv2.IMREAD_COLOR) # 进行目标检测 bboxes detector.detection(image_bytes) results [] for bbox in bboxes: x1, y1, x2, y2 bbox # 裁剪目标区域 cropped_img img_cv[y1:y2, x1:x2] # 将裁剪后的OpenCV图像转回字节流 is_success, buffer cv2.imencode(.jpg, cropped_img) cropped_bytes io.BytesIO(buffer).getvalue() # 对裁剪区域进行OCR识别 text recognizer.classification(cropped_bytes) results.append({ bbox: bbox, text: text }) print(f在区域 {bbox} 中识别到文字: {text}) # 可视化结果可选 for r in results: bbox r[bbox] text r[text] cv2.rectangle(img_cv, (bbox[0], bbox[1]), (bbox[2], bbox[3]), (0, 255, 0), 2) cv2.putText(img_cv, text, (bbox[0], bbox[1]-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2) cv2.imwrite(detection_result.jpg, img_cv)这种“检测识别”的两阶段策略非常适合验证码文字位置不固定或者需要点击图中特定词语的场景。7. 构建健壮的自动化填写流程识别只是第一步把它集成到一个完整的、能应对各种异常的自动化流程里才是项目成功的关键。7.1 流程封装与错误重试我们不能指望识别一次就100%成功。一个健壮的系统必须包含错误处理和重试机制。import ddddocr import requests import time from typing import Optional class CaptchaSolver: def __init__(self, retry_times3, ocr_betaFalse): self.ocr ddddocr.DdddOcr(betaocr_beta) self.retry_times retry_times def download_image(self, url, sessionNone, **kwargs) - Optional[bytes]: 下载验证码图片支持自定义session和headers try: if session is None: session requests.Session() response session.get(url, **kwargs) response.raise_for_status() # 检查HTTP错误 return response.content except Exception as e: print(f下载图片失败: {e}) return None def recognize_with_retry(self, image_bytes, preprocess_funcNone) - Optional[str]: 带重试和预处理的识别函数 for attempt in range(self.retry_times): try: # 可选预处理 if preprocess_func and callable(preprocess_func): processed_bytes preprocess_func(image_bytes) else: processed_bytes image_bytes result self.ocr.classification(processed_bytes) # 基础校验比如验证码通常是4-6位可以过滤掉明显错误的结果 if 3 len(result) 8: return result else: print(f第{attempt1}次识别结果长度异常: {result} 重试...) time.sleep(0.5) # 短暂等待后重试 except Exception as e: print(f第{attempt1}次识别发生异常: {e}) time.sleep(1) print(f识别失败已重试{self.retry_times}次) return None def solve_and_fill(self, captcha_url, input_element, submit_element, sessionNone, **kwargs): 完整的解决并填写流程 # 1. 获取图片 image_bytes self.download_image(captcha_url, session, **kwargs) if not image_bytes: return False, 无法获取验证码图片 # 2. 识别 captcha_text self.recognize_with_retry(image_bytes) if not captcha_text: return False, 验证码识别失败 # 3. 填写 (这里以Selenium为例实际可能是requests.post) try: input_element.clear() input_element.send_keys(captcha_text) submit_element.click() return True, captcha_text except Exception as e: return False, f填写或提交失败: {e} # 使用示例 if __name__ __main__: solver CaptchaSolver(retry_times2, ocr_betaTrue) # 假设我们已经有了driver和页面元素 # captcha_img_url driver.find_element(...).get_attribute(src) # input_box driver.find_element(By.ID, captchaInput) # submit_btn driver.find_element(By.ID, submitBtn) # success, msg solver.solve_and_fill(captcha_img_url, input_box, submit_btn, sessiondriver)7.2 验证码结果的后处理识别出来的文本可能包含空格、换行符或者将0识别为O。根据具体验证码的规则我们可以添加一些后处理逻辑。def postprocess_captcha_text(raw_text): 对识别出的验证码文本进行后处理 if not raw_text: return # 1. 去除空白字符 text raw_text.strip().replace( , ).replace(\n, ).replace(\r, ) # 2. 常见字符替换根据实际情况调整 common_mistakes { O: 0, # 大写字母O替换为数字0 I: 1, # 大写字母I替换为数字1 l: 1, # 小写字母l替换为数字1 Z: 2, S: 5, B: 8, # 可以继续添加其他易混淆字符对 } # 只替换那些看起来像混淆的比如长度是数字时 if text.isalnum() and len(text) 4: # 假设是4位纯数字验证码 corrected [] for char in text: corrected.append(common_mistakes.get(char.upper(), char)) text .join(corrected) # 3. 长度校验和格式校验 # 例如如果知道验证码必须是4位数字 if text.isdigit() and len(text) 4: return text else: # 如果不符合预期可以返回None触发重试或者尝试其他处理 return None # 或 return raw_text # 在识别流程中集成后处理 captcha_text solver.recognize_with_retry(image_bytes) if captcha_text: processed_text postprocess_captcha_text(captcha_text) if processed_text: print(f后处理后的验证码: {processed_text})7.3 集成到Web自动化中以Selenium为例一个完整的登录自动化脚本可能长这样from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import ddddocr import time class LoginAutomator: def __init__(self): self.driver webdriver.Chrome() self.wait WebDriverWait(self.driver, 10) self.ocr ddddocr.DdddOcr() def get_captcha_from_page(self): 从当前页面获取验证码图片和输入框 # 等待验证码图片加载 captcha_img_element self.wait.until( EC.presence_of_element_located((By.ID, captcha_img)) ) # 方法1获取src并下载 captcha_url captcha_img_element.get_attribute(src) # 这里需要实现一个下载函数注意处理相对路径和可能携带的token # 方法2更通用直接对元素截图 captcha_png captcha_img_element.screenshot_as_png input_box self.driver.find_element(By.ID, captcha) submit_btn self.driver.find_element(By.ID, loginBtn) return captcha_png, input_box, submit_btn def solve_captcha(self, image_bytes): 识别验证码 # 可以加入预处理和后处理 return self.ocr.classification(image_bytes) def login(self, username, password, login_url): try: self.driver.get(login_url) # 填写用户名密码 self.driver.find_element(By.ID, username).send_keys(username) self.driver.find_element(By.ID, password).send_keys(password) # 处理验证码 captcha_png, input_box, submit_btn self.get_captcha_from_page() captcha_code self.solve_captcha(captcha_png) if captcha_code and len(captcha_code) 4: input_box.clear() input_box.send_keys(captcha_code) submit_btn.click() # 检查是否登录成功 time.sleep(2) if dashboard in self.driver.current_url: print(登录成功) return True else: print(登录可能失败检查验证码或网络。) # 可以在这里加入重试逻辑比如刷新验证码再试一次 return False else: print(f识别出的验证码无效: {captcha_code}) return False except Exception as e: print(f登录过程发生异常: {e}) return False finally: # self.driver.quit() # 根据实际情况决定是否关闭 pass if __name__ __main__: automator LoginAutomator() success automator.login(your_username, your_password, https://example.com/login)8. 常见问题、优化策略与排错实录在实际使用中你肯定会遇到各种各样的问题。下面是我踩过的一些坑和解决方案。8.1 识别准确率不理想这是最常见的问题。别急着怪库可以按以下步骤排查和优化检查输入图片质量把下载或截图到的验证码图片保存下来用眼睛看是否清晰。如果人眼都难以辨认机器识别困难是正常的。尝试刷新页面获取更清晰的验证码。尝试不同的模型和参数切换betaTrue试试。使用set_ranges严格限定字符范围。对于彩色验证码尝试colors参数进行颜色过滤。实施图片预处理如第4.3节所述灰度化、二值化、降噪可以显著提升复杂背景验证码的识别率。没有一种预处理方法通吃所有需要针对你的验证码特点进行调整。后处理与校验利用业务逻辑。如果知道验证码是5位数字那么识别出6位或包含字母的结果可以直接判定为错误触发重试或刷新。融合策略如果条件允许可以同时使用ddddocr和其他轻量级OCR库如pytesseract对同一个验证码识别多次取出现次数最多的结果或者设计一个简单的投票机制。终极方案收集数据训练如果某个网站的验证码风格独特且固定识别率始终上不去可以考虑用ddddocr作者提供的dddd_trainer工具收集几百张该网站的验证码自己训练一个专用模型。这是最有效但也是最费事的方法。8.2 性能与资源问题初始化慢首次初始化DdddOcr()确实需要几秒到十几秒加载模型。务必确保在程序开始时只初始化一次然后全局复用。如果在Web服务器等需要多进程的环境中使用考虑在服务启动时初始化好模型或者使用单例模式。内存占用每个DdddOcr实例都会在内存中加载模型。如果同时需要OCR和Detect功能不要初始化一个ocrTrue, detTrue的实例这不可行而是根据需要初始化两个独立的实例。在长时间运行的服务中注意监控内存。GPU加速如果你有NVIDIA GPU且处理量巨大可以尝试启用GPU加速。首先确保安装了onnxruntime-gpu库注意与CUDA版本的兼容性然后初始化时设置use_gpuTrue。对于绝大多数个人或中小规模的自动化任务CPU已经足够快。8.3 特定网站的反爬应对验证码识别常常与反爬虫对抗。网站可能会频繁更换验证码样式导致你的预处理规则或模型失效。应对策略是使你的预处理逻辑更鲁棒或者定期更新模型。验证码与一次性的Token绑定你下载的验证码图片只能使用一次第二次提交即使识别正确也会失败。解决方案是在同一个会话Session内完成“获取图片-识别-提交”的整个流程确保Cookie或Token的一致性。滑块轨迹验证如第5.3节所述匀速滑动会被识别。必须模拟带加速度和随机停顿的人类轨迹。点选验证码需要结合目标检测(detTrue)先定位多个可点击项的位置然后模拟鼠标依次点击。点击顺序有时也是验证的一部分。触发频率限制识别和提交不要太快加入随机延迟(time.sleep(random.uniform(1, 3)))模拟真人操作间隔。8.4 错误处理与日志在生产环境中完善的错误处理和日志记录至关重要。import logging logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) def safe_recognize(ocr, image_bytes, context): try: result ocr.classification(image_bytes) logging.info(f{context} 识别成功: {result}) return result except ddddocr.DdddOcrError as e: logging.error(f{context} ddddocr内部错误: {e}) return None except Exception as e: logging.exception(f{context} 识别过程发生未知异常) # 会打印堆栈信息 return None # 保存识别失败的图片用于后续分析和模型优化 def save_failed_image(image_bytes, expected_textNone, recognized_textNone): filename ffailed_{int(time.time())}.jpg with open(f./failures/{filename}, wb) as f: f.write(image_bytes) # 可以同时记录一个元数据文件记录预期结果和识别结果 with open(f./failures/{filename}.meta, w) as f: f.write(fexpected:{expected_text}, recognized:{recognized_text})建立一个“失败案例库”定期分析哪些类型的验证码容易识别错误能帮你持续优化预处理和后处理策略。8.5 关于ddddocr的版本与兼容性关注ddddocr的GitHub仓库及时更新版本。新版本可能会修复bug、提升识别率或增加新功能。同时注意你的Python版本是否在库的支持范围内通常支持主流版本。如果遇到安装或导入错误首先检查Python版本和系统架构64位。在Windows上可能需要安装Visual C Redistributable运行库。最后记住任何自动化验证码识别的行为都应遵守目标网站的服务条款仅用于合法授权的自动化测试或个人学习研究避免对他人服务造成不必要的负担。
Python自动化验证码识别:ddddocr库实战指南与优化技巧
1. 项目概述最近在做一个自动化脚本需要处理一个带验证码的登录环节手动输入太费劲了就琢磨着能不能用Python自动识别。网上搜了一圈发现验证码识别这块水还挺深从传统的图像处理到现在的深度学习方案都有。对于咱们这种只是想快速解决问题、不想折腾复杂模型训练的开发者来说一个开箱即用、识别率还不错的库就是刚需。我试了好几个最后锁定了ddddocr江湖人称“带带弟弟OCR”。这名字挺有意思但东西是真不错它主打的就是一个“通用”和“简单”基本上常见的数字字母验证码、滑块验证码都能对付而且依赖极简几行代码就能跑起来。这篇文章我就结合自己的踩坑经验聊聊怎么用Python和ddddocr快速实现一个能自动识别并填写验证码的流程。无论你是想自动化测试、数据采集还是单纯想解放双手这套方案应该都能给你提供直接的参考。2. 核心思路与技术选型2.1 为什么选择 ddddocr在动手之前得先想清楚用什么工具。验证码识别方案很多有自己从头训练CNN模型的有用Tesseract做OCR的也有调用第三方API的。我选择ddddocr主要基于下面几个考虑第一是离线可用。这是最重要的。很多云服务API虽然识别率高但要么收费要么有调用频率限制对于需要稳定、高频次运行的自动化任务来说本地离线方案是首选。ddddocr把训练好的模型直接打包进库装好就能用数据不出本地隐私和稳定性都有保障。第二是泛化能力强。它号称“通用验证码识别”背后的模型是用海量随机生成的验证码数据训练出来的。这意味着它没见过你的特定验证码也可能猜个八九不离十。我实测过一些网站的数字字母混合验证码识别率比我预想的高对于干扰线、扭曲、粘连不严重的常规验证码效果很稳。第三是功能全面且简单。它不止能做传统的字符识别OCR还内置了目标检测和滑块匹配算法。一个库就能覆盖“点选”、“滑块”等多种验证码场景。API设计也非常简洁主要就classification分类识别和slide_match滑块匹配几个方法学习成本极低。第四是性能与生态。纯Python实现依赖少安装方便。支持CPU和GPU加速处理速度能满足大部分实时性要求不高的场景。社区活跃遇到问题比较容易找到解决方案。当然它也不是万能的。对于极其复杂、动态变化的验证码比如需要逻辑推理的或者对识别率要求达到99.9%以上的商业场景可能需要更定制化的方案。但对于绝大多数“能自动识别就省事识别不了再手动”的辅助性需求ddddocr的性价比非常高。2.2 自动化流程设计我们的目标不只是识别还要“填写”。所以整个流程是一个闭环获取验证码图片这是起点。可能是从网页上截图也可能是直接下载图片链接。关键是要能稳定、准确地拿到那张包含验证码的图片数据。图片预处理可选原始图片可能带有噪声、背景干扰或者尺寸不合适。预处理就像给图片“美颜”去掉无关信息突出验证码主体能有效提升后续识别的准确率。调用 ddddocr 进行识别这是核心步骤。根据验证码类型字符型、滑块型调用对应的识别函数得到识别结果字符串或坐标。结果后处理与校验识别结果可能包含杂讯或格式不对。比如识别出“O0”不分或者长度不符合预期。这里需要一些简单的规则进行清洗和校验。模拟填写与提交将处理好的识别结果通过自动化工具如Selenium、Playwright、requests等填充到网页或应用对应的输入框中并触发提交动作。整个流程的难点往往不在ddddocr本身而在第一步和第五步——如何稳定获取图片以及如何精准模拟交互。不同的网站或应用反爬和防护策略千差万别这部分需要具体问题具体分析。3. 环境搭建与基础识别3.1 安装与初体验安装ddddocr非常简单一条pip命令搞定。建议在虚拟环境里操作避免包冲突。pip install ddddocr安装完成后我们来写一个最简单的识别脚本感受一下它的威力。假设我们有一张名为captcha.jpg的验证码图片。import ddddocr # 初始化识别器。这一步会加载模型首次运行稍慢后续调用就快了。 ocr ddddocr.DdddOcr() # 以二进制模式读取图片 with open(captcha.jpg, rb) as f: image_bytes f.read() # 调用classification方法进行识别 result ocr.classification(image_bytes) print(f识别结果是: {result})就这么四五行代码一个基础的验证码识别功能就完成了。ddddocr支持传入图片的二进制字节流所以无论你是从文件读取还是从网络请求的response.content获取都可以直接喂给它。注意初始化DdddOcr()对象是个相对耗时的操作因为它要加载预训练的模型文件。务必避免在循环或每次识别时都重新初始化。正确的做法是在脚本开头初始化一次然后在整个程序生命周期内复用这个实例。3.2 处理不同类型的验证码图片实际项目中验证码图片的来源五花八门。下面列举几种常见情况及处理方法。情况一从网络URL获取这是爬虫场景中最常见的。我们可以使用requests库下载图片。import ddddocr import requests from io import BytesIO ocr ddddocr.DdddOcr() # 假设验证码图片的URL captcha_url https://example.com/captcha.jpg # 发送请求获取图片注意可能需要处理cookies或headers以通过反爬 session requests.Session() # 这里可能需要添加必要的headers如User-Agent headers {User-Agent: Mozilla/5.0} response session.get(captcha_url, headersheaders) if response.status_code 200: # 直接将响应的二进制内容传给ddddocr image_bytes response.content result ocr.classification(image_bytes) print(f在线验证码识别结果: {result}) else: print(下载验证码图片失败)情况二处理Base64编码的图片有些网站会将验证码图片以Base64格式直接嵌入在HTML或JSON响应里。import ddddocr import base64 ocr ddddocr.DdddOcr() # 假设base64_str是包含data:image/png;base64,前缀的字符串 base64_str data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA... # 去掉前缀只保留编码部分 if base64, in base64_str: base64_str base64_str.split(base64,)[1] # 解码为二进制字节流 try: image_bytes base64.b64decode(base64_str) result ocr.classification(image_bytes) print(fBase64验证码识别结果: {result}) except Exception as e: print(fBase64解码或识别失败: {e})情况三从Selenium等自动化工具截取元素如果你用Selenium控制浏览器需要先定位到验证码图片元素然后截图。from selenium import webdriver from selenium.webdriver.common.by import By import ddddocr import time ocr ddddocr.DdddOcr() driver webdriver.Chrome() driver.get(https://example.com/login) # 等待验证码图片加载 time.sleep(2) # 定位验证码图片元素 captcha_element driver.find_element(By.ID, captcha_image) # 方法1获取图片src并下载如果src是独立URL # image_url captcha_element.get_attribute(src) # ... 然后用requests下载 # 方法2直接对元素截图适用于动态生成的canvas或图片 # 先截图整个元素 element_png captcha_element.screenshot_as_png # 这是二进制数据 result ocr.classification(element_png) print(fSelenium截图识别结果: {result}) driver.quit()情况四处理带透明通道的PNG图片有些验证码背景是透明的这可能会干扰识别。ddddocr提供了png_fix参数来处理。import ddddocr ocr ddddocr.DdddOcr() with open(transparent_captcha.png, rb) as f: image_bytes f.read() # 使用png_fix参数优化透明背景图片的识别 result ocr.classification(image_bytes, png_fixTrue) print(f透明PNG识别结果: {result})4. 提升识别率的实战技巧直接调用classification可能无法应对所有情况尤其是当验证码干扰较强时。ddddocr提供了一些高级参数和技巧来提升识别率。4.1 限定识别字符范围如果你明确知道验证码只包含数字或者只包含字母那么限定识别范围可以大幅提升准确率因为模型不需要在无关字符上浪费“注意力”。import ddddocr ocr ddddocr.DdddOcr() with open(captcha.jpg, rb) as f: image_bytes f.read() # 方法1使用内置的字符集范围常量 # 0: 纯数字 0-9 # 1: 纯小写英文 a-z # 2: 纯大写英文 A-Z # 3: 小写大写英文 # 4: 小写英文数字 # 5: 大写英文数字 # 6: 小写大写英文数字 ocr.set_ranges(0) # 告诉识别器我只认数字 result ocr.classification(image_bytes) print(f限定为数字后的识别结果: {result}) # 方法2自定义字符范围字符串 ocr.set_ranges(ABCDEFGHJKLMNPQRSTUVWXYZ23456789) # 排除容易混淆的I,1,O,0 result2 ocr.classification(image_bytes) print(f自定义字符集后的识别结果: {result2})这个功能非常实用。比如很多网站的验证码会避免使用0和O、1和I你就可以在自定义字符集中把它们去掉减少误判。4.2 启用Beta模型或尝试颜色过滤ddddocr内置了两套OCR模型。默认使用的是common_old.onnx。你可以通过betaTrue参数尝试另一套common.onnx模型对于某些验证码可能效果更好。# 尝试使用Beta模型 ocr_beta ddddocr.DdddOcr(betaTrue) result_beta ocr_beta.classification(image_bytes) print(fBeta模型识别结果: {result_beta}) # 与默认模型结果对比 ocr_default ddddocr.DdddOcr() # 默认即 oldFalse, betaFalse result_default ocr_default.classification(image_bytes) print(f默认模型识别结果: {result_default})对于颜色对比鲜明的验证码可以使用颜色过滤功能只识别特定颜色的字符。ocr ddddocr.DdddOcr() # 假设验证码是红色字符 result ocr.classification(image_bytes, colors[red]) print(f只识别红色的结果: {result}) # 支持多种颜色 result2 ocr.classification(image_bytes, colors[red, blue]) print(f识别红色和蓝色的结果: {result2})支持的颜色关键词包括red,green,blue,yellow,orange,purple,pink,brown。这个功能对于背景复杂但字符颜色单一的验证码有奇效。4.3 自定义预处理管道如果上述方法还不够我们可以祭出大招在调用ddddocr之前先对图片进行自定义预处理。这需要用到OpenCV或PIL库。一个常见的预处理流程包括灰度化、二值化、降噪、形态学操作。import ddddocr import cv2 import numpy as np from io import BytesIO def preprocess_captcha(image_bytes): 对验证码图片进行预处理 # 将字节流转换为OpenCV图像格式 nparr np.frombuffer(image_bytes, np.uint8) img cv2.imdecode(nparr, cv2.IMREAD_COLOR) # 1. 转换为灰度图 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 2. 二值化 (阈值处理将灰度图转为黑白) # 自适应阈值有时比固定阈值更好 # binary cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)[1] binary cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2) # 3. 降噪 (去除小的白点或黑点) kernel np.ones((1, 1), np.uint8) opening cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel, iterations1) # 4. 膨胀/腐蚀操作用于连接断裂的笔画或分离粘连字符 (根据情况选择) # kernel2 np.ones((2,2), np.uint8) # dilated cv2.dilate(opening, kernel2, iterations1) # 将处理后的图像编码回字节流 is_success, buffer cv2.imencode(.png, opening) if is_success: return BytesIO(buffer).getvalue() else: # 如果编码失败返回原图 return image_bytes # 使用示例 ocr ddddocr.DdddOcr() with open(noisy_captcha.jpg, rb) as f: raw_image_bytes f.read() processed_image_bytes preprocess_captcha(raw_image_bytes) result ocr.classification(processed_image_bytes) print(f预处理后的识别结果: {result}) # 可以保存处理前后的图片对比效果 # with open(processed.png, wb) as f: # f.write(processed_image_bytes)预处理没有银弹不同的验证码需要不同的参数组合。多试试不同的阈值方法和形态学操作cv2.erode,cv2.dilate观察处理后的图像找到最适合当前验证码的方案。5. 处理滑块验证码除了字符验证码滑块验证码也越来越常见。ddddocr提供了slide_match方法专门处理这类问题。它的原理通常是比对滑块图和背景图的差异找到滑块需要移动到的缺口位置。5.1 滑块验证码识别原理滑块验证码一般由两部分组成背景图background一张带有缺口阴影或凹槽的图片。滑块图target一张需要被拖拽到正确位置的、带有凸起或图案的图片。ddddocr的slide_match函数通过图像匹配算法计算滑块图在背景图中的最佳匹配位置返回滑块左上角和右下角的坐标(x1, y1, x2, y2)。通常我们只需要X轴的坐标来计算滑动距离。5.2 代码实现与距离计算假设我们已经下载了背景图bg.jpg和滑块图target.png。import ddddocr import cv2 import numpy as np # 初始化滑块识别器注意参数 detFalse, ocrFalse slide ddddocr.DdddOcr(detFalse, ocrFalse) # 读取图片 with open(target.png, rb) as f: target_bytes f.read() with open(bg.jpg, rb) as f: background_bytes f.read() # 进行匹配 match_result slide.slide_match(target_bytes, background_bytes) print(f匹配结果: {match_result}) # 结果是一个字典例如{target: [x1, y1, x2, y2]} if target in match_result: x1, y1, x2, y2 match_result[target] # 通常滑块的滑动距离就是缺口左上角的x坐标 (x1) slide_distance x1 print(f滑块需要滑动的距离像素: {slide_distance}) else: print(未能匹配到滑块位置)关键点解析ddddocr.DdddOcr(detFalse, ocrFalse)这个参数组合是专门用于滑块匹配的模式。slide_match返回的坐标是滑块在背景图中匹配到的区域的左上角和右下角。对于最常见的从左向右滑动滑动的水平位移通常就是x1的值。有些滑块图本身带有完整的背景simple_targetTrue参数可以尝试优化匹配。5.3 模拟滑动操作识别出距离后下一步是用自动化工具模拟滑动。这里以Selenium为例演示如何计算轨迹并执行滑动。from selenium import webdriver from selenium.webdriver import ActionChains from selenium.webdriver.common.by import By import time import random # 假设我们已经获得了滑动距离 slide_distance (单位像素) # 以及网页上的滑块元素 slider_element driver webdriver.Chrome() driver.get(your_login_page_url) slider driver.find_element(By.ID, slider) # 根据实际情况定位滑块元素 # 创建动作链 actions ActionChains(driver) # 点击并按住滑块 actions.click_and_hold(slider).perform() # **关键模拟人的滑动轨迹而不是匀速移动** # 基本轨迹先快后慢最后可能有点抖动 total_distance slide_distance # 将总距离分成若干段 tracks [] current 0 # 设置一个阈值比如留出最后5-10像素进行微调或直接释放 mid_distance total_distance * 0.8 remain_distance total_distance - mid_distance # 前半段加速滑动 while current mid_distance: # 每次移动的距离是随机值模拟人手的不确定性 move random.randint(3, 6) if current move mid_distance: move mid_distance - current tracks.append(move) current move # 后半段减速滑动并加入小范围抖动 while current total_distance: move random.randint(1, 3) if current move total_distance: move total_distance - current tracks.append(move) current move # 可能还需要一点过冲再拉回 # tracks.append(random.randint(-2, 2)) # 执行滑动轨迹 for track in tracks: actions.move_by_offset(track, 0).perform() # 水平移动y轴偏移为0 time.sleep(random.uniform(0.01, 0.05)) # 每次移动后短暂停顿 # 释放滑块 actions.release().perform() time.sleep(1) # 等待结果验证 driver.quit()重要经验直接move_by_offset(slide_distance, 0)会被很多网站的反爬机制识别为机器行为。模拟人类滑动轨迹是成功的关键。轨迹应包含变速和随机停顿。此外有些网站会验证滑动轨迹的总时间太快或太慢都不行需要根据实际情况调整time.sleep的参数。6. 目标检测模式与复杂验证码处理对于一些更复杂的验证码比如点选图中特定文字或物体ddddocr的目标检测模式detTrue就能派上用场。它不识别具体文字而是找出图片中可能是“目标”的多个区域框。6.1 启用目标检测import ddddocr import cv2 import numpy as np # 初始化目标检测器 detector ddddocr.DdddOcr(detTrue, ocrFalse) # detTrue 启用检测模式 with open(complex_captcha.jpg, rb) as f: image_bytes f.read() # 进行检测返回一个列表每个元素是一个边界框 [x1, y1, x2, y2] bboxes detector.detection(image_bytes) print(f检测到 {len(bboxes)} 个目标框:) for i, bbox in enumerate(bboxes): print(f 框{i1}: 左上({bbox[0]}, {bbox[1]}), 右下({bbox[2]}, {bbox[3]}))6.2 结合OCR进行二级识别目标检测通常用于定位我们可以把检测到的每个框裁剪出来再用OCR模式去识别框内的文字。import ddddocr import cv2 import numpy as np from PIL import Image import io # 初始化两个实例一个用于检测一个用于识别 detector ddddocr.DdddOcr(detTrue, ocrFalse) recognizer ddddocr.DdddOcr() # 默认OCR模式 # 读取图片并转换为OpenCV格式用于裁剪 nparr np.frombuffer(image_bytes, np.uint8) img_cv cv2.imdecode(nparr, cv2.IMREAD_COLOR) # 进行目标检测 bboxes detector.detection(image_bytes) results [] for bbox in bboxes: x1, y1, x2, y2 bbox # 裁剪目标区域 cropped_img img_cv[y1:y2, x1:x2] # 将裁剪后的OpenCV图像转回字节流 is_success, buffer cv2.imencode(.jpg, cropped_img) cropped_bytes io.BytesIO(buffer).getvalue() # 对裁剪区域进行OCR识别 text recognizer.classification(cropped_bytes) results.append({ bbox: bbox, text: text }) print(f在区域 {bbox} 中识别到文字: {text}) # 可视化结果可选 for r in results: bbox r[bbox] text r[text] cv2.rectangle(img_cv, (bbox[0], bbox[1]), (bbox[2], bbox[3]), (0, 255, 0), 2) cv2.putText(img_cv, text, (bbox[0], bbox[1]-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2) cv2.imwrite(detection_result.jpg, img_cv)这种“检测识别”的两阶段策略非常适合验证码文字位置不固定或者需要点击图中特定词语的场景。7. 构建健壮的自动化填写流程识别只是第一步把它集成到一个完整的、能应对各种异常的自动化流程里才是项目成功的关键。7.1 流程封装与错误重试我们不能指望识别一次就100%成功。一个健壮的系统必须包含错误处理和重试机制。import ddddocr import requests import time from typing import Optional class CaptchaSolver: def __init__(self, retry_times3, ocr_betaFalse): self.ocr ddddocr.DdddOcr(betaocr_beta) self.retry_times retry_times def download_image(self, url, sessionNone, **kwargs) - Optional[bytes]: 下载验证码图片支持自定义session和headers try: if session is None: session requests.Session() response session.get(url, **kwargs) response.raise_for_status() # 检查HTTP错误 return response.content except Exception as e: print(f下载图片失败: {e}) return None def recognize_with_retry(self, image_bytes, preprocess_funcNone) - Optional[str]: 带重试和预处理的识别函数 for attempt in range(self.retry_times): try: # 可选预处理 if preprocess_func and callable(preprocess_func): processed_bytes preprocess_func(image_bytes) else: processed_bytes image_bytes result self.ocr.classification(processed_bytes) # 基础校验比如验证码通常是4-6位可以过滤掉明显错误的结果 if 3 len(result) 8: return result else: print(f第{attempt1}次识别结果长度异常: {result} 重试...) time.sleep(0.5) # 短暂等待后重试 except Exception as e: print(f第{attempt1}次识别发生异常: {e}) time.sleep(1) print(f识别失败已重试{self.retry_times}次) return None def solve_and_fill(self, captcha_url, input_element, submit_element, sessionNone, **kwargs): 完整的解决并填写流程 # 1. 获取图片 image_bytes self.download_image(captcha_url, session, **kwargs) if not image_bytes: return False, 无法获取验证码图片 # 2. 识别 captcha_text self.recognize_with_retry(image_bytes) if not captcha_text: return False, 验证码识别失败 # 3. 填写 (这里以Selenium为例实际可能是requests.post) try: input_element.clear() input_element.send_keys(captcha_text) submit_element.click() return True, captcha_text except Exception as e: return False, f填写或提交失败: {e} # 使用示例 if __name__ __main__: solver CaptchaSolver(retry_times2, ocr_betaTrue) # 假设我们已经有了driver和页面元素 # captcha_img_url driver.find_element(...).get_attribute(src) # input_box driver.find_element(By.ID, captchaInput) # submit_btn driver.find_element(By.ID, submitBtn) # success, msg solver.solve_and_fill(captcha_img_url, input_box, submit_btn, sessiondriver)7.2 验证码结果的后处理识别出来的文本可能包含空格、换行符或者将0识别为O。根据具体验证码的规则我们可以添加一些后处理逻辑。def postprocess_captcha_text(raw_text): 对识别出的验证码文本进行后处理 if not raw_text: return # 1. 去除空白字符 text raw_text.strip().replace( , ).replace(\n, ).replace(\r, ) # 2. 常见字符替换根据实际情况调整 common_mistakes { O: 0, # 大写字母O替换为数字0 I: 1, # 大写字母I替换为数字1 l: 1, # 小写字母l替换为数字1 Z: 2, S: 5, B: 8, # 可以继续添加其他易混淆字符对 } # 只替换那些看起来像混淆的比如长度是数字时 if text.isalnum() and len(text) 4: # 假设是4位纯数字验证码 corrected [] for char in text: corrected.append(common_mistakes.get(char.upper(), char)) text .join(corrected) # 3. 长度校验和格式校验 # 例如如果知道验证码必须是4位数字 if text.isdigit() and len(text) 4: return text else: # 如果不符合预期可以返回None触发重试或者尝试其他处理 return None # 或 return raw_text # 在识别流程中集成后处理 captcha_text solver.recognize_with_retry(image_bytes) if captcha_text: processed_text postprocess_captcha_text(captcha_text) if processed_text: print(f后处理后的验证码: {processed_text})7.3 集成到Web自动化中以Selenium为例一个完整的登录自动化脚本可能长这样from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import ddddocr import time class LoginAutomator: def __init__(self): self.driver webdriver.Chrome() self.wait WebDriverWait(self.driver, 10) self.ocr ddddocr.DdddOcr() def get_captcha_from_page(self): 从当前页面获取验证码图片和输入框 # 等待验证码图片加载 captcha_img_element self.wait.until( EC.presence_of_element_located((By.ID, captcha_img)) ) # 方法1获取src并下载 captcha_url captcha_img_element.get_attribute(src) # 这里需要实现一个下载函数注意处理相对路径和可能携带的token # 方法2更通用直接对元素截图 captcha_png captcha_img_element.screenshot_as_png input_box self.driver.find_element(By.ID, captcha) submit_btn self.driver.find_element(By.ID, loginBtn) return captcha_png, input_box, submit_btn def solve_captcha(self, image_bytes): 识别验证码 # 可以加入预处理和后处理 return self.ocr.classification(image_bytes) def login(self, username, password, login_url): try: self.driver.get(login_url) # 填写用户名密码 self.driver.find_element(By.ID, username).send_keys(username) self.driver.find_element(By.ID, password).send_keys(password) # 处理验证码 captcha_png, input_box, submit_btn self.get_captcha_from_page() captcha_code self.solve_captcha(captcha_png) if captcha_code and len(captcha_code) 4: input_box.clear() input_box.send_keys(captcha_code) submit_btn.click() # 检查是否登录成功 time.sleep(2) if dashboard in self.driver.current_url: print(登录成功) return True else: print(登录可能失败检查验证码或网络。) # 可以在这里加入重试逻辑比如刷新验证码再试一次 return False else: print(f识别出的验证码无效: {captcha_code}) return False except Exception as e: print(f登录过程发生异常: {e}) return False finally: # self.driver.quit() # 根据实际情况决定是否关闭 pass if __name__ __main__: automator LoginAutomator() success automator.login(your_username, your_password, https://example.com/login)8. 常见问题、优化策略与排错实录在实际使用中你肯定会遇到各种各样的问题。下面是我踩过的一些坑和解决方案。8.1 识别准确率不理想这是最常见的问题。别急着怪库可以按以下步骤排查和优化检查输入图片质量把下载或截图到的验证码图片保存下来用眼睛看是否清晰。如果人眼都难以辨认机器识别困难是正常的。尝试刷新页面获取更清晰的验证码。尝试不同的模型和参数切换betaTrue试试。使用set_ranges严格限定字符范围。对于彩色验证码尝试colors参数进行颜色过滤。实施图片预处理如第4.3节所述灰度化、二值化、降噪可以显著提升复杂背景验证码的识别率。没有一种预处理方法通吃所有需要针对你的验证码特点进行调整。后处理与校验利用业务逻辑。如果知道验证码是5位数字那么识别出6位或包含字母的结果可以直接判定为错误触发重试或刷新。融合策略如果条件允许可以同时使用ddddocr和其他轻量级OCR库如pytesseract对同一个验证码识别多次取出现次数最多的结果或者设计一个简单的投票机制。终极方案收集数据训练如果某个网站的验证码风格独特且固定识别率始终上不去可以考虑用ddddocr作者提供的dddd_trainer工具收集几百张该网站的验证码自己训练一个专用模型。这是最有效但也是最费事的方法。8.2 性能与资源问题初始化慢首次初始化DdddOcr()确实需要几秒到十几秒加载模型。务必确保在程序开始时只初始化一次然后全局复用。如果在Web服务器等需要多进程的环境中使用考虑在服务启动时初始化好模型或者使用单例模式。内存占用每个DdddOcr实例都会在内存中加载模型。如果同时需要OCR和Detect功能不要初始化一个ocrTrue, detTrue的实例这不可行而是根据需要初始化两个独立的实例。在长时间运行的服务中注意监控内存。GPU加速如果你有NVIDIA GPU且处理量巨大可以尝试启用GPU加速。首先确保安装了onnxruntime-gpu库注意与CUDA版本的兼容性然后初始化时设置use_gpuTrue。对于绝大多数个人或中小规模的自动化任务CPU已经足够快。8.3 特定网站的反爬应对验证码识别常常与反爬虫对抗。网站可能会频繁更换验证码样式导致你的预处理规则或模型失效。应对策略是使你的预处理逻辑更鲁棒或者定期更新模型。验证码与一次性的Token绑定你下载的验证码图片只能使用一次第二次提交即使识别正确也会失败。解决方案是在同一个会话Session内完成“获取图片-识别-提交”的整个流程确保Cookie或Token的一致性。滑块轨迹验证如第5.3节所述匀速滑动会被识别。必须模拟带加速度和随机停顿的人类轨迹。点选验证码需要结合目标检测(detTrue)先定位多个可点击项的位置然后模拟鼠标依次点击。点击顺序有时也是验证的一部分。触发频率限制识别和提交不要太快加入随机延迟(time.sleep(random.uniform(1, 3)))模拟真人操作间隔。8.4 错误处理与日志在生产环境中完善的错误处理和日志记录至关重要。import logging logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) def safe_recognize(ocr, image_bytes, context): try: result ocr.classification(image_bytes) logging.info(f{context} 识别成功: {result}) return result except ddddocr.DdddOcrError as e: logging.error(f{context} ddddocr内部错误: {e}) return None except Exception as e: logging.exception(f{context} 识别过程发生未知异常) # 会打印堆栈信息 return None # 保存识别失败的图片用于后续分析和模型优化 def save_failed_image(image_bytes, expected_textNone, recognized_textNone): filename ffailed_{int(time.time())}.jpg with open(f./failures/{filename}, wb) as f: f.write(image_bytes) # 可以同时记录一个元数据文件记录预期结果和识别结果 with open(f./failures/{filename}.meta, w) as f: f.write(fexpected:{expected_text}, recognized:{recognized_text})建立一个“失败案例库”定期分析哪些类型的验证码容易识别错误能帮你持续优化预处理和后处理策略。8.5 关于ddddocr的版本与兼容性关注ddddocr的GitHub仓库及时更新版本。新版本可能会修复bug、提升识别率或增加新功能。同时注意你的Python版本是否在库的支持范围内通常支持主流版本。如果遇到安装或导入错误首先检查Python版本和系统架构64位。在Windows上可能需要安装Visual C Redistributable运行库。最后记住任何自动化验证码识别的行为都应遵守目标网站的服务条款仅用于合法授权的自动化测试或个人学习研究避免对他人服务造成不必要的负担。