☰
图形验证码识别全链路:选型、预处理与Web自动化框架集成
2026/10/21 8:58:53 网站建设 项目流程

开头

每次在 Web 自动化框架里跑到登录步骤就突然停住,十有八九是撞上了图形验证码。这东西对真人来说有时都得眯着眼睛辨认,对自动化脚本来说更是一道硬关卡。做测试的朋友应该都体会过:运维环境功能可以关,但预发布环境还关不掉;验证码本身看着不难,但脚本总是识别失败,一失败就要等刷新、等下一次重试,整个用例的执行时间直接翻倍。

这篇文章我想把图形验证码处理这条链路完整拆开,从验证码类型、方案选型、图像预处理、OCR 识别,到和 Selenium、Playwright 这类 Web 自动化框架真正集成,一次性讲透。适合正在维护自动化测试框架的测试开发、刚接触自动化的小白,以及被验证码卡到头疼还找不到头绪的朋友。内容以实际项目为主,所有步骤都能直接抄走用。

1. 图形验证码为什么是自动化测试的拦路虎

1.1 常见验证码类型与识别难度分级

先给常见图形验证码做个分级,我从实际项目里遇到的频率和识别难易程度出发,列了一张表:

验证码类型典型样式识别难度自动化处理的常见问题
纯数字 / 字母4位或6位字符较低字体扭曲、颜色干扰、字符粘连
数字+字母混合大小写随机组合中等容易把0认成O,1认成l,大小写敏感
中文汉字2到4个词组较高字库覆盖不足,生僻字识别率低
数学运算8+3=? 之类中等先识别再计算,一步出错全错
滑块验证拖动缺口到指定位置较高依赖轨迹模拟和缺口定位,稳定性差
点选文字 / 图像按顺序点击图中文字或物品很高需要目标检测模型辅助,成本高
极验 / 腾讯系行为验证混合图形+行为轨迹很高需要逆向或专门的模拟策略,维护成本高

这里我重点展开纯图形验证码,也就是“图片上画了几个字符,让你输入”的这类。它是自动化测试中最常见、也是最容易通过技术手段解决的一类。滑块和点选类后续有机会单独写,但它们背后的图像定位思路和图形验证码有互通之处。

1.2 验证码在测试链路中的位置

验证码几乎只出现在两类地方:登录前和操作敏感动作前。登录前不用多说,每次自动化用例要走完整登录流程就绕不开。敏感动作前的验证码更麻烦,比如修改密码、绑定手机号、提交订单、领取优惠券,触发时机不确定,可能在用例中段突然弹出来。

对自动化脚本来说,验证码带来的影响不仅是“识别失败”,而是整个用例的稳定性崩塌:

  • 用例时间不可控。一次识别失败就要重新加载验证码、重新识别,几轮下来用例超时。
  • 用例结果不可复现。同样的代码,有时候识别成功,有时候失败,测试报告里飘红一片。
  • 大量用例被卡在登录层。尤其回归测试需要多账号登录,每个账号跑一次登录就卡一次。

所以,验证码处理不是“锦上添花”的细节,而是自动化框架能不能稳定跑下去的基础设施之一。

1.3 为什么不能一刀切“绕过去”

很多刚接触自动化的同学第一反应是:能不能把验证码跳掉?比如调用后门接口直接获取登录态。这在纯接口测试场景下完全可行,但 Web 自动化测试的核心目标是验证前端流程、交互逻辑和联调结果,如果你直接绕过登录,页面跳转、Cookie 传递、权限控制这些环节就全部失去了验证意义。

而且验证码出现的位置往往不只是登录,还有下单确认、修改资料等业务环节。这些环节没法简单通过“跳过”处理,必须让自动化具备“看到验证码并正确输入”的能力。这就是图形验证码识别处理存在的意义。

2. 图形验证码处理方案选型与取舍

2.1 四类主流方案对比

我在不同项目里试过四类方案,分别适用于不同阶段和不同环境,这里先说结论再展开:

方案实现难度维护成本稳定性适用场景
测试环境关闭验证码极低无100%开发测试环境、持续集成流水线
万能验证码 / 固定验证码极低无100%所有非生产环境
图像预处理 + OCR中等中70%~95%图形验证码干扰不严重的场景
第三方云端识别低高(按量付费)95%以上复杂验证码、生产环境联调

先说前两个,这两个方案往往是自动化的“保底手段”,也是我接手任何一个新项目时最先确认的资源。

2.2 测试环境关闭验证码与万能验证码

