news 2026/7/6 23:40:37

Web自动化测试中验证码处理与Cookie管理的实战策略

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Web自动化测试中验证码处理与Cookie管理的实战策略

1. 项目概述:当自动化测试遇上验证码与Cookie

做Web UI自动化测试的朋友,估计都绕不开两个“老大难”问题:验证码处理和Cookie管理。标题里的“12_web自动化测试 _验证码处理和cookie”,一看就是某个系列课程或实战总结的第十二章,核心就是解决这两个在自动化脚本中高频出现、又让人头疼的拦路虎。这不仅仅是写几行代码点个按钮那么简单,它背后涉及的是对Web应用安全机制的理解、对HTTP协议会话状态的掌控,以及在实际项目中如何设计出既稳定又灵活的测试策略。

简单来说,验证码是系统用来区分人和机器的,而Cookie是浏览器用来维持用户状态的。我们的自动化脚本作为“机器”,要模拟“人”的操作,就必须找到合适的方法绕过或处理验证码,同时又要像真人浏览器一样,正确地携带和使用Cookie,保持登录态、传递用户偏好。处理不好,脚本要么卡在登录页,要么动不动就掉线,测试的稳定性和效率无从谈起。这篇文章,我就结合自己趟过的坑,把验证码处理和Cookie管理的核心思路、常见方案以及那些文档里不会写的实操细节,给你掰开揉碎了讲清楚。无论你是刚接触UI自动化的新手,还是想优化现有框架的老手,这里都有你能直接拿去用的“解药”。

2. 验证码处理:从“硬刚”到“智取”的实战策略

验证码的存在,本质上是为了增加自动化攻击的成本,保护系统安全。但对于自动化测试而言,它就成了一个必须跨越的障碍。处理验证码没有银弹,需要根据验证码的类型、复杂度和项目实际情况来选择策略。核心原则是:在保证测试目的的前提下,寻求最高效、最稳定的解决方案,而不是追求百分百的通用识别。

2.1 常见验证码类型与应对思路分析

首先,我们得知道自己面对的是什么“敌人”。常见的Web验证码主要有以下几种:

  1. 数字/字母图片验证码:最经典的形式,由扭曲、粘连的数字字母组成,可能有背景干扰线。早期很多系统使用,现在逐渐减少,但仍有存量。
  2. 滑动拼图验证码:需要将缺失的拼图块拖动到正确位置。考验的是对图像缺口位置的识别和模拟拖动轨迹。
  3. 点选文字验证码:给出图片和一组文字,要求按顺序点击图片中指定的文字。难点在于文字识别和坐标定位。
  4. 算术验证码:如“3+5=?”,需要计算并输入结果。通常比较简单,但需要解析图片中的算式。
  5. 行为验证码(如极验、腾讯云验证码):集成了多种交互,如滑动、点选、推理等,背后有复杂的行为分析模型,防御能力最强。
  6. 短信/邮箱验证码:需要从短信或邮件中提取动态码。这涉及到另一个通道(手机收件箱或邮件服务器)的信息获取。

对于测试而言,我们的目标不是破解验证码(那是安全研究的范畴),而是让测试流程能顺利进行下去。因此,策略上可以分为几个层次:

  • 绕过/禁用:这是最理想的情况,需要开发配合。在测试环境,为特定的测试账号或IP地址设置白名单,或者提供一个万能验证码(如“0000”)。
  • 后端接口获取:有些系统在生成验证码的同时,会有一个仅供内部或测试使用的接口,返回验证码的明文。这是最稳定可靠的方式。
  • OCR识别:对于简单的图形验证码,使用光学字符识别技术。识别成功率受图片复杂度影响大。
  • 第三方打码平台:将验证码图片发送到专业平台,由人工或高精度模型识别后返回结果。需要付费,但稳定性和通用性较好。
  • Cookie/Session跳过:对于登录后的操作测试,有时可以手动登录一次,然后获取有效的Cookie直接用于脚本,完全避开登录环节。

注意:在选择任何识别方案前,务必与项目负责人、开发人员沟通,并确认其符合法律法规和公司安全政策。严禁对生产环境或他人系统进行未经授权的自动化识别尝试。

2.2 实战方案一:测试环境万能验证码与后端接口调用

这是成本最低、稳定性最高的方案,应作为首选。

场景:你拥有测试环境的部署权限或能与开发团队紧密协作。

