Python Selenium自动化测试中四种验证码破解方案实战解析
1. 项目概述:当UI自动化遇上验证码
做UI自动化测试或者数据采集的朋友,肯定都绕不开一个“老朋友”——验证码。无论是登录、注册还是关键操作,这个小小的图片或者滑块,往往就是自动化脚本的“终结者”。我最近在做一个电商后台的自动化巡检项目,就频繁被各种验证码卡住,从简单的数字字母,到复杂的滑块、点选,再到行为验证,真是让人头疼。所以,我花了些时间,系统地研究和实测了在Python Selenium框架下,解决验证码问题的几种主流思路。今天就把这四种亲测有效(且免费)的方法,结合我的踩坑经验,详细拆解给大家。无论你是做自动化测试还是爬虫,这篇文章应该都能给你提供一套清晰的“破局”思路。
这四种方法并不是孤立的,它们代表了四种不同的解决层级:从“绕开”(人工介入)到“合作”(第三方平台),再到“硬刚”(本地识别)和“终极规避”(环境与协议)。我会按照从易到难、从临时到稳定的顺序来讲解,并重点分析每种方法的适用场景、核心原理以及你肯定会遇到的实操细节。我们的目标是:让你的Selenium脚本在面对验证码时,不再是“一碰就死”,而是能根据实际情况,选择最合适的策略继续跑下去。
2. 方法一:半自动化策略——人工介入与Cookie复用
这是最直接、最省事,也往往是项目初期或低频任务中最实用的方法。它的核心思想是:在验证码出现的关键节点,让脚本暂停,等待人工识别并输入,然后脚本再接管后续流程。或者,更聪明一点,直接复用已经通过验证的登录状态(Cookie),避免再次触发验证。
2.1 人工手动输入验证码
这个方法听起来很“低级”,但在很多内部系统、测试环境或者验证码出现频率不高的场景下,性价比极高。你不需要研究任何识别算法,只需要让脚本“等一等”你。
核心实现步骤:
-
定位与截图 :当脚本运行到出现验证码的页面时,使用Selenium定位到验证码图片元素,然后将其截图保存到本地。
from selenium import webdriver import time driver = webdriver.Chrome() driver.get("your_login_page_url") # 假设验证码图片的id是 ‘captcha_image’ captcha_element = driver.find_element(By.ID, 'captcha_image') captcha_element.screenshot('captcha.png') # 截图保存这里有个关键点:有些网站的验证码是CSS Sprite(雪碧图)或者动态背景,直接对
img元素截图可能不完整。更稳妥的做法是截取整个浏览器窗口的图,然后根据验证码元素的坐标进行裁剪。不过对于大多数情况,直接对元素截图够用了。 -
脚本等待与人工输入 :截图后,脚本暂停运行。我们在代码里弹出一个输入框,或者直接在控制台提示,让人工去查看刚保存的
captcha.png图片,识别出验证码。# 方式1:使用内置input函数在控制台等待输入 captcha_code = input("请查看当前目录下的captcha.png图片,并输入验证码: ") # 方式2:使用更友好的图形化输入(如tkinter),适合做成小工具 # import tkinter as tk # from tkinter import simpledialog # root = tk.Tk() # root.withdraw() # 隐藏主窗口 # captcha_code = simpledialog.askstring("验证码", "请输入验证码:") -
回填与继续 :获取到人工输入的验证码后,脚本将其填入网页对应的输入框,并继续执行后续操作(如点击登录按钮)。
driver.find_element(By.ID, 'captcha_input').send_keys(captcha_code) driver.find_element(By.ID, 'login_button').click()
实操心得与避坑指南:
注意 :这种方法的最大缺点是“非全自动”。但它有几个意想不到的优点:首先,100%准确,不用担心识别率问题;其次,对于复杂的、变化频繁的验证码(如点选成语、旋转图片),这是成本最低的解决方案;最后,它完全免费,不依赖任何外部API。
- 超时处理 :一定要为
input()或图形化输入设置一个超时机制。否则如果人工离开,脚本会永远卡住。可以用threading模块开一个计时线程,超时后自动填入一个错误码或终止脚本。 - 路径问题 :确保截图保存的路径是脚本可写且人工方便查看的。可以考虑用绝对路径,或者弹窗时直接显示图片。
- 适用场景 :非常适合 开发调试阶段 、 内部管理系统自动化 、 每日仅需执行数次 的定时任务。我最初在调试登录流程时,就全靠这个方法,快速打通了流程,后续再考虑优化。
2.2 Cookie复用大法
如果说手动输入是“临时通行证”,那Cookie复用就是“永久VIP卡”。一旦用户成功登录一次,浏览器就会获得一个包含会话信息的Cookie。我们只要把这个Cookie保存下来,并在新的Selenium会话中加载,就可以直接跳过登录(包括验证码)环节,访问需要登录态的页面。
核心实现步骤:
-
首次登录并保存Cookie :专门写一个脚本,用上述手动或任何方式完成一次成功的登录。登录成功后,获取并保存所有的Cookie到文件(如JSON格式)。
# 登录成功后的操作 import json import pickle # pickle也可以,但JSON更通用安全 cookies = driver.get_cookies() with open('cookies.json', 'w') as f: json.dump(cookies, f) driver.quit() -
新会话加载Cookie :在新的自动化脚本开始时,先访问目标网站的任意页面(通常是首页),然后加载之前保存的Cookie。
driver.get("https://www.target-site.com") # 先访问域名下的页面 time.sleep(2) # 稍等,确保页面加载 with open('cookies.json', 'r') as f: cookies = json.load(f) for cookie in cookies: # 添加前可能需要删除‘expiry’字段,因为它可能是浮点数 if 'expiry' in cookie: # 有时expiry字段会导致添加失败,可以尝试删除或转换 cookie.pop('expiry') try: driver.add_cookie(cookie) except Exception as e: print(f"添加cookie失败: {cookie.get('name')}, 错误: {e}") # 刷新页面或跳转到需要登录的页面,此时应该已是登录状态 driver.refresh()
实操心得与避坑指南:
- Cookie的有效性 :Cookie是有生命周期的(
expiry)。会话Cookie在浏览器关闭后失效,持久化Cookie可以存活较长时间。保存前检查一下Cookie的expiry时间。如果过期了,就需要重新登录获取。 - 域名与路径匹配 :
driver.add_cookie(cookie)要求你必须在当前浏览器会话的域名和路径与Cookie的domain、path属性匹配或为其父级时才能添加成功。这就是为什么我们要先driver.get到目标网站根域下的一个页面。 - 安全Cookie :如果Cookie被标记为
secure(仅HTTPS)或httpOnly(禁止JS访问),Selenium仍然可以添加,但你需要确保当前使用的是HTTPS协议。 - 适用场景 :这是做 数据采集 和 自动化测试 的利器。对于需要长期登录态的任务(如每日定时爬取个人中心数据、自动化巡检已登录状态的功能),只需定期(如每天或每周)更新一次Cookie即可,完美规避验证码。我在做电商价格监控时,就用这个方式维持了长达一个月的会话,无需再次登录。
3. 方法二:借力打力——集成第三方验证码识别平台
当人工介入不现实,Cookie又容易过期时,我们就需要真正的“自动识别”了。自己从头研发识别算法门槛太高,这时候,第三方验证码识别平台就是最佳选择。它们提供了成熟的API,你只需要把验证码图片发过去,就能收到识别结果。
3.1 平台选择与工作原理
国内比较知名的平台有“超级鹰”、“图鉴”、“联众”等,国外有2Captcha、Anti-Captcha。它们通常按识别次数收费,但有丰富的免费试用额度,对于个人和小规模项目完全够用。
工作原理很简单:
- 你的脚本截取验证码图片。
- 将图片(通常是Base64编码)和平台分配的
软件ID(softid)、密钥(key)一起,通过HTTP POST请求发送到平台API。 - 平台的后台可能有真人打码员,也可能是训练好的AI模型,对图片进行识别。
- 平台将识别结果(一串字符)返回给你的脚本。
- 你的脚本将结果填入网页。
以某个平台为例的代码框架:
import requests
import base64
from selenium.webdriver.common.by import By
def recognize_captcha(image_path, username, password, soft_id, codetype):
"""
调用第三方平台识别验证码
:param image_path: 验证码图片路径
:param username: 平台用户名
:param password: 平台密码
:param soft_id: 软件ID(在平台注册后获取)
:param codetype: 验证码类型代码(如1004代表4位英文数字)
:return: 识别结果字符串
"""
with open(image_path, 'rb') as f:
img_data = f.read()
base64_data = base64.b64encode(img_data).decode('utf-8')
data = {
'user': username,
'pass': password,
'softid': soft_id,
'codetype': codetype,
'file_base64': base64_data
}
headers = {'Connection': 'Close'}
try:
resp = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=data, headers=headers)
result = resp.json()
if result['err_no'] == 0:
return result['pic_str'] # 识别成功
else:
print(f"识别失败,错误码:{result['err_no']}, 错误信息:{result['err_str']}")
return None
except Exception as e:
print(f"请求API失败: {e}")
return None
# 在Selenium脚本中使用
captcha_element = driver.find_element(By.ID, 'captcha_image')
captcha_element.screenshot('temp_captcha.png')
code = recognize_captcha('temp_captcha.png', 'your_username', 'your_password', 'your_softid', 1004)
if code:
driver.find_element(By.ID, 'captcha_input').send_keys(code)
3.2 集成要点与成本考量
实操心得与避坑指南:
- 验证码类型(codetype) :这是调用API的关键参数。平台会将验证码分类(如4位数字、5位英文数字混合、算术题、滑块等)。选错类型会极大影响识别准确率甚至导致失败。一定要在平台的文档里找到最匹配的类型码。对于不确定的,可以先用平台的“查码”功能手动测试。
- 错误处理与重试 :第三方API不是100%成功,也可能有网络波动。 必须 添加健全的错误处理和重试机制。如果识别失败或返回空,可以尝试重新截图、重新识别,或者降级到人工处理流程。
- 成本与额度 :虽然说是“免费”,但平台通常只提供少量免费额度用于测试。正式使用前,要清楚其计价模式(如1元1000次)。对于高频使用的场景,需要计算成本。我的经验是,对于常规的数字字母验证码,第三方平台的识别成功率(>95%)和性价比,远高于自己折腾一个识别模型。
- 滑块与点选验证码 :对于这类交互式验证码,平台通常提供更复杂的API。它们返回的可能是需要滑动的距离(像素值),或者需要点击的坐标序列。你需要用Selenium的
ActionChains来模拟这些鼠标操作。这部分的代码会复杂一些,但思路相通:获取问题图片->调用API获取答案->执行对应动作。 - 适用场景 :适合 需要全自动化、验证码复杂度中等、且有一定预算(或用量不大) 的项目。是平衡开发成本、识别准确率和自动化程度的最佳选择。我负责的自动化测试平台,对于预发环境的测试用例,就集成了此类服务,保证了用例的完全无人值守执行。
4. 方法三:自力更生——基于本地OCR库识别
如果你不想依赖外部服务,或者处理的验证码非常固定(比如某个内部系统自带的简单验证码),那么使用本地OCR(光学字符识别)库是一个可行的方案。Python生态里最著名的就是 Tesseract 。
4.1 Tesseract-OCR的安装与基础使用
Tesseract是一个由Google维护的开源OCR引擎。在Python中,我们可以通过 pytesseract 这个库来调用它。
部署步骤:
-
安装Tesseract引擎 :这是核心识别程序,需要单独安装。
- Windows :从 GitHub releases 下载安装包,安装时记得勾选“安装中文语言包”如果需要的话。安装后,将安装目录(如
C:\Program Files\Tesseract-OCR)添加到系统PATH环境变量。 - Mac :
brew install tesseract - Linux :
sudo apt install tesseract-ocr(Ubuntu/Debian)
- Windows :从 GitHub releases 下载安装包,安装时记得勾选“安装中文语言包”如果需要的话。安装后,将安装目录(如
-
安装Python包 :
pip install pytesseract pillow。Pillow是图像处理库,通常用于预处理图片。 -
基础识别代码 :
import pytesseract from PIL import Image # 指定tesseract.exe的路径(如果没加PATH,Windows需要) # pytesseract.pytesseract.tesseract_cmd = r‘C:\Program Files\Tesseract-OCR\tesseract.exe’ # 打开并识别图片 image = Image.open('captcha.png') text = pytesseract.image_to_string(image, config='--psm 6 --oem 3') print(f"识别结果: {text}")这里的
config参数很重要:--psm 6:假设图片为单个统一的文本块。对于验证码,常用6(统一区块)或7(单行文本)。--oem 3:使用默认的OCR引擎模式。
4.2 图像预处理:大幅提升识别率的关键
直接对原始的验证码图片使用Tesseract,识别率往往惨不忍睹。验证码为了防机器识别,通常会加入干扰线、噪点、扭曲、颜色变换等。因此, 图像预处理 是本地OCR方案的核心环节,其目的就是去除干扰,让文字更清晰。
常见的预处理手段及Pillow实现:
- 二值化 :将彩色或灰度图转为纯黑白,突出文字。
from PIL import Image image = Image.open('captcha.png').convert('L') # 先转为灰度图 # 简单阈值二值化 threshold = 150 image = image.point(lambda x: 255 if x > threshold else 0) - 降噪 :去除孤立的像素点(椒盐噪声)。
# 可以使用Pillow的滤波器,或者OpenCV(更强大) from PIL import ImageFilter image = image.filter(ImageFilter.MedianFilter(size=3)) # 中值滤波去噪 - 去干扰线 :对于单色细干扰线,可以通过形态学操作(腐蚀膨胀)去除,这通常需要
OpenCV(cv2)。import cv2 import numpy as np img = cv2.imread('captcha.png', 0) # 灰度读取 _, thresh = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY_INV) # 二值化,背景黑字白 kernel = np.ones((2,2), np.uint8) img_erode = cv2.erode(thresh, kernel, iterations=1) # 腐蚀,让字变细,线断开 img_dilate = cv2.dilate(img_erode, kernel, iterations=1) # 膨胀,让字恢复 # 最后再反转回来 result = cv2.bitwise_not(img_dilate) - 字符分割 :对于字符粘连严重的验证码,可以先尝试分割成单个字符再识别,但这步难度很高。
实操心得与避坑指南:
注意 :本地OCR方案是一个“调参”的过程。没有一套参数能通吃所有验证码。你需要针对目标验证码的特点,反复试验预处理流程。
- 识别率天花板 :对于设计精良的商业验证码(如谷歌的reCAPTCHA),本地OCR基本无能为力。它更适用于 自研的、简单的、固定的 验证码,比如一些老旧系统或内部工具。
- 预处理是核心 :90%的工作量都在预处理上。建议先用图片编辑工具(如Photoshop、GIMP)手动调整,找到能让人眼清晰辨认的处理步骤,再用代码实现这个流程。
- 使用
pytesseract.image_to_data:这个函数能返回每个识别字符的置信度、位置等信息。你可以设定一个置信度阈值(如conf > 60),过滤掉低置信度的结果,提高准确率。 - 考虑训练自定义字库 :如果验证码字体非常特殊且固定,可以尝试用
jTessBoxEditor等工具收集样本,训练Tesseract专用的字库。这能极大提升对该特定验证码的识别率,但过程繁琐。 - 适用场景 : 验证码样式简单固定、对识别率要求不是100%、且希望完全离线运行 的场景。例如,公司内部一个老旧的CRM系统,其验证码多年未变,就非常适合用此方法。我曾用“灰度化->二值化->中值滤波”三步预处理,将一个内部系统的验证码识别率从不到20%提升到了85%以上。
5. 方法四:釜底抽薪——规避验证码触发机制
最高级的“解决”问题,是让问题根本不发生。有些验证码的触发是有条件的,我们可以通过技术手段,让自己看起来更像一个“正常用户”,从而避免被系统要求输入验证码。
5.1 理解验证码的触发逻辑
网站通常在以下情况会弹出验证码:
- 高频访问 :短时间内来自同一IP或同一会话的请求过多。
- 非人类行为模式 :鼠标移动轨迹是直线、点击速度恒定且极快、没有页面停留时间等。
- 浏览器指纹异常 :使用无头浏览器(Headless)、缺失常见浏览器特征(如WebGL、字体、插件列表等)。
- Cookie或本地存储状态 :没有携带正常的Cookie,或localStorage/sessionStorage状态异常。
5.2 Selenium的“拟人化”配置策略
我们的目标就是针对以上几点,对Selenium驱动的浏览器进行伪装。
-
禁用自动化控制标志 :这是最基本的一步。Chrome和Edge浏览器会通过
navigator.webdriver属性暴露自己被自动化控制。我们需要在启动时添加参数来隐藏它。from selenium import webdriver from selenium.webdriver.chrome.options import Options options = Options() options.add_argument("--disable-blink-features=AutomationControlled") options.add_experimental_option("excludeSwitches", ["enable-automation"]) options.add_experimental_option('useAutomationExtension', False) driver = webdriver.Chrome(options=options) # 此外,还可以执行CDP命令来覆盖navigator.webdriver属性(更彻底) driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', { 'source': ''' Object.defineProperty(navigator, 'webdriver', { get: () => undefined }); ''' }) -
模拟真人行为 :使用
ActionChains来模拟人类的鼠标移动、点击、滚动等行为,加入随机延迟和曲线轨迹。from selenium.webdriver.common.action_chains import ActionChains import random import time element = driver.find_element(By.ID, 'some_button') # 不直接click,而是移动过去再点 actions = ActionChains(driver) actions.move_to_element(element).pause(random.uniform(0.2, 0.5)).click().perform() # 模拟滚动 driver.execute_script("window.scrollBy(0, 500);") time.sleep(random.uniform(1, 2)) -
完善浏览器指纹 :无头模式(
--headless)很容易被检测。如果非要用,需要添加更多参数来模拟完整浏览器。更好的做法是使用非无头模式,但将其窗口最小化或移动到屏幕外。options.add_argument("--window-size=1920,1080") # 添加用户代理 options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...") # 禁用自动化扩展,但启用其他常见扩展的模拟(复杂,需谨慎) -
使用高质量代理IP池 :这是解决因IP高频访问而触发验证码的根本方法。通过轮换不同的IP地址,将请求分散开来。你可以在Selenium中为每个WebDriver实例配置不同的代理。
proxy = "123.45.67.89:8080" options.add_argument(f'--proxy-server=http://{proxy}')注意,免费代理大多不稳定,商业代理IP池是一笔成本。
-
妥善管理Cookie和本地存储 :像方法二提到的那样,尽量复用登录态。同时,可以尝试在启动浏览器时,加载一个包含正常历史记录、Cookie的“用户数据目录”(User Data Dir),让你的浏览器看起来像一个长期使用的真实浏览器。
options.add_argument(r"user-data-dir=C:\Users\YourName\AppData\Local\Google\Chrome\User Data\TestProfile")
实操心得与避坑指南:
- 道高一尺,魔高一丈 :反爬与反反爬是持续对抗。上述技巧可能今天有效,明天就失效。尤其是大型网站(如各大电商、社交平台),他们的验证码和风控系统非常复杂,单靠Selenium伪装很难完全规避。
- 成本与复杂度 :维护代理IP池、管理多个浏览器指纹配置文件,会显著增加系统的复杂度和运维成本。
- 检测与反检测 :有些网站会集成像
distil、PerimeterX这样的专业反爬服务,它们能检测非常细微的浏览器环境差异。对抗这些服务,通常需要更底层的工具,如Playwright或Puppeteer配合更高级的指纹伪装,甚至考虑使用浏览器自动化框架的“隐身”模式。 - 适用场景 :适用于 风控不那么严格的中小型网站 ,或者作为其他方法的 辅助手段 。例如,在调用第三方识别平台前,先通过行为模拟和IP轮换,尝试降低触发验证码的频率,从而节省识别费用。在我的爬虫项目中,通常会将方法四作为基础配置,再结合方法二(第三方平台)作为后备方案,形成一个混合策略。
6. 方案选型与混合策略实战
面对一个具体的项目,我们该如何选择?没有银弹,只有最适合的权衡。下面我提供一个决策思路和混合策略的实战案例。
6.1 四象限决策法
我们可以根据两个维度来决策: 验证码的复杂程度 和 自动化任务的执行频率 。
| 执行频率 / 复杂程度 | 简单(数字、字母) | 复杂(滑块、点选、行为) |
|---|---|---|
| 低频(< 10次/天) | 方法一(人工/Cookie) :成本最低,实现最快。 | 方法一(人工) :对于复杂码,人工识别准确率最高,成本可控。 |
| 高频(>= 10次/天) | 方法三(本地OCR)或 方法二(第三方) :如果样式固定,优先本地OCR;否则用第三方平台,计算成本。 | 方法二(第三方平台) :这是最务实的选择。自己开发识别模型投入产出比太低。 |
风控强度 是第三个隐藏维度:对于风控极强的网站(如大型电商、社交平台),上述所有方法都可能失效,可能需要结合 方法四(深度伪装) 甚至探索更底层的接口请求方式(这已超出纯UI自动化范畴)。
6.2 混合策略实战:一个自动登录脚本的例子
假设我们要自动化登录一个论坛,它有时会出现4位数字字母混合验证码,且同一IP短时多次登录会触发。
我们的策略可以是:
- 首选 :尝试使用之前保存的Cookie直接登录(方法二复用)。
- 备选 :如果Cookie失效,则进入正常登录流程。在遇到验证码时: a. 尝试本地OCR识别(方法三) :因为该论坛验证码样式固定,我们已调好预处理参数。 b. 本地识别失败则调用第三方平台(方法二) :作为降级方案。 c. 第三方平台也失败则暂停并报警,转为人工处理(方法一) :作为最终保障。
- 全程辅助 :在脚本中启用随机延迟、模拟人类行为(方法四),并考虑在部署时使用代理IP轮换,以降低触发验证码的概率。
代码结构示意:
import json
import time
from selenium import webdriver
from recognize_local import local_ocr # 假设封装好的本地识别函数
from recognize_third_party import third_party_ocr # 假设封装好的第三方识别函数
def login_with_cookie(driver):
"""尝试Cookie登录"""
try:
driver.get("https://bbs.example.com")
with open('forum_cookies.json', 'r') as f:
cookies = json.load(f)
for cookie in cookies:
driver.add_cookie(cookie)
driver.refresh()
# 检查是否登录成功,例如查找用户头像元素
if driver.find_elements(By.CLASS_NAME, "user-avatar"):
print("Cookie登录成功!")
return True
except:
pass
return False
def handle_captcha(driver):
"""处理验证码,混合策略"""
# 1. 截图
captcha_element = driver.find_element(By.ID, 'captcha_img')
captcha_path = 'temp_cap.png'
captcha_element.screenshot(captcha_path)
# 2. 优先本地识别
code = local_ocr(captcha_path)
if code and len(code) == 4: # 假设验证码是4位
print(f"本地识别结果: {code}")
return code
# 3. 本地失败,降级到第三方平台
print("本地识别失败,尝试第三方平台...")
code = third_party_ocr(captcha_path, codetype=1004) # 1004代表4位英文数字
if code:
print(f"第三方平台识别结果: {code}")
return code
# 4. 全部失败,抛出异常或转为人工
raise Exception("验证码识别失败,请手动处理。")
# 主流程
driver = webdriver.Chrome(options=get_stealth_options()) # 获取伪装后的options
if not login_with_cookie(driver):
print("Cookie失效,开始常规登录流程...")
driver.get("https://bbs.example.com/login")
# ... 填写用户名密码
username_input = driver.find_element(By.ID, 'username')
password_input = driver.find_element(By.ID, 'password')
username_input.send_keys("your_username")
# 模拟人类输入间隔
time.sleep(random.uniform(0.3, 0.7))
password_input.send_keys("your_password")
# 处理验证码
try:
captcha_code = handle_captcha(driver)
driver.find_element(By.ID, 'captcha_input').send_keys(captcha_code)
time.sleep(random.uniform(0.5, 1.5)) # 随机等待
driver.find_element(By.ID, 'login_btn').click()
except Exception as e:
print(e)
# 这里可以触发邮件/钉钉报警,或者暂停脚本等待人工介入
input("验证码识别失败,请手动登录后按回车继续...")
这种混合策略,既保证了高成功率,又兼顾了成本和自动化程度,是工业级项目中常用的设计模式。
7. 常见问题与排查技巧实录
在实际操作中,你会遇到各种各样稀奇古怪的问题。这里我记录了几个最典型的问题和我的解决思路。
7.1 验证码图片无法截图或截图空白
- 问题现象 :
element.screenshot()保存的图片是空白、纯色或者不完整的。 - 可能原因与排查 :
- 元素未完全加载/不可见 :截图前确保元素已在视口中并且可见。可以尝试滚动到元素位置,或等待更长时间。
from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC element = WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.ID, "captcha_image")) ) driver.execute_script("arguments[0].scrollIntoView(true);", element) time.sleep(0.5) # 等待滚动稳定 - Canvas或SVG验证码 :现代验证码可能使用
<canvas>或<svg>绘制,直接对元素截图无效。此时需要截取整个页面或Canvas所在的父容器,然后根据其坐标进行裁剪。更复杂的情况可能需要通过执行JavaScript来获取Canvas的图像数据。 - 动态背景/拼图验证码 :这类验证码通常由多张图片叠加而成。你需要识别出前景(验证码文字或滑块)所在的图层进行截图。可能需要分析网页CSS和DOM结构,找到正确的元素。
- 元素未完全加载/不可见 :截图前确保元素已在视口中并且可见。可以尝试滚动到元素位置,或等待更长时间。
7.2 第三方平台识别准确率突然下降
- 问题现象 :之前一直好用的平台API,突然返回的识别结果错误百出。
- 排查步骤 :
- 检查图片质量 :首先确认你上传的图片是否清晰、完整。可能是网站更新了验证码样式,导致你的截图逻辑出了问题。
- 核对验证码类型(codetype) :验证码类型是否选对?去平台后台查看最近的识别记录,看错误码是什么。有时平台会新增类型。
- 查看平台状态与公告 :平台可能在维护,或者你的账户余额不足、调用频率超限。
- 样本测试 :手动在平台的“测试识别”功能里上传几张当前的问题图片,看结果如何。如果平台手动测试都识别不准,说明该验证码已升级,平台模型需要时间更新。此时你可能需要暂时切换为人工处理,或者寻找其他平台。
7.3 本地OCR(Tesseract)识别结果乱码或为空
- 问题现象 :
pytesseract返回一堆乱码符号,或者空字符串。 - 排查与解决 :
- 语言包 :确保安装了正确的语言包。对于纯英文数字验证码,
eng包就够了。如果需要中文,要安装chi_sim等。在代码中指定语言:pytesseract.image_to_string(img, lang='eng')。 - 图片模式 :Tesseract对输入图片的格式有要求。确保传递给它的
PIL.Image对象是RGB或L(灰度)模式,而不是RGBA(带透明度)。可以用image.convert('RGB')转换。 - 预处理不足 :这是最常见的原因。尝试以下步骤:
- 将图片显示出来,看看人眼是否容易识别。
- 增加二值化的对比度。
- 尝试不同的滤波器和形态学操作去除噪点。
- 如果字符粘连,尝试调整
--psm参数,或者进行字符分割。
- 使用
image_to_data调试 :输出识别数据的详细信息,看看置信度如何,字符框的位置是否正确。这能帮你判断是预处理没做好,还是Tesseract根本找不到文字区域。
- 语言包 :确保安装了正确的语言包。对于纯英文数字验证码,
7.4 行为模拟后仍然被检测到
- 问题现象 :已经添加了各种反检测参数,并模拟了鼠标移动,但网站仍然弹出验证码或直接封锁。
- 升级对抗思路 :
- 检查WebDriver属性 :在浏览器控制台输入
navigator.webdriver,看是否返回undefined。如果返回true,说明你的CDP命令或启动参数未生效。 - 使用更隐蔽的驱动 :考虑使用
undetected-chromedriver这样的库,它专门为绕过检测做了更多工作。 - 指纹深度伪装 :检查其他浏览器指纹,如
plugins,languages,hardwareConcurrency等。可以使用selenium-stealth这类库,或者手动通过CDP命令覆盖更多属性。 - 放弃纯UI自动化 :对于顶尖的反爬系统(如Cloudflare 5秒盾、Distil Networks),纯Selenium可能力不从心。此时需要考虑:
- 逆向工程 :分析网站登录或获取数据的真实API接口,直接发送HTTP请求。这需要抓包和分析JS代码。
- 使用Playwright/Puppeteer :它们提供比Selenium更底层的控制,能更好地模拟真实浏览器环境。
- 终极方案 :在可控环境下,运行带有完整图形界面的浏览器,通过VNC远程控制,但这牺牲了性能和可扩展性。
- 检查WebDriver属性 :在浏览器控制台输入
验证码对抗是一个持续的过程,没有一劳永逸的方案。最关键的思路是: 明确你的需求和成本边界,选择最适合当前场景的技术组合,并准备好当一种方法失效时的备用方案(Fallback) 。保持代码的灵活性和可维护性,比追求一个“万能”的破解方法更重要。
更多推荐
所有评论(0)