基于OpenCV与Selenium的滑块验证码自动化破解实战指南

发布时间:2026/8/29 10:09:28
基于OpenCV与Selenium的滑块验证码自动化破解实战指南 1. 从“看得见”到“点得准”滑块验证码的自动化破局思路做爬虫的朋友对滑块验证码这个“老朋友”肯定又爱又恨。爱的是它比复杂的点选、文字识别验证码看起来要简单直观得多恨的是当你想用代码模拟人类“一拖而过”的动作时会发现处处是坑。坐标计算不准、轨迹模拟不像、被风控系统识别为机器操作……这些问题让不少自动化脚本折戟沉沙。传统的破解思路比如直接计算缺口位置像素差在背景图经过高斯模糊、随机噪点干扰后准确率会大幅下降。而一些基于深度学习的方案虽然效果好但模型训练、部署的成本对很多轻量级爬虫任务来说又显得过于沉重。有没有一种折中的、稳定且易于实现的方案这就是我们今天要讨论的核心结合 OpenCV 进行图像识别定位再通过 Selenium 精细化模拟人工操作。这套方案的价值在于它不依赖于任何第三方收费API完全基于开源技术栈将图像处理和浏览器自动化这两个领域的工具结合起来形成了一个闭环解决方案。你不仅能用它来应对滑块验证码其图像识别和轨迹模拟的思路也能迁移到其他类似的交互式验证场景中。接下来我将拆解整个流程从环境搭建、核心原理到每一步的代码实现与避坑指南手把手带你构建一个健壮的滑块验证码自动化通过工具。2. 战场准备环境搭建与核心库的“正确”安装姿势工欲善其事必先利其器。我们的技术栈非常明确Python 作为主语言Selenium 用于驱动浏览器模拟操作OpenCV 用于图像分析与缺口定位。听起来很简单但安装环节就有几个关键点直接关系到后续代码能否顺利运行。2.1 Python 与 Selenium 环境配置首先确保你有一个 Python 3.7 及以上版本的环境。我强烈建议使用虚拟环境如venv或conda来管理项目依赖避免包冲突。# 创建并激活虚拟环境以 venv 为例 python -m venv slider_env source slider_env/bin/activate # Linux/Mac # 或 slider_env\Scripts\activate # Windows接下来安装 Selenium。直接用 pip 安装最新版即可pip install selenium但 Selenium 本身只是一个控制浏览器的“遥控器”它需要一个“浏览器驱动”才能实际工作。这里以最常用的 Chrome 浏览器为例。查看 Chrome 版本打开 Chrome 浏览器在地址栏输入chrome://version/查看第一行的“Google Chrome”版本号例如 120.0.6099.110。下载对应驱动访问 ChromeDriver 的官方下载站点或国内镜像站。关键点在于驱动版本必须与你的 Chrome 主版本号完全一致。比如 Chrome 是 120.x.x.x那么你就必须下载版本号为 120.x.x.x 的 ChromeDriver。大版本不匹配一定会报错。放置驱动下载的chromedriverWindows 是chromedriver.exe可执行文件需要放在系统 PATH 环境变量包含的目录下如/usr/local/bin或C:\Windows或者更简单的做法是在代码中指定它的绝对路径。一个常见的坑是浏览器会自动更新但驱动忘记更新导致某天脚本突然无法运行。一种稳健的做法是在代码开始时通过webdriver的Service对象来指定驱动路径便于管理。2.2 OpenCV-Python 的安装与“隐形”依赖安装 OpenCV 对于 Python 通常是指安装opencv-python这个包。然而这里有一个新手极易踩中的大坑。如果你直接pip install opencv-python在编写代码并导入import cv2时很可能会遇到一个令人困惑的错误ImportError: libGL.so.1: cannot open shared object file: No such file or directoryLinux或类似的动态链接库缺失问题。这是因为opencv-python是一个“头”包它依赖于系统底层的一些图形库如 GTK、Qt、libGL等而这些库在纯净的 Python 环境或某些服务器系统如 Docker 基础镜像中并不预装。解决方案是安装opencv-python-headless。这个版本移除了对 GUI 功能如cv2.imshow()的依赖只保留核心的图像处理算法非常适合服务器或无头环境下的自动化任务并且避免了上述依赖问题。pip install opencv-python-headless如果你确实需要在本地开发时显示图片进行调试可以同时安装标准版但用于生产或服务器部署时headless版本是更安全、更轻量的选择。我们接下来的识别算法完全基于headless版本的功能无需担心功能缺失。2.3 辅助工具库我们可能还需要numpyOpenCV 处理后的图像本质是 numpy 数组和PillowPIL用于可能的图像格式转换或预处理它们通常会被opencv-python自动安装但为了明确也可以一并安装pip install numpy pillow至此我们的核心战场已经准备就绪。接下来进入最核心的环节如何让计算机“看见”并“找到”那个滑块缺口。3. 核心原理拆解OpenCV 如何“看见”缺口滑块验证码通常由两张图组成一张是完整的、带有缺口阴影的背景图另一张是那个需要拖动的、孤立的小滑块图。我们的目标是在背景图上找到与小滑块图最匹配的位置也就是缺口的位置。这本质上是一个模板匹配问题。3.1 模板匹配算法选择OpenCV 提供了多种模板匹配方法通过cv2.matchTemplate函数实现。它会在背景图大图上滑动滑块图模板计算每个位置的相似度最终得到一个结果矩阵其中数值最极值最大或最小取决于方法的点就是最佳匹配位置。常用的方法有cv2.TM_CCOEFF_NORMED计算归一化相关系数。值越接近1匹配度越高。这是我们最常用的方法它对图像的亮度变化有一定鲁棒性。cv2.TM_SQDIFF_NORMED计算归一化平方差。值越接近0匹配度越高。为什么首选TM_CCOEFF_NORMED因为滑块验证码的背景往往有渐变、阴影缺口边缘的像素值变化不是简单的“有无”而是存在一定的相关性。相关系数方法能更好地捕捉这种结构相似性抗干扰能力比单纯的像素差方法更强。3.2 图像预处理提升匹配成功率的关键直接拿原始图片进行模板匹配成功率可能不高。因为验证码图片常常会加入干扰比如高斯模糊让边缘变得不清晰。随机噪点在图片上撒上彩色或黑白的像素点。背景干扰线一些曲线或直线。为了提高匹配的鲁棒性我们需要对图片进行预处理。核心思路是强化边缘弱化颜色和纹理干扰。最有效的预处理步骤之一是边缘检测。我们可以使用 Canny 边缘检测算法或者更简单的将图像转换为灰度图后应用阈值处理或 Sobel 算子来提取边缘。import cv2 import numpy as np def preprocess_image(image): 预处理图像增强边缘特征 # 1. 转换为灰度图减少颜色维度干扰 gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 2. 应用高斯模糊平滑微小噪点内核大小需根据图片调整通常用3x3或5x5 blurred cv2.GaussianBlur(gray, (3, 3), 0) # 3. 使用Canny边缘检测或Sobel提取边缘 # Canny阈值需要根据实际情况调整 edges cv2.Canny(blurred, threshold150, threshold2150) return edges经验之谈预处理没有“银弹”。不同的验证码风格需要不同的预处理参数。例如有些验证码的缺口边缘非常模糊Canny 的阈值就要调低有些背景干扰线很强可能需要先进行形态学操作如开运算去除细线。在实际项目中你需要截取一批验证码图片手动调整预处理流程和参数观察处理后的边缘图像确保缺口形状能被较好地保留而背景干扰被最大程度抑制。这是一个需要耐心调试的过程。3.3 执行匹配与位置计算预处理后我们对背景大图和滑块小图都进行同样的处理然后进行模板匹配。def find_gap_position(bg_image_path, slider_image_path): 识别缺口位置 # 读取图片 bg_img cv2.imread(bg_image_path) # 背景大图 slider_img cv2.imread(slider_image_path) # 滑块小图 # 预处理 bg_processed preprocess_image(bg_img) slider_processed preprocess_image(slider_img) # 执行模板匹配 # bg_processed 是“大图” slider_processed 是“模板” result cv2.matchTemplate(bg_processed, slider_processed, cv2.TM_CCOEFF_NORMED) # 找到最大值最佳匹配的位置 min_val, max_val, min_loc, max_loc cv2.minMaxLoc(result) top_left max_loc # 因为用的是CCOEFF_NORMED取最大值位置 # top_left 是匹配区域左上角的坐标 (x, y) # 缺口在背景图中的x坐标通常是这个top_left的x值。 # 但注意有些验证码的滑块图是带有透明背景或多余空白边的匹配到的位置可能需要偏移。 gap_x top_left[0] # 一个重要的修正匹配到的是滑块“形状”的位置但我们需要的是滑块“开始拖动”的位置。 # 通常滑块拼图初始位置在背景图最左侧其中心点或左侧边缘与背景图左边缘对齐。 # 我们计算出的 gap_x 是缺口左上角的x坐标。而滑块的移动距离应该是 gap_x 减去滑块初始位置的x坐标。 # 假设滑块初始位置在x0处那么移动距离就是 gap_x。 # 但更严谨的做法是在匹配前先获取滑块元素在网页上的位置计算其初始x坐标。 move_distance gap_x # 这里先简单认为初始x为0 return move_distance这里引出了一个关键细节cv2.matchTemplate匹配得到的位置(x, y)是模板滑块图左上角在背景图中的坐标。然而我们最终需要传递给 Selenium 的拖动距离是滑块需要水平移动的像素值。这个值通常是gap_x减去滑块拼图初始位置在背景图上的x坐标。如果验证码的滑块初始是紧贴背景图左边缘的那么这个初始坐标可能就是0。但有些验证码设计会让滑块初始位置有一个小偏移这就需要我们通过截图或计算来校准。如何获取这个初始偏移一个实用的方法是在浏览器中先获取滑块拼图元素和背景图元素的位置和大小通过 JavaScript 或 Selenium 的location、size属性计算出滑块拼图相对于背景图容器的初始位置。这涉及到下一部分的浏览器自动化操作。4. Selenium 自动化操作模拟“人类”的拖动识别出缺口位置只是成功了一半。如何让 Selenium 精准、平滑地将滑块拖到目标位置并且不被反爬机制判定为机器操作是另一半挑战。4.1 定位元素与获取关键坐标首先我们需要用 Selenium 定位到几个关键元素背景图元素用于截图供 OpenCV 分析。滑块拼图元素就是我们要拖动的那个小块。滑块轨道/容器元素有时用于计算相对位置。from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains import time driver webdriver.Chrome() # 或指定Service路径 driver.get(你的目标网址) # 假设通过检查元素找到背景图和滑块的选择器 # 实际选择器需要根据目标网站调整可能是id, class, xpath等 bg_element driver.find_element(By.CLASS_NAME, geetest_canvas_bg) slider_element driver.find_element(By.CLASS_NAME, geetest_slider_button) # 获取元素的位置和大小 bg_location bg_element.location bg_size bg_element.size slider_location slider_element.location print(f背景图位置: {bg_location}, 大小: {bg_size}) print(f滑块位置: {slider_location})这里有一个巨坑element.location返回的是该元素相对于整个浏览器窗口左上角的坐标。而cv2.imread读取的截图其坐标系原点在图片的左上角。如果我们直接截取整个网页或者背景图元素那么从 OpenCV 计算出的gap_x图片内x坐标需要经过转换才能对应到浏览器坐标系中的绝对x坐标。更可靠的做法是直接对背景图元素进行截图。这样截图本身的像素坐标就与元素内部的坐标一一对应无需复杂的坐标系转换。from PIL import Image import io # 对背景图元素进行截图 bg_screenshot bg_element.screenshot_as_png # 将二进制数据转换为PIL Image对象方便后续处理或保存 bg_image Image.open(io.BytesIO(bg_screenshot)) bg_image.save(background.png) # 同样地对滑块拼图元素截图如果需要 slider_screenshot slider_element.screenshot_as_png slider_image Image.open(io.BytesIO(slider_screenshot)) slider_image.save(slider.png)通过元素截图我们确保了 OpenCV 处理的图片范围与网页上显示的区域完全一致计算出的像素距离就是我们需要在该元素内部移动的距离。4.2 计算精确的拖动距离现在我们结合 OpenCV 的识别结果和 Selenium 获取的元素信息来计算精确的拖动距离。假设通过上一节的find_gap_position函数我们得到了缺口在背景图截图中的x坐标gap_x_pixel单位像素。那么滑块需要移动的距离move_distance就是move_distance gap_x_pixel为什么这么简单因为我们对bg_element进行了局部截图gap_x_pixel已经是相对于这个截图左上角也就是bg_element左上角的x坐标。而滑块拼图slider_element的初始位置通常就在这个背景图容器的左侧边缘x坐标可能为0或一个很小的固定值。因此gap_x_pixel几乎就是滑块需要水平移动的绝对像素距离。但为了应对更复杂的情况比如滑块初始位置有偏移我们可以计算一个偏移量slider_offset slider_location[x] - bg_location[x]。这表示滑块相对于背景图左上角的初始x偏移。 那么最终移动距离为move_distance gap_x_pixel - slider_offset。4.3 设计拟人化拖动轨迹直接让 Selenium 将滑块瞬间移动到目标位置是百分百会被识别为机器的。人类拖动滑块有加速、减速、甚至轻微回弹的过程。我们需要用ActionChains来模拟这个轨迹。核心思路是将总移动距离分解成多段小移动每段移动的时间和位移量符合物理运动规律如先加速后减速。def get_track(distance): 根据总距离生成移动轨迹列表模拟先加速后减速 track [] current 0 mid distance * 4 / 5 # 前4/5段加速后1/5段减速 t 0.2 # 计算间隔 v 0 while current distance: if current mid: a 2 # 加速阶段的加速度 else: a -3 # 减速阶段的加速度 v0 v v v0 a * t move v0 * t 0.5 * a * t * t current move track.append(round(move)) # 确保最终刚好到达目标点处理小数误差 track.append(distance - sum(track)) # 过滤掉移动距离为0的步骤 track [x for x in track if x ! 0] return track # 生成轨迹 track_list get_track(move_distance) print(f移动轨迹: {track_list})这个get_track函数模拟了一个简单的匀变速运动。在实际应用中你可以让轨迹更复杂比如加入随机的小抖动、短暂的停顿使其更接近真人操作。轨迹的精细程度是绕过高级风控的关键。4.4 执行拖动并处理结果使用ActionChains执行拖动操作。注意要先点击并按住滑块然后按照轨迹一步步移动最后释放。# 初始化ActionChains actions ActionChains(driver) # 点击并按住滑块元素 actions.click_and_hold(slider_element).perform() # 按照轨迹移动 for move in track_list: actions.move_by_offset(xoffsetmove, yoffset0).perform() # 每移动一小段加入一个极短的随机延迟更像人类 time.sleep(random.uniform(0.001, 0.005)) # 释放鼠标 actions.release().perform() time.sleep(1) # 等待验证结果执行完毕后页面通常会验证滑块位置。我们需要检测是否成功。成功标志可能是某个元素消失、出现成功提示文字、或者页面发生跳转。你需要根据目标网站的具体情况来编写检测逻辑。# 示例检测成功提示元素是否出现 try: success_element driver.find_element(By.XPATH, //div[contains(text(), 验证成功)]) print(滑块验证通过) except: print(滑块验证可能失败尝试重新识别或记录日志。) # 可以在这里加入重试逻辑比如重新截图、识别、拖动5. 实战中的“坑”与精细化调优策略将上述步骤组合起来一个基本的自动化流程就完成了。但在实际生产环境中你会遇到各种各样的问题。下面分享几个我踩过的坑和对应的解决方案。5.1 验证码图片的动态加载与获取很多网站的验证码图片不是简单的img标签其src可能是动态生成的 Base64 数据或 Blob 链接甚至是通过 Canvas 绘制的。直接用element.get_attribute(src)可能拿不到图片地址。解决方案对于 Canvas 绘制如我们之前所做使用element.screenshot_as_png是最通用、最可靠的方法。它能直接获取元素渲染后的像素数据。对于 Base64 图片可以通过get_attribute(src)获取到以data:image/png;base64,开头的字符串然后解码为图片数据。import base64 src bg_element.get_attribute(src) if src.startswith(data:image): # 去掉头部信息 base64_data src.split(,)[1] image_data base64.b64decode(base64_data) with open(bg.png, wb) as f: f.write(image_data)对于 Blob 链接稍微复杂可能需要通过 JavaScript 在浏览器上下文中将 Blob 转换为 Data URL 再获取。经验优先尝试screenshot_as_png它几乎适用于所有可见元素省去了分析复杂前端代码的麻烦。5.2 识别失败与重试机制OpenCV 的模板匹配不是 100% 准确的。干扰过强、图片缩放、缺口边缘过于模糊都可能导致匹配位置错误。应对策略置信度判断cv2.matchTemplate返回的max_val最大匹配值是一个重要的置信度指标。可以设置一个阈值如 0.7只有当max_val大于该阈值时才认为识别结果可信。否则触发重试如刷新验证码。min_val, max_val, min_loc, max_loc cv2.minMaxLoc(result) if max_val 0.7: print(f匹配置信度过低: {max_val} 可能识别错误尝试刷新验证码。) # 触发刷新验证码的逻辑 return None多尺度匹配有些网站在不同设备或缩放比例下验证码图片的实际显示尺寸可能微调。可以在匹配前将模板图在小范围内进行比例缩放如 0.95, 1.0, 1.05进行多次匹配取置信度最高的一次。重试逻辑将整个识别-拖动流程封装在一个函数中并加入重试计数器。如果验证失败通过检测成功元素判断则重新执行流程最多重试 N 次。5.3 轨迹模拟的进阶对抗简单的匀加速轨迹可能被一些风控系统破解。更高级的模拟需要考虑随机化总移动时间、加速段和减速段的比例、每步之间的延迟都可以在一定范围内随机。人类抖动在移动轨迹中偶尔加入微小的、随机的垂直方向y轴移动模拟手抖。非匀速停顿在移动过程中随机插入几次 50-150 毫秒的短暂停顿。轨迹回放录制真人拖动滑块的鼠标坐标序列然后让 Selenium 复现这个序列。这是最拟真但实现成本也最高的方法。def human_like_move(actions, element, track_x): 更拟人化的移动包含随机停顿和微小抖动 actions.click_and_hold(element).perform() time.sleep(random.uniform(0.1, 0.3)) # 按下后停顿一下 for i, move_x in enumerate(track_x): # 加入微小的垂直抖动范围-2到2像素 move_y random.randint(-2, 2) if random.random() 0.7 else 0 actions.move_by_offset(xoffsetmove_x, yoffsetmove_y).perform() # 随机加入停顿 if random.random() 0.9 and i len(track_x) - 2: time.sleep(random.uniform(0.05, 0.15)) else: time.sleep(random.uniform(0.002, 0.008)) # 基础移动间隔 actions.release().perform()5.4 浏览器指纹与环境伪装即使滑块拖得再像人如果你的浏览器指纹很“干净”或者很“机器”也可能在整体风控评分中暴露。Selenium 驱动的浏览器有一些特征可以被检测到例如navigator.webdriver属性为true。缓解措施使用 undetected-chromedriver这是一个修改过的 Chromedriver可以隐藏部分自动化特征。但它可能与 Selenium 新版本存在兼容性问题需谨慎使用。注入 JavaScript 修改属性在页面加载前通过execute_cdp_cmd执行 Chrome DevTools Protocol 命令来覆盖某些属性。driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: Object.defineProperty(navigator, webdriver, { get: () undefined }); })使用真实的用户代理User-Agent和合理的浏览器窗口尺寸。启用浏览器正常功能如允许通知、设置语言、时区等让浏览器环境看起来更真实。需要注意的是与风控的对抗是持续升级的。没有一劳永逸的方案上述方法能提高通过率但无法保证 100%。对于核心业务需要准备备选方案如打码平台、人工处理队列等。6. 完整代码示例与集成思路将以上所有环节整合形成一个完整的、带有基础错误处理和重试机制的示例脚本。import cv2 import numpy as np from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time import random import io from PIL import Image class SliderCaptchaSolver: def __init__(self, driver): self.driver driver self.wait WebDriverWait(driver, 10) def preprocess_image(self, image): 图像预处理转灰度、模糊、边缘检测 gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) blurred cv2.GaussianBlur(gray, (3, 3), 0) # 尝试调整Canny阈值以适应不同图片 edges cv2.Canny(blurred, 50, 150) return edges def get_element_screenshot(self, element, save_pathNone): 获取WebElement的截图返回cv2图像格式 png_data element.screenshot_as_png pil_image Image.open(io.BytesIO(png_data)) if save_path: pil_image.save(save_path) # 将PIL Image转换为OpenCV格式 (BGR) opencv_image cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR) return opencv_image def calculate_slide_distance(self, bg_image, slider_image): 计算滑块需要移动的距离 # 预处理 bg_processed self.preprocess_image(bg_image) slider_processed self.preprocess_image(slider_image) # 模板匹配 result cv2.matchTemplate(bg_processed, slider_processed, cv2.TM_CCOEFF_NORMED) min_val, max_val, min_loc, max_loc cv2.minMaxLoc(result) print(f模板匹配置信度: {max_val}) if max_val 0.6: # 置信度阈值 raise ValueError(f匹配置信度过低({max_val})无法准确定位缺口。) top_left max_loc # 假设滑块初始位置在背景图最左端移动距离就是缺口左上角x坐标 # 更复杂的场景需要根据滑块实际初始位置调整 slide_distance top_left[0] # 可选由于滑块图可能包含透明边匹配位置可能需要微调 # 例如如果滑块图宽度是w缺口实际位置可能在 top_left[0] w/2 附近 # 这里需要根据实际验证码样式调整 # slide_distance top_left[0] slider_image.shape[1] // 2 return slide_distance def generate_track(self, distance): 生成模拟人类拖动的轨迹 track [] current 0 mid distance * 0.8 t 0.2 v 0 while current distance: if current mid: a 2 random.random() * 1 # 随机加速度 else: a -3 - random.random() * 1 # 随机减速度 v0 v v v0 a * t move v0 * t 0.5 * a * t * t if current move distance: move distance - current current move track.append(round(move)) # 最后可能还差一点补上 if sum(track) distance: track.append(distance - sum(track)) # 加入随机的小停顿和抖动 final_track [] for i, step in enumerate(track): final_track.append(step) if i len(track) - 1 and random.random() 0.8: # 随机插入一个0-2像素的垂直抖动 final_track.append((0, random.randint(-1, 1))) return track def drag_slider(self, slider_element, distance): 执行拖动操作 track self.generate_track(distance) actions ActionChains(self.driver) actions.click_and_hold(slider_element).perform() time.sleep(random.uniform(0.1, 0.3)) for step in track: if isinstance(step, tuple): # 处理抖动步骤 actions.move_by_offset(xoffsetstep[0], yoffsetstep[1]).perform() else: actions.move_by_offset(xoffsetstep, yoffset0).perform() time.sleep(random.uniform(0.001, 0.01)) # 随机延迟 # 模拟释放前的轻微回弹或抖动 actions.move_by_offset(xoffsetrandom.randint(-2, 0), yoffsetrandom.randint(-1, 1)).perform() time.sleep(0.05) actions.release().perform() def solve(self, bg_selector, slider_selector, max_retries3): 主解决函数 for attempt in range(max_retries): try: print(f尝试第 {attempt 1} 次验证...) # 等待元素出现 bg_element self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, bg_selector))) slider_element self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, slider_selector))) time.sleep(0.5) # 等待图片加载稳定 # 截图 bg_image self.get_element_screenshot(bg_element, fbg_attempt_{attempt}.png) slider_image self.get_element_screenshot(slider_element, fslider_attempt_{attempt}.png) # 计算距离 distance self.calculate_slide_distance(bg_image, slider_image) print(f计算出的移动距离: {distance} 像素) # 拖动滑块 self.drag_slider(slider_element, distance) # 等待并检查结果这里需要根据目标网站的成功标识来修改 time.sleep(2) # 示例检查某个成功元素是否出现 # success_indicator self.driver.find_elements(By.XPATH, //div[contains(class, success)]) # if success_indicator: # print(滑块验证成功) # return True # 暂时假设成功实际应添加验证逻辑 print(拖动操作完成请根据页面变化判断是否成功。) return True except Exception as e: print(f第 {attempt 1} 次尝试失败: {e}) # 可以在这里触发刷新验证码按钮 # refresh_button driver.find_element(By.CLASS_NAME, geetest_refresh) # refresh_button.click() time.sleep(1) if attempt max_retries - 1: print(已达到最大重试次数验证失败。) return False # 使用示例 if __name__ __main__: # 初始化浏览器 options webdriver.ChromeOptions() # 可选添加一些选项来隐藏自动化特征 # options.add_argument(--disable-blink-featuresAutomationControlled) driver webdriver.Chrome(optionsoptions) # 访问目标页面 driver.get(https://你的目标登录页或验证码页) # 初始化破解器 solver SliderCaptchaSolver(driver) # 尝试破解验证码需要替换为实际页面的CSS选择器 # 例如某网站的验证码背景图class可能是 .geetest_canvas_bg滑块是 .geetest_slider_button is_success solver.solve(.geetest_canvas_bg, .geetest_slider_button, max_retries3) if is_success: # 验证通过继续后续操作如输入账号密码登录 pass else: # 验证失败采取其他策略 pass # driver.quit()这个类提供了一个基础的框架。在实际项目中你需要根据目标网站的具体 HTML 结构调整选择器根据验证码的视觉样式调整图像预处理参数和轨迹生成算法并根据成功/失败的表现来完善结果检测逻辑。这套基于 OpenCV Selenium 的方案其优势在于可控性强、成本低并且能让你深入理解滑块验证码对抗的本质。它可能无法应对所有极端情况但对于大多数中等难度的滑块验证码经过精心调优后能达到很高的通过率是爬虫工程师工具箱中一件非常实用的武器。