操作步骤

  1. 沟通确认:向开发团队提出需求,说明自动化测试需要稳定绕过验证码。建议的方案是:在测试环境部署时,通过配置文件开关控制验证码的难度,或者为测试专用的账号/IP设置验证码白名单。
  2. 实现万能验证码:让开发在验证码校验逻辑中增加一个判断,如果输入的验证码是某个特定字符串(如“test”或“automation”),则直接校验通过。你在脚本中固定输入这个字符串即可。
  3. 调用后端接口:如果系统有验证码生成接口(例如/api/captcha/generate),通常也会有一个配套的获取接口(例如/api/captcha/value?key=xxx)。你需要:
    • 首先调用生成接口,获取一个本次验证码对应的唯一key(可能放在响应头、Cookie或响应体里)。
    • 然后调用获取接口,传入这个key,获取明文的验证码。
    • 在UI页面上输入这个验证码。
    • 这里的关键是模拟浏览器行为,处理好接口调用与页面操作之间的Session或Token关联。

Python + Selenium 示例(假设有获取接口)

import requests from selenium import webdriver driver = webdriver.Chrome() driver.get("https://test.example.com/login") # 1. 首先,可能需要从页面中获取生成验证码时使用的token或key # 例如,可能隐藏在某个img标签的data-key属性里,或者由前端JS生成 captcha_img = driver.find_element(By.ID, “captcha-img”) captcha_key = captcha_img.get_attribute(“data-key”) # 假设如此 # 2. 调用后端接口获取明文验证码(注意处理Cookie同步) session = requests.Session() # 将Selenium驱动当前页面的Cookie同步到requests session selenium_cookies = driver.get_cookies() for cookie in selenium_cookies: session.cookies.set(cookie[‘name’], cookie[‘value’]) # 调用获取验证码的接口 api_url = f“https://test.example.com/api/captcha/value?key={captcha_key}” response = session.get(api_url) captcha_text = response.text # 假设接口直接返回文本 # 3. 在页面上输入验证码 driver.find_element(By.ID, “captcha-input”).send_keys(captcha_text) # ... 继续输入用户名密码并登录

实操心得

  • 与开发定好规范,比如万能验证码的字符串、接口的路径和参数,并写入项目的测试Wiki,避免后续人员不知情。
  • 使用requests.Session()可以自动管理Cookie,确保接口调用和浏览器操作处在同一个会话中,这是模拟关联请求的关键。
  • 如果接口需要额外的认证头(如Token),可能需要从页面JavaScript变量或网络请求中提取。

2.3 实战方案二:OCR识别与第三方打码平台集成

当无法从后端直接获取验证码时,我们才考虑识别方案。

对于简单图片验证码(OCR): 可以使用开源的Tesseract OCR库,但识别率对图片质量要求高。通常需要先对图片进行预处理。

预处理+识别步骤

  1. 截图:定位并截取验证码图片元素。
  2. 预处理:转灰度、二值化、降噪、去除干扰线、字符分割。这一步非常关键,直接决定识别成功率。
  3. 识别:使用Tesseract或其它OCR引擎进行识别。
  4. 后处理:根据验证码可能的字符集(如纯数字)对识别结果进行校正。

Python示例(使用pytesseract和PIL)

from PIL import Image, ImageFilter, ImageEnhance import pytesseract from selenium import webdriver driver = webdriver.Chrome() driver.get(“login_page_url”) # 定位验证码元素并截图 captcha_element = driver.find_element(By.ID, “captcha-img”) captcha_element.screenshot(‘captcha.png’) # 图像预处理 image = Image.open(‘captcha.png’) # 转灰度 image = image.convert(‘L’) # 二值化 (阈值可根据实际情况调整) threshold = 150 image = image.point(lambda p: p > threshold and 255) # 降噪 (中值滤波) image = image.filter(ImageFilter.MedianFilter(size=3)) # 增强对比度 enhancer = ImageEnhance.Contrast(image) image = enhancer.enhance(2.0) image.save(‘captcha_processed.png’) # OCR识别,并配置只识别数字(如果验证码是数字) custom_config = r’--oem 3 --psm 7 -c tessedit_char_whitelist=0123456789’ captcha_text = pytesseract.image_to_string(image, config=custom_config).strip() print(f“识别结果: {captcha_text}”) # 输入识别结果 driver.find_element(By.ID, “captcha-input”).send_keys(captcha_text)

对于复杂验证码或追求稳定性: 推荐使用第三方打码平台,如超级鹰、图鉴等。流程通常是:

  1. 注册平台,获取软件ID和密钥。
  2. 将验证码图片(字节流或Base64编码)通过平台API发送。
  3. 平台返回识别结果。
  4. 将结果输入页面。

优点:识别率高,专人维护模型,支持种类多。缺点:产生费用,有网络依赖。

集成打码平台的核心代码逻辑