关闭验证码的方式很多:后台配置开关、通过接口开关、测试数据初始化时设置。最理想的是框架层面做一个基础抽象,让代码自动判断当前环境,如果是测试环境,直接跳过验证码输入步骤;否则才走 OCR 或标准识别流程。

万能验证码也很常见。就是在验证码校验逻辑里预留一个超级账号或固定字符串,测试时输入它,无论后台生成什么验证码都能通过。这种方案的好处是代码根本不用改,自动化脚本正常走输入流程,区分度仅在于“输入的内容是万能值”。但要注意两点:一是万能验证码千万不能上线到生产环境,否则安全形同虚设;二是如果测试环境偶尔因为数据初始化问题没有开启万能验证码,脚本回像“谜语人”一样摸不着头脑,得提前在配置中心里固定好。

我不建议把这两类方案作为唯一方案。因为预发环境、灰度环境往往不会开放这类开关,而那些环境恰恰是最需要跑完整回归的。所以一个好框架,应当优先走“真实识别”,并保留环境开关做兜底。

2.3 图像预处理 + OCR 的技术原理和应用边界

OCR 识别图形验证码的原理并不神秘:图像输入后,先做预处理提升字符和背景的对比度,再用 OCR 引擎把图像特征映射为文字,最后配合后处理规则(字符白名单、长度限制)提升准确率。

主流的 OCR 引擎是 Tesseract,配合 Python 的 pytesseract 库,适合中等干扰程度的验证码。判断适不适合的黄金准则:验证码图片里有没有明显的干扰线、噪点或背景花纹。如果只是简单的扭曲字体,比如深色字浅色底,预处理之后识别率能到 90% 以上。如果是渐变背景加密集噪点,识别率会掉到 50% 以下,那就要考虑云端识别或另想办法。

2.4 第三方云端识别如何选

市面上现在有不少验证码识别服务,它们通常提供 HTTP 接口,把图片传上去,返回文本结果。优点是识别率高、省掉模型训练成本;缺点是按次计费、依赖外网,内网隔离的项目没法用。

实际项目中我通常这么衔接:给 OCR 识别层做一个统一接口,内部先跑 Tesseract,如果结果的置信度不够或者连续重试仍然失败,再调用云端接口。这样既控制了成本,也保证了复杂场景下的成功率。

2.5 底层框架与工具选型

图形验证码识别的核心依赖无外乎三个:截图工具、图像处理库、OCR 引擎。截图工具由自动化框架自带;图像处理库我和大部分团队一样用 OpenCV;OCR 引擎用 Tesseract,这里先给出基础环境:

# 安装 Python 依赖 pip install opencv-python pytesseract requests # Ubuntu/Debian 安装 Tesseract 引擎(并支持中文) apt-get install -y tesseract-ocr tesseract-ocr-chi-sim

另外说一句,如果你想识别效果再好一点,可以下载训练好的专用数字验证码字库放到 Tesseract 的 tessdata 目录,识别率和原版相比有质的提升。这个点我在后面的章节详细讲。

3. 图形验证码识别核心链路:从截屏到输出文本

3.1 第一步:拿到干净的验证码图片

识别之前必须保证图片质量。很多自动化脚本识别率低,不是算法不行,而是截图环节就出了幺蛾子。我用 Selenium 和 Playwright 分别给出两个获取图片的方式。

Selenium 获取验证码元素的 base64 截图:

import base64 from selenium.webdriver.common.by import By # 先确保页面结构加载完整 captcha_element = driver.find_element(By.CSS_SELECTOR, "img.captcha-img") # 方式一:直接截图并保存为文件(推荐) captcha_element.screenshot("captcha_raw.png") # 方式二:截图后转成 base64 字符串,方便直接扔给处理函数 screenshot_b64 = captcha_element.screenshot_as_base64 raw_data = base64.b64decode(screenshot_b64) with open("captcha_raw_base64.png", "wb") as f: f.write(raw_data)

Playwright 获取验证码元素截图:

# 使用 Playwright 的同步 API cap_locator = page.locator("img#captcha-img") cap_locator.screenshot(path="captcha_raw_playwright.png") # 也可以直接截全页面然后裁剪,但我更推荐直接对元素截图,省掉坐标计算

