1. 项目概述与核心痛点每次在网站上遇到那种让你在一堆扭曲的文字里点选指定汉字的验证码是不是都感觉血压有点升高尤其是极验3代这种文字点选验证码它把几个汉字随机打散、扭曲、粘连然后让你按顺序点击手动操作不仅耗时还容易点错特别是在需要批量处理数据或者自动化测试的场景下简直是个噩梦。我最近就遇到了一个需要自动登录某数据分析平台的任务这个平台用的就是极验3代文字点选手动操作完全不可行。经过一番折腾我找到了一套基于Python和ddddocr库的解决方案成功实现了验证码的自动识别与点击把人力彻底解放了出来。这个方案的核心就是利用开源的OCR光学字符识别库ddddocr来识别图片中的文字然后通过坐标计算模拟鼠标点击正确的文字位置。听起来简单但实际操作中会遇到各种坑比如图片的预处理、文字定位的精度、以及如何模拟人类点击行为以避免被反爬机制检测到。接下来我就把完整的实现思路、代码细节以及我踩过的那些坑毫无保留地分享给你。无论你是想做自动化测试、数据采集还是单纯想研究一下验证码破解技术这套方法都能给你提供一个扎实的起点。2. 核心工具选型与环境搭建工欲善其事必先利其器。在开始编码之前我们需要准备好核心的工具和运行环境。这里的选择直接决定了后续开发的效率和成功率。2.1 为什么是Python ddddocr首先说Python它几乎是自动化脚本和爬虫领域的首选语言生态丰富库多代码写起来快。像处理图片、模拟点击、发送网络请求这些操作都有非常成熟的库支持比如Pillow、selenium、requests等。然后是重头戏ddddocr。市面上OCR库不少比如Tesseract但它在识别这种经过严重干扰、字体多变的验证码文字时准确率往往不尽如人意。ddddocr是一个基于深度学习的OCR开源项目它最大的特点就是针对各种验证码滑块、点选、文字进行了专门的训练和优化。我实测下来对于极验3代这种点选文字ddddocr的识别准确率远高于传统OCR而且它使用起来非常简单几行代码就能搞定识别不需要你去训练复杂的模型。这就是我选择它的核心原因专精、高效、易用。2.2 环境配置详细步骤环境配置是第一步也是最容易出问题的一步。我会把每一步都拆开讲清楚确保你能一次成功。安装Python确保你的电脑上安装了Python 3.7或更高版本。可以去Python官网下载安装包安装时务必勾选“Add Python to PATH”这个选项这样才可以在命令行里直接使用python命令。安装完成后打开命令行CMD或PowerShell输入python --version如果能正确显示版本号说明安装成功。安装必备库我们将使用pipPython的包管理工具来安装所有需要的库。在命令行中依次执行以下命令pip install ddddocr pip install Pillow pip install selenium pip install opencv-pythonddddocr核心的验证码识别库。PillowPIL一个强大的图像处理库用于图片的加载、裁剪和保存。selenium浏览器自动化工具用于驱动浏览器打开网页并执行点击操作。这是模拟真人操作的关键。opencv-python一个计算机视觉库这里我们主要用它来做一些更高级的图片预处理比如二值化、降噪虽然ddddocr自带预处理但有时手动处理一下效果更好。下载浏览器驱动Selenium需要对应的浏览器驱动才能工作。我们以最常用的Chrome浏览器为例。首先查看你电脑上Chrome的版本。打开Chrome点击右上角三个点 - 帮助 - 关于Google Chrome。然后访问ChromeDriver的官方下载镜像站例如https://chromedriver.storage.googleapis.com/index.html找到与你Chrome版本号完全一致的驱动文件进行下载。下载后你会得到一个chromedriver.exeWindows系统文件。你需要把这个文件的路径添加到系统的环境变量PATH中或者更简单的办法是把它放在你的Python脚本所在的同一个文件夹里。这样Selenium启动时就能自动找到它。注意驱动版本必须与浏览器版本严格匹配否则Selenium会报错。这是新手最容易踩的坑之一。3. 极验3代验证码机制分析与破解思路在动手写代码之前我们必须先搞清楚对手是怎么工作的。只有理解了它的防御机制我们才能找到有效的攻击点。3.1 验证码的呈现与交互逻辑当你触发验证码时页面通常会经历以下几个步骤初始化前端JavaScript加载极验的SDK生成一个包含验证码图片和提示文字的容器。获取挑战向极验服务器请求一次“挑战”challenge服务器会返回一组唯一的参数如gtchallenge以及一张背景图和一张缺口图对于滑块或文字点选图。对于文字点选返回的是一张包含多个干扰文字的图片。图片处理前端会将得到的图片数据进行一些处理比如Base64解码、添加干扰元素等然后渲染到页面上。同时它会提示用户需要点击的文字顺序例如“请依次点击天、地、人”。用户交互用户按照提示依次点击图片中对应的文字。验证提交前端收集用户点击的坐标序列结合本次挑战的gt、challenge等参数经过特定的算法加密后提交给服务器进行验证。服务器校验服务器根据提交的坐标、挑战参数以及它自己知道的正确答案进行比对返回成功或失败。我们的破解目标就是自动化完成第4步识别文字并生成正确的点击坐标序列。3.2 核心破解思路拆解我们的自动化脚本需要模拟上述流程关键在于两步识别和模拟。获取验证码图片这是所有工作的基础。我们需要从网页上把那个包含干扰文字的验证码图片“抠”下来。通常有两种方式截图裁剪使用Selenium对验证码所在的网页元素进行截图。这种方法最通用但精度受屏幕缩放、页面布局影响。解析图片源通过浏览器开发者工具F12的Network网络面板找到验证码图片的请求地址直接通过Python的requests库下载图片。这种方法得到的图片最原始、最干净是首选方案。极验的图片通常是一个独立的图片URL。文字识别OCR将下载或截取的图片送入ddddocr进行识别。这里不是简单识别所有文字因为图片里有很多干扰字。我们的目标是识别出提示要求点击的那几个特定文字。所以我们需要把ddddocr识别出的所有文字结果与我们得到的文字提示如“天、地、人”进行匹配。坐标定位与计算ddddocr在识别文字时可以返回每个识别出的文字在图片中的位置一个矩形框的坐标通常是左上角和右下角的坐标。我们需要从这个矩形框中计算出一个用于点击的“点”。通常我们取这个矩形的中心点坐标。这里有一个关键转换我们得到的坐标是相对于下载下来的那张验证码图片的。而我们需要点击的坐标是相对于整个浏览器视口或者那个验证码图片HTML元素的。因此必须进行坐标系的转换。模拟点击与提交使用Selenium的ActionChains来模拟鼠标移动和点击动作。为了提高成功率避免被识别为机器点击动作需要加入一些人性化设计比如移动轨迹不是直线而是带一点弧度的曲线并且在点击前有短暂的随机停顿。最后触发前端的验证提交逻辑。整个流程的难点和精髓就在于坐标转换和模拟行为的拟人化。下面我们就进入具体的代码实现环节。4. 完整代码实现与逐行解析理论讲完了现在上干货。我会把完整的代码拆分成几个功能模块并逐行解释其作用和注意事项。4.1 模块一驱动初始化与页面访问这个模块负责启动浏览器打开目标页面并等待验证码加载出来。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 init_driver(): 初始化Chrome浏览器驱动 返回: driver对象 # 创建Chrome选项可以添加一些反检测参数可选 options webdriver.ChromeOptions() # 隐藏“Chrome正在受到自动软件控制”的提示 options.add_experimental_option(excludeSwitches, [enable-automation]) options.add_experimental_option(useAutomationExtension, False) # 如果想无头运行不显示浏览器界面取消下面这行的注释。调试阶段建议显示界面。 # options.add_argument(--headless) # 初始化驱动这里假设chromedriver.exe放在脚本同目录下 driver webdriver.Chrome(optionsoptions) # 执行CDP命令规避selenium检测重要 driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: Object.defineProperty(navigator, webdriver, { get: () undefined }) }) driver.maximize_window() # 最大化窗口确保页面元素位置稳定 return driver def access_target_page(driver, url): 访问目标页面并等待验证码区域加载 driver.get(url) # 等待验证码触发按钮或容器出现这里需要根据实际网页修改选择器 # 例如极验的触发按钮可能有一个特定的class try: # 假设验证码触发按钮的ID是‘captcha-btn’ trigger_element WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, captcha-btn)) ) print(页面加载成功验证码触发按钮已找到。) # 点击按钮弹出验证码 trigger_element.click() time.sleep(2) # 等待验证码弹窗加载 except Exception as e: print(f未找到特定触发按钮或页面结构不同: {e}) # 也可能验证码是自动加载的这里直接等待验证码图片出现 pass # 关键等待验证码图片本身加载出来。这里需要你手动查看网页元素找到图片的标签。 # 极验3代的图片通常在一个img标签里可能有‘geetest_item_img’之类的class。 # 我们用更通用的方式等待任何包含‘geetest’字符的图片出现。 try: wait WebDriverWait(driver, 15) # 这里使用XPath查找包含‘geetest’的img标签这是一个示例请根据实际情况调整 captcha_img_element wait.until( EC.presence_of_element_located((By.XPATH, //img[contains(src, geetest) or contains(class, geetest)])) ) print(验证码图片元素已定位。) return captcha_img_element except Exception as e: print(f等待验证码图片超时或失败: {e}) return None代码解析与注意事项init_driver函数中的CDP命令execute_cdp_cmd至关重要。很多网站会检测navigator.webdriver属性来判断是否为自动化脚本这个命令可以将其置为undefined有效规避基础检测。WebDriverWait是Selenium中处理等待的最佳实践它比固定的time.sleep更高效、更可靠。它会在指定时间内持续检查条件是否满足满足则立即继续避免不必要的等待。查找验证码图片元素是整个项目中最需要定制化的一步。你必须亲自打开目标网页按F12进入开发者工具使用元素选择器仔细查看验证码图片对应的HTML标签找到其稳定的特征如id、class、src属性包含的特定字符串。我代码中的XPath只是一个示例你必须替换成你自己分析出的选择器。4.2 模块二验证码图片获取与预处理获取到图片元素后我们需要把它变成ddddocr能处理的图像数据。import requests from PIL import Image import io import base64 import cv2 import numpy as np def get_captcha_image(driver, img_element): 从网页元素中获取验证码图片 参数: driver - 浏览器驱动对象 img_element - 定位到的图片元素对象 返回: PIL Image对象 和 图片在页面中的位置信息 # 方法1通过元素的src属性下载最优图片最干净 img_src img_element.get_attribute(src) if img_src and img_src.startswith(http): print(f通过URL下载图片: {img_src[:50]}...) response requests.get(img_src) image Image.open(io.BytesIO(response.content)) # 方法2如果src是base64数据data:image/png;base64,... elif img_src and img_src.startswith(data:image): print(解析Base64图片数据。) # 剥离头部信息只取base64部分 base64_data img_src.split(,)[1] image_data base64.b64decode(base64_data) image Image.open(io.BytesIO(image_data)) else: # 方法3作为备选方案对元素进行截图 print(通过元素截图获取图片。) location img_element.location size img_element.size # 先对整个页面截图 driver.save_screenshot(full_page.png) full_img Image.open(full_page.png) # 计算裁剪区域 left location[x] top location[y] right left size[width] bottom top size[height] # 根据你的屏幕缩放比例可能需要调整这里假设缩放为100% image full_img.crop((left, top, right, bottom)) # 获取元素在页面中的位置和大小用于后续坐标转换 location img_element.location size img_element.size element_info { x: location[x], y: location[y], width: size[width], height: size[height] } return image, element_info def preprocess_image(pil_image): 对图片进行预处理提高识别率 参数: pil_image - PIL Image对象 返回: 处理后的图像数据通常为numpy数组 # 将PIL图像转换为OpenCV格式 (BGR) open_cv_image np.array(pil_image) # 如果图像是RGBA带透明度转换为RGB if open_cv_image.shape[2] 4: open_cv_image cv2.cvtColor(open_cv_image, cv2.COLOR_RGBA2RGB) # 转换为灰度图很多OCR在灰度图上效果更好 gray cv2.cvtColor(open_cv_image, cv2.COLOR_RGB2GRAY) # 二值化处理将灰度图转为黑白图强化文字边缘 # 自适应阈值二值化能更好地处理光照不均的图片 binary cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2) # 可选降噪。使用中值滤波去除小的噪点。 denoised cv2.medianBlur(binary, 3) # 注意ddddocr内部有强大的预处理能力对于极验3代很多时候原图直接识别效果就很好。 # 过度预处理有时反而会丢失特征。建议先尝试不预处理如果识别率低再启用。 # 这里返回二值化后的图像供测试 return denoised代码解析与注意事项优先使用URL下载通过src属性直接下载图片能得到最原始、未经浏览器渲染干扰的图像识别成功率最高。务必优先尝试这种方法。Base64解码有些网站会将图片以Base64格式直接嵌入HTML也需要能处理。截图作为保底当无法直接获取图片源时对元素截图是最后的手段。但要注意屏幕缩放比例Windows的显示设置如果不是100%坐标会错位以及页面滚动的影响。location获取的是相对于当前视口的位置。预处理是双刃剑ddddocr的模型已经包含了很强的抗干扰能力。我建议先直接用原图进行识别如果发现某个别文字识别不准再尝试启用预处理函数对图像进行二值化、降噪等操作并对比效果。不要盲目预处理。4.3 模块三使用ddddocr进行文字识别与匹配这是整个项目的核心我们将调用ddddocr来识别图片中的文字及其位置。import ddddocr def ocr_and_match(target_words, image): 识别图片文字并与目标词匹配 参数: target_words - 列表需要点击的文字顺序如 [天, 地, 人] image - 图像数据PIL Image或numpy数组 返回: 匹配成功的文字框信息列表每个元素为 (文字, 中心点x, 中心点y) # 初始化ddddocr识别器指定识别模式为‘ocr’文字识别 ocr ddddocr.DdddOcr(show_adFalse) # show_adFalse 关闭广告显示 # 如果image是PIL Image需要转换为bytes if isinstance(image, Image.Image): img_byte_arr io.BytesIO() image.save(img_byte_arr, formatPNG) image_bytes img_byte_arr.getvalue() else: # 如果image是numpy数组预处理后的用cv2编码为bytes _, image_encoded cv2.imencode(.png, image) image_bytes image_encoded.tobytes() # 进行OCR识别detTrue表示返回检测框位置 results ocr.detection(image_bytes) # results 是一个列表每个元素是一个字典包含 # box: 文字框的四个坐标点 [[x1,y1],[x2,y2],[x3,y3],[x4,y4]] # text: 识别出的文字 print(fOCR识别出 {len(results)} 个文字区域。) for res in results: print(f 文字: {res[text]}, 坐标: {res[box]}) matched_positions [] # 遍历我们需要点击的目标文字 for word in target_words: found False # 在识别结果中寻找匹配的文字 for res in results: # 使用精确匹配极验的文字通常是单个汉字 if res[text] word: # 计算文字框的中心点坐标 box res[box] # box是四个点的列表我们取x和y坐标的平均值作为中心 center_x int((box[0][0] box[1][0] box[2][0] box[3][0]) / 4) center_y int((box[0][1] box[1][1] box[2][1] box[3][1]) / 4) matched_positions.append((word, center_x, center_y)) print(f 匹配到目标文字 {word}中心坐标: ({center_x}, {center_y})) found True # 匹配到一个后跳出内层循环继续找下一个目标字 break if not found: print(f [警告] 未在图片中找到目标文字: {word}) # 可以选择跳过或抛出异常这里我们加入一个None占位 matched_positions.append((word, None, None)) return matched_positions代码解析与注意事项ddddocr.DdddOcr(show_adFalse)初始化识别器。show_ad参数建议设为False否则控制台会输出一些无关信息。ocr.detection(image_bytes)这是核心识别方法。它返回一个包含多个字典的列表每个字典对应一个识别出的文字区域包含了文字内容和其外接四边形的四个顶点坐标。这个坐标是相对于你传入的图片的。匹配策略代码中使用的是精确匹配res[text] word。在极少数情况下OCR可能会将“人”识别为“入”造成匹配失败。如果你的目标环境识别错误率较高可以考虑使用模糊匹配比如计算字符串相似度使用difflib.SequenceMatcher但极验点选通常是清晰汉字精确匹配在正确预处理后成功率很高。中心点计算我们取四边形四个顶点的坐标平均值作为点击点。这是一个简单有效的策略。更复杂的策略可以取四边形底边的中心模拟人手点击文字下半部分的习惯。4.4 模块四坐标转换与拟人化点击识别出图片内的坐标后我们需要将其转换为浏览器内的绝对坐标并模拟人类点击。from selenium.webdriver.common.action_chains import ActionChains import random import math def convert_coordinates(matched_positions, element_info): 将图片内的坐标转换为浏览器窗口内的绝对坐标 参数: matched_positions - ocr_and_match返回的列表 element_info - get_captcha_image返回的图片元素位置信息 返回: 转换后的绝对坐标列表 [(word, abs_x, abs_y), ...] absolute_positions [] for word, rel_x, rel_y in matched_positions: if rel_x is None or rel_y is None: absolute_positions.append((word, None, None)) continue # 转换公式图片元素左上角坐标 图片内相对坐标 # 注意需要考虑可能的页面滚动和边框这里假设元素完全在视口内且无边框。 abs_x element_info[x] rel_x abs_y element_info[y] rel_y absolute_positions.append((word, abs_x, abs_y)) print(f 文字 {word} 的浏览器绝对坐标: ({abs_x}, {abs_y})) return absolute_positions def human_like_move_and_click(driver, x, y): 模拟人类移动鼠标并点击 参数: driver - 浏览器驱动 x, y - 目标点击的绝对坐标 actions ActionChains(driver) # 获取当前鼠标位置通常从屏幕左上角开始这里我们从一个随机位置开始移动更真实 start_x random.randint(300, 500) start_y random.randint(300, 500) # 将鼠标移动到随机起始点 actions.move_by_offset(start_x, start_y).perform() time.sleep(random.uniform(0.1, 0.3)) # 计算到目标点的距离和角度 dx x - start_x dy y - start_y distance math.sqrt(dx**2 dy**2) # 将移动路径分成多段模拟非直线移动 num_steps max(5, int(distance / 30)) # 至少5步 for i in range(num_steps): # 使用贝塞尔曲线或简单随机偏移来模拟人手抖动 # 这里用简单的线性插值加小随机偏移 ratio (i 1) / num_steps current_target_x start_x dx * ratio random.randint(-3, 3) current_target_y start_y dy * ratio random.randint(-3, 3) # 移动一小步 actions.move_to_location(current_target_x, current_target_y) actions.perform() # 每步之间加入随机微小停顿 time.sleep(random.uniform(0.02, 0.06)) # 最终移动到精确位置并加入一点最后调整 actions.move_to_location(x random.randint(-1, 1), y random.randint(-1, 1)).perform() time.sleep(random.uniform(0.1, 0.2)) # 点击前停顿仿佛在确认 actions.click().perform() print(f 已在坐标 ({x}, {y}) 执行点击。) time.sleep(random.uniform(0.5, 0.8)) # 点击后停顿准备下一次点击 def perform_clicks(driver, absolute_positions): 按顺序执行所有点击 for word, abs_x, abs_y in absolute_positions: if abs_x is None or abs_y is None: print(f跳过无法定位的文字: {word}) continue print(f正在点击文字: {word}) human_like_move_and_click(driver, abs_x, abs_y)代码解析与注意事项坐标转换convert_coordinates函数是桥梁。element_info[x]和[y]是图片元素左上角相对于浏览器视口的坐标。加上OCR识别出的图片内相对坐标就得到了相对于浏览器视口的绝对坐标。这是SeleniumActionChains移动鼠标所需的坐标。拟人化移动human_like_move_and_click函数是避免被检测的关键。直接让鼠标从A点瞬间跳到B点是明显的机器行为。我们通过随机起始点不从固定位置开始移动。分步移动将总移动距离分成多小步。轨迹随机偏移在每一步的目标点上加入微小随机偏移模拟人手的不稳定性。随机延迟在移动和点击前后加入随机时长的停顿。 这些细节能极大提升模拟的真实性。执行顺序perform_clicks函数按target_words列表的顺序依次点击。顺序必须与网页提示的顺序完全一致否则验证会失败。4.5 模块五主流程整合与执行最后我们把所有模块像拼积木一样组合起来形成一个完整的可执行流程。def main(): # 0. 用户配置 target_url https://你的目标网站登录页 # 替换成实际网址 target_words [文, 字, 点, 选] # 替换成实际验证码提示的文字顺序很重要 # 例如网页提示“请依次点击文、字、点、选”则列表为 [文, 字, 点, 选] # 1. 初始化驱动并访问页面 print(步骤1: 初始化浏览器驱动...) driver init_driver() print(步骤2: 访问目标页面...) img_element access_target_page(driver, target_url) if not img_element: print(错误未能定位到验证码图片元素请检查网页结构或选择器。) driver.quit() return # 2. 获取并预处理图片 print(步骤3: 获取验证码图片...) captcha_image, element_info get_captcha_image(driver, img_element) # 可以保存图片看看 captcha_image.save(current_captcha.png) print(步骤4: (可选)预处理图片...) # processed_image preprocess_image(captcha_image) # 根据需要开启 # 3. OCR识别与匹配 print(步骤5: 进行OCR文字识别与匹配...) # 使用原图识别 matched_positions ocr_and_match(target_words, captcha_image) # 如果识别效果不好可以尝试使用预处理后的图片 # matched_positions ocr_and_match(target_words, processed_image) # 检查是否所有目标文字都匹配到了 if any(pos[1] is None for pos in matched_positions): print(错误未能识别出所有目标文字请检查图片质量或尝试预处理。) # 可以在这里加入重试逻辑比如刷新验证码 driver.quit() return # 4. 坐标转换 print(步骤6: 转换坐标...) absolute_positions convert_coordinates(matched_positions, element_info) # 5. 执行模拟点击 print(步骤7: 开始模拟点击...) perform_clicks(driver, absolute_positions) print(所有点击操作已完成。请观察页面验证是否通过。) # 这里可以加入一个等待检查验证成功的元素出现 # time.sleep(3) # success_element driver.find_element(By.CLASS_NAME, geetest_success) # if success_element: # print(验证成功) # 保持浏览器打开方便观察 # input(按回车键关闭浏览器...) # driver.quit() if __name__ __main__: main()5. 实战避坑指南与常见问题排查代码跑起来了但很可能不会一帆风顺。下面是我在实战中总结出来的“血泪教训”和排查思路能帮你节省大量时间。5.1 识别率低或匹配失败这是最常见的问题。表现为ocr_and_match函数返回的matched_positions里包含None。可能原因1图片获取不完整或质量差。排查检查保存的current_captcha.png。图片是否清晰是否包含了所有需要点击的文字是否被其他页面元素遮挡解决优化get_captcha_image函数。优先使用URL下载法。如果必须截图确保浏览器窗口已最大化并且通过JavaScript将页面滚动到验证码元素的位置再截图。可能原因2目标文字选择器错误或网页动态加载。排查target_words列表里的文字顺序和内容是否与网页上提示的完全一致注意全角/半角符号。有些网站提示文字是动态生成的需要从网页上实时提取。解决写一个函数从网页上抓取提示文字。例如通过Selenium找到提示语的元素如div classgeetest_tip请依次点击span天/spanspan地/spanspan人/span/div然后提取出span标签内的文字。可能原因3ddddocr识别特定字体效果不佳。排查观察ddddocr识别出的所有文字结果代码中已打印。看看它把目标字识别成了什么是识别错了还是根本没识别出来解决启用预处理在main函数中使用processed_image进行识别。二值化有时能极大提升对扭曲、粘连文字的识别率。调整ddddocr参数ddddocr.DdddOcr初始化时可以传入det和rec相关的参数但通常默认值已是最优。可以查阅其官方文档看是否有高级参数。图像增强在预处理环节尝试其他操作如调整对比度、亮度或使用形态学操作开运算、闭运算去除细小噪点或连接断裂笔划。备用方案如果某个字始终识别不了可以考虑建立一个小型的“字库映射表”。例如发现ddddocr总是把“王”识别成“玉”那么在匹配时可以将“王”也映射到“玉”去匹配。5.2 坐标点击不准验证失败点击后验证不通过提示“验证失败”或直接刷新验证码。可能原因1坐标转换计算错误。排查在convert_coordinates函数中打印出element_info和计算出的absolute_positions。手动用浏览器开发者工具的“检查”功能将鼠标悬浮在验证码图片上查看其位置和大小与你代码计算的值进行对比。解决检查get_captcha_image中获取location和size的时机。确保在图片完全加载且稳定显示后才获取。有时需要加入time.sleep或更智能的等待。另外考虑浏览器导航栏、工具栏、页面滚动条的偏移。一个更稳健的方法是使用JavaScript直接获取元素相对于视口的精确位置driver.execute_script(return arguments[0].getBoundingClientRect();, img_element)。可能原因2拟人化移动被检测。排查虽然我们加入了拟人化移动但某些高级反爬策略可能通过更细微的行为如加速度曲线、点击事件的属性来检测。可以尝试注释掉human_like_move_and_click中的复杂移动直接用actions.move_to_element(img_element).click().perform()测试坐标是否基本正确。解决进一步强化模拟行为。可以引入更复杂的移动轨迹算法如贝塞尔曲线并随机化点击事件的button、clientX/Y等属性这需要更底层的JavaScript注入。此外确保Selenium的CDP命令已执行以隐藏webdriver属性。可能原因3点击顺序或时机不对。排查极验验证有时要求点击必须在特定时间间隔内完成过快或过慢都会失败。解决调整human_like_move_and_click和perform_clicks中的time.sleep参数使其更符合人类操作速度。可以在每次点击之间加入更长一点的随机延迟如0.8-1.5秒。5.3 其他通用问题与优化建议问题脚本运行一段时间后失效。分析可能是目标网站更新了验证码的前端代码或接口你的选择器或解析逻辑失效了。建议将关键的选择器如验证码图片、提示文字作为配置项提取到文件或变量开头方便修改。代码中增加更多的try...except和日志输出便于快速定位失败环节。问题需要处理大量验证码如何提高效率建议并发处理使用多线程或异步IO如asyncioaiohttp来并行处理多个验证码任务。注意管理好浏览器实例或使用无头模式。缓存与复用如果验证码的gt和challenge在一定时间内可以复用可以缓存识别结果。失败重试在main函数中加入重试逻辑。当识别或点击失败时自动触发“刷新验证码”按钮获取新验证码重新尝试设置最大重试次数。优化代码健壮性。为每个可能失败的步骤网络请求、元素查找、OCR识别添加异常捕获和日志记录。将配置信息URL、目标词、选择器、等待超时时间集中管理。考虑将核心功能如图片获取、OCR识别封装成类使代码结构更清晰。这套方案的核心思路是通用的不仅限于极验3代。任何基于文字点选的验证码只要你能获取到图片和提示文字都可以用类似的流程进行自动化。关键在于灵活调整图片获取方式和坐标定位逻辑。希望这份超详细的指南能帮你彻底告别手动点验证码的烦恼。在实际使用中多观察、多调试、多记录日志你很快就能让它稳定运行起来。