import base64 import requests def identify_captcha_with_platform(image_path, platform_config): “””调用第三方打码平台””” with open(image_path, ‘rb’) as f: img_bytes = f.read() # 通常平台要求base64编码 b64_data = base64.b64encode(img_bytes).decode(‘utf-8’) # 构造请求参数 (以某个平台为例,具体看API文档) data = { ‘username’: platform_config[‘username’], ‘password’: platform_config[‘password’], ‘typeid’: ‘1001’, # 验证码类型编号,如1001代表4位数字字母 ‘image’: b64_data } resp = requests.post(‘http://api.example.com/decode’, data=data) result = resp.json() if result[‘success’]: return result[‘data’][‘code’] # 返回识别结果 else: raise Exception(f“打码失败: {result[‘message’]}”)

踩坑记录

  • 识别率问题:本地OCR对扭曲、粘连严重的验证码基本无效。不要花太多时间调优,不如直接考虑打码平台。
  • 成本控制:打码平台通常按次计费。在脚本调试阶段,频繁失败会浪费次数。建议在调试时先使用万能验证码或手动处理,脚本稳定后再切换。
  • 异步加载:验证码图片可能是点击后动态生成的,需要确保在截图前,图片已经加载完成。使用WebDriverWait等待元素可见或具备特定属性。
  • 超时与重试:识别过程可能失败,需要加入重试机制。例如,识别后提交登录,如果页面提示验证码错误,则刷新验证码重新识别,最多重试3次。

2.4 实战方案三:Cookie跳过登录与滑动验证码处理

Cookie跳过登录:这其实不是处理验证码,而是完全规避了登录流程。适用于测试登录后功能的场景。

操作流程

  1. 手动用浏览器正常登录一次系统。
  2. 打开开发者工具(F12),进入Application存储选项卡,找到Cookies,选择当前网站的域名。
  3. 你会看到一个Cookie列表。你需要找到那些用于维持登录态的关键Cookie,通常是sessionid,token,auth等名称。将它们全部复制出来(可以右键Copy All,但注意格式)。
  4. 在你的自动化脚本启动浏览器后,在访问登录页或首页之前,通过driver.add_cookie()方法将这些Cookie添加到WebDriver中。
  5. 添加完成后,刷新页面或跳转到目标页,此时应该已经是登录状态。

Python Selenium 示例