这里有几个注意事项是截图阶段很关键的:

  • 等待验证码图片加载完成再截图。如果脚本点击“刷新验证码”后立刻截图,很容易拍到旧图或半加载图,识别出来的是上一个验证码,后续输入必然失败。建议显式等待图片的 src 或 class 变化,或者干脆固定等待 0.5~1 秒。
  • 用元素截图,不要用全屏截图再裁剪。全屏截图依赖元素坐标,如果页面有滚动或响应式布局变化,坐标很容易偏移,导致裁剪到旁边的广告位。
  • 图片可能带有 data URI 前缀。如果验证码图片直接是 base64 内嵌的(src 以 data:image 开头),那根本不需要浏览器截图,直接把 base64 解码成文件即可,速度和清晰度都有保障。

3.2 第二步:图像预处理,这一步决定识别率上限

拿到原始图像后,不能直接扔给 OCR。原图往往有背景纹理、干扰线、字符颜色与背景接近等问题,直接识别可能连 20% 都不到。我常用的预处理流程依次是:灰度化、二值化、降噪、去边框或去干扰线。

下面是一段我用 OpenCV 实现的预处理函数,覆盖大部分图形验证码的清洗需求:

import cv2 import numpy as np def preprocess_captcha(image_path: str) -> np.ndarray: """图形验证码预处理:灰度 -> 二值化 -> 形态学降噪""" # 1. 保持原始图像和通道信息 img = cv2.imread(image_path) if img is None: raise ValueError(f"无法读取图片: {image_path}") # 2. 灰度化:去掉颜色,保留亮度信息 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 3. 直方图均衡化:增强字符与背景的对比度 gray = cv2.equalizeHist(gray) # 4. 自适应二值化:代替固定阈值,适应不同亮度的验证码 binary = cv2.adaptiveThreshold( gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, # 邻域大小,可根据图片尺寸微调 15 # 常数 C,越大越容易保留浅色字符 ) # 5. 形态学操作:先腐蚀去掉细小的噪点,再膨胀恢复字符粗细 kernel = np.ones((3, 3), np.uint8) cleaned = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel) cleaned = cv2.medianBlur(cleaned, 3) return cleaned

二值化是整条链路中最需要调参的一步。固定阈值 cv2.threshold 在统一背景下挺好用,但验证码图片的背景色经常变化,今天浅黄明天浅蓝,固定阈值会导致字符被吞。自适应二值化通过局部邻域计算阈值,对这种场景明显更稳。常见验证码适配的关键参数如下:

图像特点邻域大小(blockSize)常数 C推荐操作
字符粗大、图片清晰1510直接自适应二值化即可
背景干扰线密集3115先高斯模糊再二值化
字符细长、容易断线1120二值化后额外做一次膨胀
背景与字符颜色接近5110先做直方图均衡化

如果你看到的验证码带明显的弧线干扰,预处理阶段还可以加一步:按连通域面积过滤,只保留面积超过某个阈值的连通区域。干扰线通常是一条细长的连通域,面积占比远小于字符区域,过滤掉之后效果立竿见影。这个思路适合固定样式、干扰线位置相对稳定的验证码,通用性稍差,但对特定项目很有用。

3.3 第三步:用 Tesseract 做 OCR 识别

预处理完的图片就可以喂给 Tesseract 了。这里说的“喂”不只是调一个函数,关键是 Tesseract 的模式和参数设置。看一段代码:

import pytesseract from PIL import Image def recognize_captcha(processed_img: np.ndarray) -> str: """将预处理后的图像识别为字符串""" # OpenCV 的 BGR 数组先转成 PIL 的 RGB 格式 rgb_img = cv2.cvtColor(processed_img, cv2.COLOR_BGR2RGB) pil_img = Image.fromarray(rgb_img) # 给 Tesseract 一个字符白名单:只允许大写字母和数字 config = ( "--psm 7 " "-c tessedit_char_whitelist=ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz0123456789" ) text = pytesseract.image_to_string(pil_img, config=config) text = text.strip().replace(" ", "").replace("\n", "") return text.upper()

这里有一个核心参数 psm(Page Segmentation Mode),它告诉 Tesseract 图像应该按什么布局来切分。对图形验证码,我强烈建议用--psm 7(单行文本模式),因为绝大多数验证码都是一行字符。如果同时出现多行词组的验证码,可以用--psm 6。很多识别失败案例都是因为默认模式把验证码当成整页文档,切分出来一堆乱七八糟的框。

tessedit_char_whitelist的作用是告诉 Tesseract 只需要在这些字符里猜。如果明明是纯数字验证码,你却允许字母参与猜测,那么数字 8 被认成字母 B 的概率就会增高。所以白名单要精确,纯数字验证码就只放0123456789。

