Python+Selenium+OCR实战:Web安全自动化测试中验证码处理全攻略

发布时间:2026/6/25 20:42:56
Python+Selenium+OCR实战:Web安全自动化测试中验证码处理全攻略 1. 项目概述当Web安全测试遇上验证码做Web安全测试或者自动化爬虫的朋友肯定都绕不开一个“老朋友”——验证码。无论是简单的数字字母、扭曲的图形还是现在越来越流行的滑块、点选、旋转拼图验证码的存在本质上就是一道横亘在自动化脚本和服务器之间的“智能门禁”。它的核心目的就是区分操作者是人还是机器。对于我们这些希望通过自动化手段进行安全扫描、漏洞探测、批量测试或者数据采集的人来说验证码就像一块必须搬开的绊脚石。传统的安全测试遇到验证码往往就“卡壳”了。手动输入效率太低无法实现自动化流程。直接绕过对于现代复杂的验证码机制几乎不可能。这时候一套能够智能处理验证码的自动化方案就成了提升测试效率和深度的关键。这个项目就是聚焦于如何利用Python和Selenium这对黄金组合来攻克Web安全自动化测试中的验证码难题。它不仅仅是写几行代码去“识别”图片而是一套从环境搭建、策略选择、代码实现到异常处理的完整实战指南旨在让你构建的自动化测试脚本在面对验证码时也能“畅通无阻”。2. 核心思路与方案选型为什么是Python Selenium 第三方OCR面对验证码处理市面上有很多方案比如专用的打码平台API、深度学习自建模型、或者基于浏览器插件的方案。我们选择Python Selenium 第三方OCR库如ddddocr、pytesseract作为核心技术栈是基于以下几个核心考量2.1 选择Selenium而非Playwright或Puppeteer的理由Selenium作为老牌的Web自动化工具其优势在于生态极其成熟、社区庞大、资料丰富。对于Web安全测试而言我们经常需要模拟各种浏览器行为、操作Cookie、执行JavaScript甚至需要与Burp Suite等代理工具联动。Selenium在这些方面的支持度和灵活性经过长期验证相关“坑”和解决方案都能轻易找到。虽然Playwright在性能和现代化API上有优势但Selenium在安全测试这个特定领域其稳定性和可控性依然是首选。特别是处理一些老旧系统或需要特定浏览器驱动版本时Selenium的兼容性更好。2.2 验证码处理的核心策略识别而非绕过我们的目标是“处理”验证码而不是“破解”或“绕过”验证码机制。这有本质区别。绕过可能涉及寻找逻辑漏洞这属于安全研究的范畴。而处理是在遵守规则的前提下让机器完成原本需要人眼和人脑的工作。因此我们的技术路径很清晰定位与捕获使用Selenium定位到验证码图片元素并将其截图保存到本地。预处理对截图进行图像处理如灰度化、二值化、降噪、裁剪等提升识别率。识别调用OCR光学字符识别库将处理后的图像转换为文本字符串。填入与提交使用Selenium将识别出的字符串填入验证码输入框并触发表单提交。2.3 第三方OCR库的选型平衡速度、精度与易用性对于常见的字符型验证码数字、字母pytesseractTesseract的Python封装是一个经典选择但它的识别率对复杂扭曲的验证码往往不尽如人意。近年来基于深度学习的ddddocr库异军突起它对滑块验证码、点选验证码和常见字符验证码的识别效果非常好且使用极其简单成为了很多项目的首选。对于更复杂的验证码如复杂的旋转拼图、行为验证则可能需要考虑接入付费的打码平台API如超级鹰、图鉴等它们提供了高精度的云端识别服务。本指南将以免费的ddddocr和pytesseract作为主要工具进行讲解。注意使用自动化工具处理验证码应仅用于授权的安全测试、学习研究或个人合法自动化需求。未经授权对他人网站进行高频验证码识别尝试可能被视为恶意攻击请务必遵守法律法规和网站的使用条款。3. 环境搭建与核心工具详解工欲善其事必先利其器。一个稳定、可复现的自动化测试环境是成功的第一步。3.1 Python环境与依赖库安装首先确保你安装了Python建议3.7及以上版本。然后通过pip安装核心库pip install selenium # 浏览器自动化核心 pip install ddddocr # 高精度验证码识别推荐 pip install pillow # 图像处理库用于截图和处理 pip install pytesseract # 传统OCR识别可选 pip install opencv-python # 高级图像处理可选用于复杂预处理3.2 浏览器驱动管理Selenium需要对应的浏览器驱动如Chrome的chromedriver才能控制浏览器。这里强烈推荐使用webdriver-manager库它可以自动下载、匹配和管理驱动版本省去手动下载和配置环境变量的麻烦。pip install webdriver-manager在代码中你可以这样使用它来启动Chromefrom selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager # 自动下载并配置最新版chromedriver service Service(ChromeDriverManager().install()) driver webdriver.Chrome(serviceservice) driver.get(https://www.example.com)3.3 验证码识别库初探ddddocr vs. pytesseractddddocr安装简单针对中文验证码和滑块验证码优化极好。基本使用只需两行代码import ddddocr ocr ddddocr.DdddOcr() with open(captcha.png, rb) as f: img_bytes f.read() result ocr.classification(img_bytes) # 识别结果 print(result)对于滑块验证码它还能计算滑块缺口位置功能强大。pytesseract需要先安装Tesseract-OCR引擎本体一个独立的软件。在Windows上你需要从GitHub下载安装程序安装后记得将Tesseract的安装路径如C:\Program Files\Tesseract-OCR添加到系统环境变量PATH中或者在代码中指定路径。它的使用需要对图像进行更多的预处理才能获得较好效果。import pytesseract from PIL import Image # 如果未添加环境变量需指定tesseract路径 # pytesseract.pytesseract.tesseract_cmd r‘C:\Program Files\Tesseract-OCR\tesseract.exe’ image Image.open(captcha.png) # 通常需要先对image进行灰度、二值化等处理 text pytesseract.image_to_string(image, config--psm 7 --oem 3) print(text)实操心得对于新手或追求快速上手的项目优先选择ddddocr。它的识别成功率在大多数场景下远高于未经调优的pytesseract。pytesseract更适合作为备选或者当你需要对识别过程进行极其精细的控制和调优时使用。4. 验证码处理全流程实战拆解下面我们以一个典型的“用户名密码图形验证码”登录场景为例拆解完整的自动化处理流程。4.1 步骤一页面导航与元素定位首先使用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 driver.get(‘https://target-site.com/login’) # 使用显式等待确保元素加载完成 wait WebDriverWait(driver, 10) # 定位验证码图片元素假设其id为‘captcha_img’ captcha_img_element wait.until(EC.presence_of_element_located((By.ID, ‘captcha_img’))) # 定位验证码输入框 captcha_input driver.find_element(By.ID, ‘captcha_code’)4.2 步骤二验证码图片捕获与保存Selenium可以获取元素的位置和大小然后对整个浏览器窗口进行截图再根据元素坐标裁剪出验证码图片。更简单的方法是直接获取图片的src属性如果是网络图片并下载或者使用element.screenshot()方法最推荐最准确。from PIL import Image import io # 方法1直接对元素截图最精准推荐 captcha_png captcha_img_element.screenshot_as_png # 将二进制数据转换为PIL Image对象 image Image.open(io.BytesIO(captcha_png)) image.save(‘current_captcha.png’) # 方法2若截图失败或元素特殊可尝试截全屏后裁剪备用方案 # driver.save_screenshot(‘full_page.png’) # location captcha_img_element.location # size captcha_img_element.size # left location[‘x’] # top location[‘y’] # right left size[‘width’] # bottom top size[‘height’] # full_img Image.open(‘full_page.png’) # captcha_img full_img.crop((left, top, right, bottom)) # captcha_img.save(‘current_captcha.png’)4.3 步骤三图像预处理可选但关键原始的验证码截图可能包含干扰线、噪点、背景色直接识别效果差。预处理的目的就是净化图像让文字或特征更突出。def preprocess_image(image_path): from PIL import Image, ImageFilter, ImageEnhance img Image.open(image_path).convert(‘L’) # 转换为灰度图 # 二值化设置一个阈值将灰度图转为黑白 threshold 150 # 阈值需要根据具体验证码调整 img img.point(lambda x: 255 if x threshold else 0) # 降噪使用中值滤波或腐蚀膨胀简单情况可能不需要 # img img.filter(ImageFilter.MedianFilter(size3)) # 增强对比度 enhancer ImageEnhance.Contrast(img) img enhancer.enhance(2.0) # 增强2倍对比度 img.save(‘processed_captcha.png’) return img4.4 步骤四调用OCR库进行识别将预处理后的图像交给OCR库识别。这里以ddddocr为例。import ddddocr def recognize_captcha(image_path): ocr ddddocr.DdddOcr() with open(image_path, ‘rb’) as f: img_bytes f.read() captcha_text ocr.classification(img_bytes) # ddddocr识别出的结果可能包含空格或换行根据实际情况清理 captcha_text captcha_text.strip().replace(‘ ‘, ‘’).replace(‘\n’, ‘’) return captcha_text captcha_code recognize_captcha(‘processed_captcha.png’) print(f“识别出的验证码为{captcha_code}”)4.5 步骤五填写验证码并完成后续操作将识别结果填入输入框并完成登录或其他操作。captcha_input.clear() captcha_input.send_keys(captcha_code) # 填写用户名密码 username_input driver.find_element(By.ID, ‘username’) password_input driver.find_element(By.ID, ‘password’) username_input.send_keys(‘your_username’) password_input.send_keys(‘your_password’) # 点击登录按钮 login_button driver.find_element(By.ID, ‘login_btn’) login_button.click()4.6 步骤六处理识别失败与重试机制验证码识别不可能100%成功必须设计重试机制。识别结果校验有些验证码是固定长度如4位数字。识别后可以检查长度不符合则视为失败。失败反馈判断提交后通过检测页面是否出现“验证码错误”的提示元素来判断。刷新验证码找到验证码图片旁的“刷新”按钮并点击或者直接重新请求页面。循环重试将上述步骤封装在一个循环中设定最大重试次数。max_retries 3 for attempt in range(max_retries): # 步骤1-5捕获、识别、填写、提交 # ... # 提交后等待页面跳转或出现错误提示 try: # 假设验证码错误时会出现一个class为‘error-msg’的元素 error_msg WebDriverWait(driver, 3).until( EC.presence_of_element_located((By.CLASS_NAME, ‘error-msg’)) ) if “验证码” in error_msg.text: print(f“第{attempt1}次尝试验证码识别错误。”) # 点击刷新验证码 refresh_btn driver.find_element(By.ID, ‘refresh_captcha’) refresh_btn.click() time.sleep(1) # 等待新验证码加载 continue # 进入下一次循环重试 except: # 如果没有找到错误信息很可能登录成功了 print(“登录成功或进入下一页面”) break else: print(f“经过{max_retries}次尝试仍然失败。”)5. 进阶挑战滑块与点选验证码的处理对于更复杂的验证码如滑块和点选思路从“识别文字”变为“识别图形特征并模拟操作”。5.1 滑块验证码处理思路核心是计算滑块缺口的位置。ddddocr提供了直接计算缺口位置的功能。import ddddocr import cv2 import numpy as np def get_slider_offset(bg_path, slider_path): bg_path: 背景大图路径 slider_path: 滑块小图路径 return: 缺口位置的x坐标偏移量 slide ddddocr.DdddOcr(detFalse, ocrFalse) # 只启用目标检测功能 with open(bg_path, ‘rb’) as f: target_bytes f.read() with open(slider_path, ‘rb’) as f: background_bytes f.read() res slide.slide_comparison(target_bytes, background_bytes) return res[‘target’][0] # 返回缺口左上角的x坐标 # 获取背景图和滑块图 bg_element driver.find_element(By.CLASS_NAME, ‘bg-img’) slider_element driver.find_element(By.CLASS_NAME, ‘slider-img’) bg_element.screenshot(‘bg.png’) slider_element.screenshot(‘slider.png’) offset_x get_slider_offset(‘bg.png’, ‘slider.png’) print(f“需要滑动的水平距离为{offset_x}像素”)得到距离后使用Selenium的ActionChains来模拟拖动。这里有个关键点需要模拟人的拖动轨迹先加速后减速而不是瞬间移动否则容易被反爬机制检测。from selenium.webdriver.common.action_chains import ActionChains import random slider_btn driver.find_element(By.CLASS_NAME, ‘slider-btn’) # 滑动按钮 action ActionChains(driver) action.click_and_hold(slider_btn).perform() # 模拟人类滑动轨迹 track generate_track(offset_x) # 这是一个生成轨迹列表的函数例如[5,10,15,...] for x in track: action.move_by_offset(xoffsetx, yoffset0).perform() time.sleep(random.uniform(0.001, 0.005)) # 随机间隔更像真人 action.release().perform()5.2 点选验证码处理思路点选验证码如“请点击图中所有的XXX”需要识别图中目标物体并计算其中心点坐标进行点击。截图截取包含验证码的整个区域。目标识别可以使用ddddocr的目标检测功能或者更专业的YOLO等模型离线或在线API。ddddocr的detection模式可以检测出图中文字或物体的坐标框。坐标转换识别出的坐标是相对于截图图像的坐标需要转换为相对于浏览器窗口的坐标才能用Selenium正确点击。模拟点击使用ActionChains移动到转换后的坐标并点击。# 假设使用ddddocr检测图中“自行车”的位置 det ddddocr.DdddOcr(detTrue) with open(‘click_captcha.png’, ‘rb’) as f: img_bytes f.read() results det.detection(img_bytes) # 返回一个包含多个[x, y, w, h]的列表 # 假设验证码区域在页面中的位置是 (area_x, area_y) area_element driver.find_element(By.ID, ‘captcha-area’) location area_element.location size area_element.size area_x, area_y location[‘x’], location[‘y’] for box in results: # box: [x1, y1, x2, y2] center_x area_x (box[0] box[2]) / 2 center_y area_y (box[1] box[3]) / 2 # 使用ActionChains移动并点击 action ActionChains(driver) action.move_to_element_with_offset(area_element, center_x - area_x, center_y - area_y).click().perform() time.sleep(0.5) # 点击间隔6. 集成到Web安全自动化测试框架处理验证码的功能不应是孤立的它需要集成到你的自动化测试流程中例如在登录模块、表单提交等环节自动调用。6.1 设计一个可复用的验证码处理器类将上述功能封装成一个类便于管理和调用。class CaptchaHandler: def __init__(self, driver, ocr_type‘ddddocr’): self.driver driver self.ocr_type ocr_type if ocr_type ‘ddddocr’: import ddddocr self.ocr ddddocr.DdddOcr() # 可以扩展其他OCR初始化 def get_element_screenshot(self, element, save_path): “”“对指定元素截图并保存”“” png_data element.screenshot_as_png with open(save_path, ‘wb’) as f: f.write(png_data) return save_path def recognize_text_captcha(self, img_path): “”“识别文本验证码”“” with open(img_path, ‘rb’) as f: img_bytes f.read() result self.ocr.classification(img_bytes) return result.strip() def handle_login_with_captcha(self, url, username, password, img_locator, input_locator, username_locator, password_locator, submit_locator, max_retry3): “”“处理带验证码的登录流程”“” self.driver.get(url) for i in range(max_retry): try: # 定位元素 captcha_img WebDriverWait(self.driver,5).until( EC.presence_of_element_located(img_locator) ) # 截图识别 img_path f‘captcha_{i}.png’ self.get_element_screenshot(captcha_img, img_path) code self.recognize_text_captcha(img_path) # 填写表单 self.driver.find_element(*username_locator).send_keys(username) self.driver.find_element(*password_locator).send_keys(password) self.driver.find_element(*input_locator).send_keys(code) self.driver.find_element(*submit_locator).click() # 验证是否成功这里需要根据实际成功后的页面特征来判断 time.sleep(2) if “dashboard” in self.driver.current_url: # 示例判断 return True else: print(f“登录尝试 {i1} 失败可能验证码错误尝试刷新。”) # 寻找刷新按钮或重新加载验证码 # ... except Exception as e: print(f“尝试 {i1} 出现异常{e}”) return False6.2 在安全测试脚本中调用在你的SQL注入、XSS等漏洞扫描脚本中如果需要先登录就可以无缝集成这个处理器。from my_security_framework import Scanner from captcha_handler import CaptchaHandler # 初始化扫描器和验证码处理器 scanner Scanner() handler CaptchaHandler(scanner.driver) # 目标登录页面 login_url ‘http://test-site.com/login’ # 定义所有定位器 (By.ID, ‘id_value’) locators { ‘img’: (By.ID, ‘captchaImg’), ‘input’: (By.ID, ‘captchaInput’), ‘username’: (By.NAME, ‘user’), ‘password’: (By.NAME, ‘pass’), ‘submit’: (By.XPATH, ‘//button[type“submit”]’) } # 执行登录 if handler.handle_login_with_captcha(login_url, ‘admin’, ‘password’, **locators): print(“登录成功开始安全扫描...”) # 此时driver已处于登录状态可以开始爬取链接、测试表单等 scanner.crawl_and_test() else: print(“登录失败无法进行后续测试。”)7. 常见问题、反爬策略与优化技巧在实际操作中你会遇到各种各样的问题。下面是一些典型问题及解决方案。7.1 验证码识别率低怎么办问题根源图像质量差、干扰严重、字体特殊。解决方案优化预处理尝试不同的二值化阈值、使用不同的滤波器如高斯模糊去噪后再锐化、进行形态学操作腐蚀、膨胀去除孤立点或连接断裂笔画。尝试多种OCR引擎ddddocr不行就换pytesseract并调整Tesseract的PSM页面分割模式和OEMOCR引擎模式参数。例如对于单行文本--psm 7通常效果较好。人工打码兜底在自动化流程中设置一个阈值当连续失败N次后将图片弹出人工输入验证码并将正确的图片-文本对保存下来作为后续训练或校验的样本。接入付费打码平台对于商业项目或高难度验证码这是最稳定高效的方案。将图片发送到平台API获取识别结果。7.2 被网站检测到自动化行为怎么办问题根源Selenium驱动的浏览器有一些特征可以被JavaScript检测到如navigator.webdriver属性为true。解决方案使用undetected-chromedriver这是一个专门为绕过检测而修改的ChromeDriver能有效隐藏自动化特征。pip install undetected-chromedriverimport undetected_chromedriver as uc driver uc.Chrome()添加Stealth插件或执行CDP命令通过Chrome DevTools Protocol命令来覆盖或隐藏WebDriver特征。driver.execute_cdp_cmd(‘Page.addScriptToEvaluateOnNewDocument’, { ‘source’: ‘ Object.defineProperty(navigator, ‘webdriver’, { get: () undefined }); ‘ })模拟人类行为在操作中加入随机延迟、随机的鼠标移动轨迹如前文滑块验证码所示避免过于规律的操作频率。7.3 验证码图片是动态加载或Canvas绘制的怎么办问题根源验证码不是简单的img src...而是通过JavaScript动态生成或使用Canvas绘制无法直接通过src下载或元素截图。解决方案Canvas截图对于Canvas可以通过执行JavaScript来获取其数据URL。canvas_element driver.find_element(By.TAG_NAME, ‘canvas’) # 获取canvas的base64数据 canvas_data driver.execute_script(“”” var canvas arguments[0]; return canvas.toDataURL(‘image/png’).substring(22); // 去掉data:image/png;base64,前缀 “””, canvas_element) # 将base64解码为图片 import base64 img_data base64.b64decode(canvas_data) with open(‘canvas_captcha.png’, ‘wb’) as f: f.write(img_data)监听网络请求如果图片是通过XHR/Fetch请求加载的可以使用Selenium的performance日志或配合像browsermob-proxy这样的代理工具拦截并保存该图片请求的响应内容。7.4 如何管理大量的验证码样本用于训练或测试建立一个本地的验证码样本库非常有用。你可以将成功识别和失败识别的图片分别存放在success/和fail/目录下并附上一个label.txt文件记录正确的文本。这有助于你分析识别失败原因集中查看哪些类型的验证码容易出错。训练自定义模型如果你未来想使用深度学习框架如CNN训练自己的验证码识别模型这些数据就是宝贵的训练集。测试OCR引擎效果批量运行不同OCR引擎或参数在这些样本上统计识别准确率科学地选择最佳方案。实操心得验证码对抗是一个持续的过程。网站会升级验证码你的识别策略也需要随之调整。保持代码的模块化和可扩展性至关重要。将验证码处理逻辑独立出来当需要更换识别方式或应对新验证码类型时你只需要修改或替换对应的模块而不必重写整个自动化流程。记住没有一劳永逸的方案灵活性和可维护性才是长期战斗的关键。