from selenium import webdriver import json driver = webdriver.Chrome() driver.get(“https://target-website.com”) # 先访问域名,以便浏览器设置Cookie的域名上下文 # 加载之前保存的Cookie文件 (手动登录后通过插件或脚本导出的JSON) with open(‘cookies.json’, ‘r’) as f: cookies = json.load(f) for cookie in cookies: # 添加前可能需要删除‘sameSite’等Selenium不支持的属性 if ‘sameSite’ in cookie: if cookie[‘sameSite’] == ‘None’: cookie[‘sameSite’] = ‘Lax’ # 或直接删除该字段 try: driver.add_cookie(cookie) except Exception as e: print(f“添加Cookie {cookie[‘name’]} 时出错: {e}”) # 添加完Cookie后,刷新页面或跳转到需要登录的页面 driver.refresh() # 或者 driver.get(“https://target-website.com/dashboard”) # 此时应已处于登录状态

重要提示:Cookie有生命周期(过期时间expiry)和域名限制(domain)。确保你添加的Cookie对当前驱动访问的域名有效,且未过期。过期时间是一个Unix时间戳。

滑动验证码处理:对于滑动拼图验证码,核心是计算缺口位置,并模拟人的滑动轨迹。

处理步骤

  1. 获取背景图和缺口图:通常可以通过元素定位,获取两张图片的src属性,然后下载或转换为PIL Image对象。
  2. 识别缺口位置:这是最关键的步骤。可以使用图像处理库(如OpenCV)进行模板匹配。将小的缺口图(滑块)在大的背景图上滑动,找到最匹配的位置,其X轴偏移量就是需要滑动的距离。
  3. 模拟滑动:计算出滑动距离后,不能简单地将滑块瞬间移动到终点,因为会被检测为机器操作。需要模拟人的滑动轨迹:先加速,再匀速,最后减速。使用Selenium的ActionChains来拖拽元素。

Python + OpenCV + Selenium 示例

import cv2 import numpy as np from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.by import By import time, requests from io import BytesIO from PIL import Image driver = webdriver.Chrome() driver.get(“login_page_with_slide”) # 1. 获取背景图和滑块图URL bg_img_url = driver.find_element(By.CLASS_NAME, “bg-img”).get_attribute(“src”) slide_img_url = driver.find_element(By.CLASS_NAME, “slide-img”).get_attribute(“src”) # 2. 下载图片并转换为OpenCV格式 bg_resp = requests.get(bg_img_url) slide_resp = requests.get(slide_img_url) bg_image = cv2.imdecode(np.frombuffer(bg_resp.content, np.uint8), cv2.IMREAD_COLOR) slide_image = cv2.imdecode(np.frombuffer(slide_resp.content, np.uint8), cv2.IMREAD_COLOR) # 3. 使用模板匹配找出缺口位置 result = cv2.matchTemplate(bg_image, slide_image, cv2.TM_CCOEFF_NORMED) min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) slide_length = max_loc[0] # 匹配位置的x坐标,即缺口起始位置 # 注意:网页上的图片可能经过缩放,需要计算缩放比例 # 获取网页上背景图元素的真实宽度 bg_element = driver.find_element(By.CLASS_NAME, “bg-img”) bg_width = bg_element.size[‘width’] # 获取原始背景图片的宽度 bg_img_original_width = bg_image.shape[1] scale = bg_width / bg_img_original_width # 计算在网页上需要滑动的实际距离 slide_distance = slide_length * scale # 4. 模拟人的滑动轨迹 slider = driver.find_element(By.CLASS_NAME, “slider-button”) action = ActionChains(driver) action.click_and_hold(slider).perform() # 模拟滑动轨迹:先加速,后减速 total_tracks = [] current = 0 mid = slide_distance * 0.8 t = 0.2 v = 0 while current < slide_distance: if current < mid: a = 2 # 加速度 else: a = -3 # 减速度 s = v * t + 0.5 * a * t * t v = v + a * t current += s total_tracks.append(round(s)) # 执行滑动 for track in total_tracks: action.move_by_offset(track, 0).perform() time.sleep(t/10) # 加入微小间隔,更拟人 action.release().perform() time.sleep(1) # 等待验证结果

注意事项

  • 缩放比例:网页显示的图片尺寸和原始图片尺寸可能不同,必须计算缩放比例,否则滑动距离会错。
  • 轨迹检测:高级的反爬系统会检测滑动轨迹是否符合人类特征。上述的匀加速-匀减速模型是基础,更复杂的系统可能需要更随机的轨迹,甚至加入Y轴的微小抖动。
  • 动态加载:有些验证码的缺口位置每次会随机变化,需要每次重新计算。
  • 失败重试:滑动验证可能因为轨迹或位置计算偏差而失败,需要加入判断(如检查是否出现成功提示)和重试逻辑。

3. Cookie管理:维持会话状态的基石

如果说验证码是门卫,那Cookie就是你的门禁卡。在Web自动化中,Cookie管理不好,就会出现“登录状态丢失”、“页面跳转后数据没了”等问题。理解Cookie的工作原理,并能在Selenium中熟练操作,是编写稳定自动化脚本的基础。

3.1 Cookie的核心概念与在自动化中的重要性

Cookie是服务器发送到用户浏览器并保存在本地的一小块数据。浏览器下次向同一服务器再发起请求时,会自动携带Cookie,服务器据此识别用户身份和状态。在自动化测试中,Cookie管理主要涉及:

  • 登录状态保持:登录成功后,服务器会下发包含会话ID(如JSESSIONID,PHPSESSID)或Token的Cookie。后续所有请求携带这个Cookie,服务器就知道你是谁。
  • 用户偏好记录:如语言设置、主题样式等。
  • 跟踪与统计:一些分析工具会使用Cookie。

为什么在自动化中要专门管理Cookie?

  1. 效率:避免每次测试都从头执行完整的登录流程(尤其是需要验证码时)。
  2. 稳定性:直接使用有效的Cookie可以绕过不稳定的登录环节。
  3. 测试特定场景:测试Cookie过期、失效、篡改等安全相关用例。
  4. 多环境切换:在不同环境(测试、预发布)使用不同的认证Cookie。

3.2 Selenium操作Cookie的完整API详解

Selenium WebDriver提供了完整的Cookie操作API,功能非常直接。

获取所有Cookie

all_cookies = driver.get_cookies() # 返回一个列表,每个元素是一个字典,包含‘name’, ‘value’, ‘domain’, ‘path’, ‘expiry’(Unix时间戳), ‘secure’, ‘httpOnly’等键。 print(all_cookies)

获取指定名称的Cookie

specific_cookie = driver.get_cookie(‘sessionid’) # 返回一个字典,如果不存在则返回None。

添加Cookie

# 添加一个Cookie。注意:添加Cookie前,driver必须已经访问过该Cookie所属的域名。 driver.add_cookie({ ‘name’: ‘my_cookie’, ‘value’: ‘cookie_value’, ‘domain’: ‘.example.com’, # 可选,默认为当前域名 ‘path’: ‘/’, # 可选 ‘secure’: True, # 可选,仅HTTPS ‘httpOnly’: False, # 可选,脚本是否可访问 ‘expiry’: 1740816000 # 可选,过期时间戳 })

关键点driver.add_cookie()必须在当前浏览器会话的域名上下文与Cookie的domain匹配时才能成功。通常的做法是先driver.get(“https://domain.com”)访问一下该域名的主页,然后再添加Cookie。

删除Cookie

# 删除指定Cookie driver.delete_cookie(‘cookie_name’) # 删除所有Cookie driver.delete_all_cookies()

3.3 实战:Cookie的持久化、复用与失效处理

单纯会操作API还不够,要在项目中用好Cookie,需要一套管理策略。

场景一:Cookie持久化与复用(实现免登录)这是最常用的技巧。将手动登录后获取的Cookie保存到文件(如JSON),后续脚本运行时直接加载。

步骤

  1. 手动登录并导出Cookie。可以写一个简单的脚本,在手动登录后执行,将driver.get_cookies()的结果保存为JSON文件。
    # save_cookies.py from selenium import webdriver import json, time driver = webdriver.Chrome() driver.get(“https://your-test-site.com/login”) input(“请手动完成登录,然后按回车键继续...”) # 登录后,Cookie已设置 cookies = driver.get_cookies() with open(‘cookies.json’, ‘w’) as f: json.dump(cookies, f) print(“Cookie已保存至cookies.json”) driver.quit()
  2. 在自动化脚本中加载Cookie。脚本启动后,先访问目标域名,然后加载Cookie文件。
    # test_with_cookies.py from selenium import webdriver import json def create_driver_with_cookies(cookie_file_path): driver = webdriver.Chrome() # 必须先访问域名,哪怕是个空白页,以建立正确的域名上下文 driver.get(“https://your-test-site.com/”) # 删除可能存在的旧Cookie,避免冲突 driver.delete_all_cookies() with open(cookie_file_path, ‘r’) as f: cookies = json.load(f) for cookie in cookies: # 处理可能存在的‘expiry’类型问题(JSON保存后可能是float) if ‘expiry’ in cookie: cookie[‘expiry’] = int(cookie[‘expiry’]) try: driver.add_cookie(cookie) except Exception as e: print(f“跳过无效Cookie {cookie.get(‘name’)}: {e}”) # 再次访问目标页面,此时应携带Cookie driver.get(“https://your-test-site.com/dashboard”) return driver driver = create_driver_with_cookies(‘cookies.json’) # 此时driver应处于登录状态,可以开始后续测试

场景二:Cookie失效的自动检测与重新登录Cookie会过期。脚本不能假设Cookie永远有效,必须有容错机制。

策略

  1. 启动时检查:加载Cookie后,访问一个需要登录的API或页面,检查返回内容或页面元素,判断是否仍处于登录状态。
  2. 操作中检查:在关键操作(如提交表单)前,可以检查页面上是否存在登录相关的元素(如用户头像、退出按钮)。
  3. 失败后重试:如果检测到未登录,则触发重新登录流程(可能需要处理验证码),并更新Cookie文件。

示例代码框架

class AutoTestWithCookie: def __init__(self, cookie_file=‘cookies.json’): self.cookie_file = cookie_file self.driver = webdriver.Chrome() self.is_logged_in = False def ensure_login(self): “””确保处于登录状态””” if self._check_cookie_valid(): self.is_logged_in = True return # Cookie无效,执行登录 self._perform_login() # 登录成功后保存新的Cookie self._save_cookies() self.is_logged_in = True def _check_cookie_valid(self): “””检查当前Cookie是否有效””” try: # 先尝试加载已有Cookie self.driver.get(“https://your-test-site.com/”) if os.path.exists(self.cookie_file): # ... 加载Cookie代码 ... pass # 访问一个需要登录才能看到的页面或API self.driver.get(“https://your-test-site.com/api/user/profile”) # 检查响应或页面元素,判断是否登录 # 例如,页面包含用户信息,或者API返回了用户数据而非401错误 # 这里简化处理,检查某个登录后特有的元素 user_element = self.driver.find_elements(By.ID, “user-avatar”) return len(user_element) > 0 except Exception as e: print(f“检查登录状态时出错: {e}”) return False def _perform_login(self): “””执行完整的登录流程(可能需要处理验证码)””” self.driver.get(“https://your-test-site.com/login”) # … 输入用户名、密码、处理验证码 … # 点击登录按钮 # 等待登录成功跳转 WebDriverWait(self.driver, 10).until( EC.url_contains(“/dashboard”) ) def _save_cookies(self): “””保存当前有效的Cookie到文件””” cookies = self.driver.get_cookies() with open(self.cookie_file, ‘w’) as f: json.dump(cookies, f) # 后续的测试方法,在开始前调用 ensure_login def test_something(self): self.ensure_login() # … 具体的测试步骤 …

场景三:多账号Cookie隔离测试中可能需要切换不同角色的账号。可以为每个账号准备独立的Cookie文件,或者使用不同的浏览器Profile(用户数据目录)。

使用不同Profile

from selenium import webdriver from selenium.webdriver.chrome.options import Options def create_driver_for_user(user_profile_dir): options = Options() options.add_argument(f“--user-data-dir={user_profile_dir}”) # 可以指定Profile目录,这样每个Driver实例有独立的Cookie、缓存等 driver = webdriver.Chrome(options=options) return driver # 为管理员和普通用户创建不同的驱动实例 admin_driver = create_driver_for_user(“/path/to/admin_profile”) user_driver = create_driver_for_user(“/path/to/user_profile”)

3.4 高级话题:处理HttpOnly、Secure Cookie与同源策略

  • HttpOnly Cookie:这种Cookie无法通过JavaScript(包括Selenium的execute_script)读取,主要是为了防范XSS攻击。但Selenium WebDriver是通过浏览器调试协议操作的,可以正常获取和添加HttpOnly Cookiedriver.get_cookies()返回的字典里,httpOnly字段为True的就是这类Cookie。
  • Secure Cookie:仅在HTTPS连接时才会被发送。Selenium在访问HTTPS站点时,可以正常处理Secure Cookie。如果你在本地测试HTTP环境,可能需要让开发暂时关闭这个标志,或者配置测试环境使用HTTPS。
  • 同源策略:Cookie有domainpath属性限制。你不能给driver添加一个属于google.com的Cookie,然后期望在访问baidu.com时携带它。添加Cookie前,必须确保driver当前页面的域名与Cookie的domain匹配或符合其规则(例如,Cookie的domain.example.com,则可以用于a.example.comb.example.com)。

4. 框架集成与最佳实践

将验证码处理和Cookie管理融入到你的自动化测试框架中,能让代码更清晰、更易维护。

4.1 设计可配置的验证码处理模块

一个好的验证码处理模块应该支持多种策略,并且可以通过配置轻松切换。

模块设计思路

  1. 定义策略接口:所有验证码处理方式都实现同一个接口,比如CaptchaSolver,包含一个solve(captcha_element)方法。
  2. 实现具体策略DisableCaptchaSolver(输入万能码)、OcrCaptchaSolverThirdPartyPlatformSolverApiCaptchaSolver(调用后端接口)等。
  3. 工厂模式选择:根据配置文件(如config.ini或环境变量)决定实例化哪个策略。
  4. 上下文注入:将验证码处理器注入到需要它的页面对象(Page Object)或测试步骤中。

简化的代码结构

# captcha_solver.py from abc import ABC, abstractmethod import pytesseract from PIL import Image import requests class CaptchaSolver(ABC): @abstractmethod def solve(self, driver, captcha_element): “””返回识别出的验证码文本””” pass class DisabledCaptchaSolver(CaptchaSolver): def __init__(self, universal_code=“test”): self.universal_code = universal_code def solve(self, driver, captcha_element): return self.universal_code # 直接返回万能码 class OcrCaptchaSolver(CaptchaSolver): def __init__(self, preprocessing_steps=None): self.preprocessing = preprocessing_steps or [] def solve(self, driver, captcha_element): # … 截图、预处理、OCR识别 … return recognized_text class ApiCaptchaSolver(CaptchaSolver): def __init__(self, api_url, auth_params): self.api_url = api_url self.auth = auth_params def solve(self, driver, captcha_element): # 从页面获取key,调用接口 captcha_key = captcha_element.get_attribute(“data-key”) response = requests.get(self.api_url, params={‘key’: captcha_key}, auth=self.auth) return response.text # config.yaml 或 config.ini # captcha: # strategy: “api” # 可选: disabled, ocr, api, third_party # api_url: “http://test-env.internal/api/captcha” # universal_code: “000000” # 在框架初始化时 config = load_config() if config[‘captcha’][‘strategy’] == ‘api’: solver = ApiCaptchaSolver(config[‘captcha’][‘api_url’], …) elif config[‘captcha’][‘strategy’] == ‘disabled’: solver = DisabledCaptchaSolver(config[‘captcha’][‘universal_code’]) # … 其他策略 … # 在登录页面对象中使用 class LoginPage: def __init__(self, driver, captcha_solver): self.driver = driver self.solver = captcha_solver self.captcha_input = (By.ID, “captcha”) self.captcha_img = (By.ID, “captcha-img”) def input_captcha(self): captcha_element = self.driver.find_element(*self.captcha_img) code = self.solver.solve(self.driver, captcha_element) self.driver.find_element(*self.captcha_input).send_keys(code)

4.2 Cookie管理器的封装

同样,可以封装一个CookieManager类,负责Cookie的加载、保存、验证和刷新。

核心功能

  • load(cookie_file): 从文件加载Cookie到当前driver。
  • save(cookie_file): 将当前driver的Cookie保存到文件。
  • is_valid(): 检查当前Cookie是否有效(通过访问一个鉴权端点)。
  • refresh(): 如果无效,执行重新登录流程并更新Cookie文件。

将其与测试框架的setUp(用例前)和tearDown(用例后)钩子结合,实现全局的登录态管理。

4.3 稳定性与异常处理增强

验证码处理的重试机制

def solve_captcha_with_retry(solver, driver, element, max_retries=3): for attempt in range(max_retries): try: code = solver.solve(driver, element) # 输入验证码并提交后,检查是否成功(例如,错误提示是否出现) # 这里假设有一个检查验证码是否正确的方法 if verify_captcha_success(driver): return code else: print(f“第{attempt+1}次识别失败,验证码错误。刷新重试...”) refresh_captcha(driver) # 刷新验证码图片 element = driver.find_element(...) # 重新定位元素 except Exception as e: print(f“第{attempt+1}次识别出现异常: {e}”) if attempt == max_retries - 1: raise refresh_captcha(driver) raise Exception(“验证码识别重试次数用尽”)

Cookie失效的优雅降级: 在测试用例中,如果关键操作因Cookie失效失败,不应让整个用例崩溃,而应记录错误、尝试重新登录并重试操作,或者标记用例为“阻塞状态”。

5. 常见问题排查与调试技巧

在实际操作中,你会遇到各种各样奇怪的问题。这里记录一些典型的坑和排查思路。

5.1 验证码处理常见问题

问题1:OCR识别率始终很低。

  • 排查:首先检查预处理后的图片。把预处理后保存的图片(captcha_processed.png)打开看看,字符是否清晰可辨?干扰线去除了吗?如果人眼都难分辨,机器识别率自然低。
  • 解决
    • 调整预处理参数:二值化的阈值(threshold)、滤波器的尺寸和类型需要针对特定验证码样式调整。
    • 尝试不同OCR引擎和模式:Tesseract有多个OCR引擎模式(--oem)和页面分割模式(--psm)。对于单行验证码,--psm 7(单行文本)通常比默认模式好。也可以尝试其他OCR库,如easyocr
    • 放弃本地OCR:如果验证码设计复杂,果断转向第三方打码平台,节省时间。

问题2:使用打码平台,识别成功了但登录还是失败。

  • 排查
    1. 验证码是否一次性有效?有些验证码在一次识别失败(即使识别正确)或超时后就会失效。确保识别后立即输入并提交。
    2. 网络延迟:从截图、上传到获取结果有时间差,验证码可能已过期。可以尝试在获取验证码图片元素后立即截图并发送,减少延迟。
    3. 输入错误:打印出平台返回的识别结果,确认没有多余空格或换行符。有些平台返回的结果可能包含干扰字符。
  • 解决:在输入前对识别结果进行strip()处理。加入重试逻辑,当登录失败提示“验证码错误”时,刷新验证码重试。

问题3:滑动验证码滑动后,提示“操作太快”或“验证失败”。

  • 排查:轨迹太假。机器生成的匀速或简单加速减速轨迹容易被识别。
  • 解决
    • 引入随机性:在轨迹生成函数中加入小的随机偏移量(random.uniform(-2, 2)),模拟人手的不稳定。
    • 模拟人类犹豫:在滑动开始前和结束后,加入短暂的随机等待(time.sleep(random.uniform(0.1, 0.5)))。
    • 加入回拉:人类操作有时会稍微过头再拉回来。可以在接近终点时,加入一个很小的反向移动。
    • 使用更真实的轨迹算法:搜索“Selenium模拟人类滑动轨迹”,有一些开源算法模拟了更复杂的速度曲线。

5.2 Cookie管理常见问题

问题1:add_cookie()失败,报错“Invalid cookie domain”。

  • 原因:这是最常见的问题。你尝试添加的Cookie的domain属性与浏览器当前页面的域名不匹配。
  • 解决
    1. add_cookie之前,必须先使用driver.get()访问一次该Cookie所属的域名(或父域名)。例如,Cookie的domain.example.com,你需要先访问https://www.example.comhttps://example.com
    2. 检查Cookie字典中的domain字段。从浏览器复制出来的Cookie,其domain可能带有前导点(如.example.com),这表示对该域名及其所有子域名有效。确保你添加的domain值与浏览器中的一致。
    3. 如果是从get_cookies()获取后直接保存再添加,通常没问题。如果是手动构造Cookie字典,要特别注意domain格式。

问题2:添加Cookie后,刷新页面登录状态依然无效。

  • 排查
    1. Cookie是否关键:确认你添加的Cookie包含了维持登录态的关键Cookie(如sessionid,token)。有时会有多个Cookie,缺一不可。
    2. Cookie已过期:检查Cookie字典中的expiry字段(Unix时间戳)。如果已经过期,自然无效。在添加前可以判断一下。
    3. 路径问题path属性限制了Cookie的有效路径。如果你添加的Cookie的path/admin,而你访问的是/user页面,则该Cookie不会被发送。通常登录Cookie的path/
    4. Secure标志:如果Cookie设置了secure=True,但当前页面是HTTP协议,浏览器不会发送它。确保测试环境使用HTTPS,或者让开发在测试环境关闭Secure标志。
    5. 添加时机:确保在添加Cookie后,刷新了页面导航到了新页面,浏览器才会在请求中携带新的Cookie。仅仅添加不会立即生效。
  • 解决:使用浏览器开发者工具的Application->Storage->Cookies面板,对比手动登录后和脚本添加Cookie后,该域名下的Cookie列表是否完全一致。这是最直接的调试方法。

问题3:从get_cookies()获取的Cookie列表里,没有找到关键的session cookie。

  • 原因:可能是HttpOnlyCookie在某种情况下获取不全,或者该Cookie是Secure且当前连接不是HTTPS。更常见的原因是,你获取Cookie的时机不对。必须在登录成功、页面完全加载后获取。
  • 解决:在保存Cookie前,确保已经跳转到了登录后的页面(如仪表盘)。可以加入一个显式等待,等待登录后特有的元素出现,再执行get_cookies()

5.3 通用调试技巧

  • 多用driver.get_cookies()driver.current_url打印信息:在关键步骤前后,打印当前的Cookie和URL,能帮你理清上下文。
  • 善用浏览器开发者工具
    • Network面板:查看登录请求和后续请求的Request Headers,确认Cookie是否正确被携带。这是验证Cookie是否生效的金标准。
    • Application面板:实时查看和修改当前页面的Cookie,可以手动修改值来测试,或者对比脚本添加后的状态。
  • 截图辅助:在验证码识别的前后、滑动验证码滑动前后,使用driver.save_screenshot(‘debug_step1.png’)保存截图,便于离线分析问题。
  • 降低执行速度:在调试时,在关键操作间加入time.sleep(2),让你有足够时间观察浏览器状态。当然,正式脚本中应该用WebDriverWait代替sleep

处理验证码和Cookie是Web UI自动化从“玩具”走向“实用”的关键一步。没有一劳永逸的方案,最好的策略是根据项目实际情况,组合运用多种手段:在测试环境优先寻求开发支持(万能码或接口),在不得已时使用可靠的第三方服务,并将这些能力封装成框架的稳固组件。记住,自动化测试的目的是提升效率,而不是挑战安全机制。在合法合规的前提下,找到那个平衡点,你的自动化之路才会越走越顺。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/6 23:40:21

ntpd vs chrony vs systemd-timesyncd:3 款 Linux 时间同步方案对比与选型指南

ntpd vs chrony vs systemd-timesyncd&#xff1a;3 款 Linux 时间同步方案深度评测与工程实践在分布式系统、金融交易和日志分析等领域&#xff0c;毫秒级的时间偏差都可能导致数据不一致或业务异常。本文将深入解析 Linux 生态中三大主流时间同步方案的技术特性&#xff0c;通…

作者头像 李华
网站建设 2026/7/6 23:38:48

QQ空间 JS SDK 与 DOM 操作:从官方 API 到自动化脚本的 3 种实现路径解析

QQ空间自动化操作技术全景解析&#xff1a;从官方SDK到混合模式实战在社交平台深度融入日常生活的今天&#xff0c;QQ空间作为国内主流社交阵地&#xff0c;其内容管理效率直接影响用户体验。本文将系统剖析三种主流技术实现路径&#xff1a;官方JS SDK标准方案、DOM操作模拟交…

作者头像 李华
网站建设 2026/7/6 23:38:16

基于OWASP Top10的DeepSeek API安全防护实战指南

1. 项目概述&#xff1a;为什么我们需要为DeepSeek构建专属的OWASP防护方案&#xff1f;最近在几个技术社区和项目复盘会上&#xff0c;经常看到有朋友在讨论如何将DeepSeek这类大模型API集成到自己的应用中&#xff0c;从VSCode插件到自动化代码审计工具&#xff0c;热度确实很…

作者头像 李华
网站建设 2026/7/6 23:36:00

ISO 639-1/2/3 语言编码实战:5分钟搞定多语言网站SEO与i18n配置

ISO 639语言编码实战&#xff1a;多语言网站SEO与i18n配置完全指南当你在Chrome浏览器中右键点击"查看页面源代码"&#xff0c;看到<html lang"zh-CN">时&#xff0c;是否思考过这个简单的标签背后隐藏着怎样的国际化逻辑&#xff1f;本文将带你深入…

作者头像 李华
网站建设 2026/7/6 23:34:35

Java毕设项目:基于 SpringBoot 的家政用户预约消费管理系统的设计与实现 基于 SpringBoot+Vue 的社区家政服务综合平台 (源码+文档,讲解、调试运行,定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华