3.4 专用训练字库与二次校验

对识别率迟迟上不去的场景,可以自己训练 Tesseract 专用字库。这一步在小范围固定字体的验证码上非常有效,能把识别率从 70% 拉到 95% 以上。训练流程大致是这样的:

  1. 收集 100~200 张同一款式的验证码图片,并记录每张图片对应的真实文本(就是每条验证码应该是什么)。
  2. 用收集的数据生成 Tesseract 的训练文件:对每张图片做 box 标注(可以用 JTessBoxEditor 工具可视化标框)。
  3. 执行tesseract训练命令生成 tr 文件、字体特征文件和最终 traineddata 文件。
  4. 把训练好的 traineddata 放到 tessdata 目录,并在调用时通过--traineddata指定。

这个过程需要花一些时间,但对固定业务系统来说性价比极高。我的建议是,先判断验证码是不是内网业务系统里固定样式、长期不换的。如果是,优先考虑训练专用字库而不是反复堆预处理参数。如果验证码样式经常换,训练字库容易白费。

另外,OCR 识别出的结果不要直接拿去用,加一层校验逻辑会更稳妥:

def validate_and_normalize(text: str, expected_length: int = 4) -> str: """校验识别结果,如长度不对则返回空字符串,让外层走重试""" if len(text) != expected_length: return "" # 常见误识别纠正:数字 0 和字母 O、字母 I 和数字 1 等 # (如果业务是纯数字,这里可以统一替换) text = text.replace("O", "0").replace("I", "1").replace("l", "1") return text

这一步属于经验积累,不同项目的识别混淆点不一样。比如电商系统的纯数字验证码,把 OCR 输出的全部非数字字符替换成数字是很常见的操作。如果业务允许大小写不敏感,全部转大写也能救回一部分混淆结果。

4. 在 Web 自动化框架中落地验证码处理

4.1 统一封装一个验证码处理模块

框架层落地时,最忌讳把识别逻辑散落在各个用例里。我在项目中习惯把验证码处理抽象成一个独立模块,统一暴露给自动化脚本调用。模块内部划分三层:

  • 图片获取层:负责从页面元素或接口响应中获取验证码图片;
  • 识别层:负责预处理 + OCR + 云端识别,返回识别文本;
  • 执行层:负责输入验证码、点击提交、判断是否成功、失败重试。

这样做的价值在于,底层怎么识别对上层透明。今天用 Tesseract,明天换成识别服务,用例代码一行都不用改。

4.2 在 Selenium 中实现完整登录流程

下面给出一个在 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 time def solve_captcha_and_login(driver, username, password, is_test_env=False): # 输入用户名和密码 driver.find_element(By.ID, "username").send_keys(username) driver.find_element(By.ID, "password").send_keys(password) # 定位验证码图片 captcha_img = WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, "img.captcha-img")) ) captcha_img.screenshot("captcha_cur.png") # 识别验证码文字 if is_test_env: captcha_code = "1234" # 测试环境的万能验证码 else: processed = preprocess_captcha("captcha_cur.png") captcha_code = recognize_captcha(processed) # 校验长度,如果异常则点击刷新并重新获取一次 if captcha_code is None or len(captcha_code) < 4: driver.find_element(By.CSS_SELECTOR, "a.refresh-captcha").click() time.sleep(0.5) driver.find_element(By.CSS_SELECTOR, "img.captcha-img").screenshot("captcha_cur.png") processed = preprocess_captcha("captcha_cur.png") captcha_code = recognize_captcha(processed) # 输入验证码并登录 driver.find_element(By.ID, "captcha").send_keys(captcha_code) driver.find_element(By.ID, "loginBtn").click() # 真正的核心:判断是否登录成功,而不是猜时间 try: WebDriverWait(driver, 8).until( EC.visibility_of_element_located((By.CSS_SELECTOR, "div.user-panel")) ) print("登录成功") return True except Exception: error = driver.find_element(By.CSS_SELECTOR, "span.login-error") print(f"登录失败,原因:{error.text}") return False

这段代码里有几个容易被忽略的细节,值得单独拿出来讲:

  • 获取验证码元素前要等待可见,不要刚开始抢。如果页面有异步接口在生成验证码,立刻截图会拍到旧图或者空白图,这是 Selenium 新手最容易踩的坑。
  • 识别失败不是直接抛异常,而是先刷新验证码再识别一次。很多验证码点击刷新后会生成新图,再试一次往往就成功了。
  • 登录后的判断必须用显式等待。不能用time.sleep(5)然后看一眼页面就完事。页面加载快慢受网络影响,sleep 长了浪费时间,短了误报失败。用 WebDriverWait 等待登录成功后的标志元素,才是靠谱的方式。

4.3 在 Playwright 中实现自动识别

Playwright 的 API 相比 Selenium 更简洁一些,并且自动处理了等待逻辑,示例代码可以更干净:

from playwright.sync_api import sync_playwright import time def playwright_login(): with sync_playwright() as p: browser = p.chromium.launch(headless=False) page = browser.new_page() page.goto("https://example.com/login") page.fill("#username", "test_user") page.fill("#password", "test_password") # 等待验证码图片稳定可见 captcha_locator = page.locator("img.captcha-img") captcha_locator.wait_for(state="visible") captcha_locator.screenshot(path="captcha_pw.png") processed = preprocess_captcha("captcha_pw.png") captcha_code = recognize_captcha(processed) page.fill("#captcha", captcha_code) page.click("button:has-text('登录')") # 等待登录成功的标志元素出现,超时会自动抛出异常 page.wait_for_selector("div.user-panel", timeout=8000) print("登录成功") browser.close()

Playwright 自带的wait_for_selector实际上兼了 Selenium 里 WebDriverWait 的作用,代码少了一截。如果你负责的项目已经切到 Playwright,建议优先用它的自动等待能力,不要再手写 sleep,这会让用例稳定很多。

4.4 重试策略与用例级自愈

不管识别算法调得多好,验证码识别都不可能做到 100%。所以自动化框架在处理验证码时必须内建重试机制。我的经验是做成三层重试:

  1. 识别前重试:预处理结果如果明显异常(比如 OCR 返回空、返回长度不等于验证码位数),直接刷新验证码重来,不进入填写流程。
  2. 登录后重试:填写验证码后点击登录,如果页面提示“验证码错误”,自动刷新验证码并重新执行一次填充。注意此时账号密码可以不用重新输入,但有些系统会在登录提交失败后清空密码框,所以重试流程里要考虑重新填一遍。
  3. 用例级重试:整个登录步骤连续失败超过 3 次后,终止用例并标记失败,同时截图存档。不要无限制重试下去,否则一个故障用例会白白消耗十几分钟。

我给团队常用的次数阈值是:单次验证码识别重试最多 3 次,超过即走用例级自愈或失败。这个阈值既给了验证码算法自我纠正的空间,也保证了整体用例的执行时间不会因为服务偶发抖动而失控。

4.5 多账号与并行执行时的特别注意事项

回归测试经常要跑几十个账号并行登录,验证码模块在并发行下会遇到两个额外问题。

第一个是浏览器上下文隔离。Selenium Grid 下每个节点是独立浏览器进程,验证码截图和登录态不会互相污染,这还好。但如果你在同一个浏览器进程里用多标签页并行登录,验证码的 Cookie 和页面状态可能串台,强烈建议每个并行任务使用独立浏览器上下文。Playwright 的browser.new_context()天然支持这个,隔离性极佳。

第二个是验证码刷新时机。并行跑同一个系统的用例时,如果多台机器同时刷新验证码,后端接口可能会有频率限制,导致一部分刷新请求失败返回旧图。这个问题的表象是识别结果总是对不上。排查思路是看后端验证码刷新接口的日限量告警,或者在框架里给验证码刷新动作加一个全局互斥锁 / 小延时。个人经验是小规模并行加 200~500 毫秒的随机延时就能缓解,真正的大并发还得从后端接口限流入手。

5. 常见问题排查与实战避坑

5.1 识别率低,问题到底出在哪一环

碰到“验证码识别率老上不去”的情况,我整理了问题定位速查表:

现象可能原因排查方向
OCR 总是输出空字符串预处理过度,字符被吞掉二值化阈值过大,减少常数 C;检查形态学操作是否腐蚀过度
OCR 输出乱码干扰线太多增加降噪步骤,先高斯模糊再接二值化;使用连通域过滤
长度对,字符错一半字体不在 Tesseract 训练范围训练专用字库;缩小白名单范围
识别正确却登录失败验证码时效太短或请求带着旧 Cookie缩短截图到输入的间隔;登录前刷新 Cookie
偶发失败,重试即成功网络延迟导致图片半加载在截图前等待图片加载完成的信号,别用固定 sleep
验证码刷新后永远失败后端缓存旧图查看接口是否设置了 CDN 缓存头,测试环境可请求禁用

如果用这个表逐项排查,大部分识别率问题都能找到答案。这里顺便说一句,遇到问题时先记录验证码图片和识别结果,对比着看比凭空想象高效得多。我习惯在处理函数里默认保存最近 50 张验证码原图和预处理结果图,排查问题时有据可查,这个习惯帮过大忙。

5.2 识别正确但登录总失败的细节坑

这是最让人抓狂的情况:OCR 输出的文本肉眼看着完全正确,但系统就是提示验证码错误。

我遇到过一种典型的“隐性时效”场景。页面刚打开时加载了一个验证码,脚本把页面晾了很久才开始操作,等填完账号密码,验证码早已过期。表面上你看不到任何提示,但提交时后端发现验证码过期,就返回错误。解决办法是:在登录流程中,先填账号密码,再截图验证码,截完图立刻填,中间不要夹带其它耗时操作。

还有一种是验证码校验绑定的是登录请求的 Session Cookie。如果自动化脚本在“获取验证码”和“提交验证码”之间不小心发起了一个会刷新 Session 的请求(比如点了一下页面其他区域触发了异步刷新),后端认为会话变了,旧验证码就失效了。排查方式是在提交时对比验证码刷新前后请求的 Cookie 是否有变化,如果有,就该在代码里避免这类额外请求。

5.3 固定 sleep 的滥用怎么破

代码里写time.sleep(2)等待验证码加载,是所有自动化工程师都干过的事。sleep 确实是调试阶段最简单的办法,但它有两个先天缺陷:无谓等待和误判成功。网络好的时候 2 秒是多余的,网络差的时候 2 秒又不够。而且 sleep 不校验结果,只要过了时间就继续往下走,哪怕图片还是空白。

我更推荐的做法是围绕验证码自身的特点做轮询式等待。比如验证码图片的 src 里通常带时间戳或随机参数,可以用 Playwright 或 Selenium 的条件等待,轮询判断这个 src 是否从“旧值”变成了“新值”。如果验证码图片接口的响应包含Set-Cookie,还可以在代码层面显式等待验证码接口的响应完成,然后再截图。这样既快又稳。

5.4 并行跑用例时的验证码冲突

这里再补充一个实战场景。某次大规模回归,框架并行跑 20 个账号,结果有一小部分用例登录后进入了别人的会话,排查到最后,发现是登录用例里用了共享的一个静态变量在保存验证码 Cookie,并行执行时变量被覆盖了。这种低级错误在并发场景下特别容易暴露。

解决办法是:验证码相关的 Cookie、验证码文本、处理进度等状态,一律保存在“当前任务上下文”对象里,不要放模块级全局变量。框架如果用线程池跑用例,务必要让每个线程有独立的上下文实例。不要问我为什么强调这点,团队里已经不止一次在这上面栽跟头了。

5.5 识别服务不可用时的降级策略

如果你的框架接入了云端识别服务,还会遇到外网 API 超时、限流或故障的情况。测试总不能因为一个外部服务挂了就全线红。我给框架设计的降级顺序是:

  1. 本地 Tesseract 优先,识别失败才请求云端;
  2. 云端超时或返回异常,自动切换回本地 OCR 结果;
  3. 本地和云端都不理想,走万能验证码(仅限非生产环境);
  4. 所有手段都用完仍失败,标记为“可重试失败”,由用例级 Runner 整体重跑该用例。

这套降级策略我要求所有接入验证码模块的团队都落地。它的好处是把外部依赖的影响控制在最小范围,测试平台不会因为验证码识别服务间歇性故障而大面积报红。

写在最后

图形验证码识别这件事,说到底没有银弹。不同项目用的验证码风格千差万别,今天解决了的方案,明天系统升级验证码加了两条干扰线,识别率可能又掉一半。我个人的体会是,处理这类问题要抓住关键环节:先把图片拿干净,再把预处理参数调准,最后用重试和降级把成功率兜起来。这三点做好,大部分图形验证码场景都能稳定跑起来。

如果验证码升级到滑块验证、点选验证这类更复杂的形式,很多团队选择在测试环境关掉验证码,或者在框架里引入专门的验证码识别服务来应对,这些方案我在文中的选型部分都有提到。对大多数做 Web 自动化的人来说,先把手头图形验证码的处理链路打通,已经能解决绝大多数登录难、被卡住的问题了。希望这篇攻略能让你少走几步弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询