python
import time
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 requests
from PIL import Image
import io
import base64
import re
class GeetestClickVerifier:
"""
极验3点选验证码识别与验证类
支持文字点选、图标点选等验证类型
"""
def __init__(self, driver_path='chromedriver'):
"""
初始化验证器
Args:
driver_path: ChromeDriver路径
"""
# 设置Chrome选项
options = webdriver.ChromeOptions()
options.add_argument('--disable-blink-features=AutomationControlled')
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
# 初始化WebDriver
self.driver = webdriver.Chrome(executable_path=driver_path, options=options)
self.driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
'source': '''
Object.defineProperty(navigator, 'webdriver', {get: () => undefined})
'''
})
self.wait = WebDriverWait(self.driver, 10)
self.actions = ActionChains(self.driver)
def load_page(self, url):
"""
加载目标页面
Args:
url: 目标页面URL
"""
self.driver.get(url)
time.sleep(2)
def detect_geetest_widget(self):
"""
检测页面中的极验验证码组件
Returns:
dict: 包含验证码元素的字典
"""
elements = {}
# 查找极验验证码相关元素
selectors = {
'widget': '.geetest_btn', # 触发验证的按钮
'captcha_popup': '.geetest_popup', # 验证码弹窗
'full_bg': '.geetest_bg', # 背景图
'slice_bg': '.geetest_slice_bg', # 拼图背景
'refresh_btn': '.geetest_refresh', # 刷新按钮
'voice_btn': '.geetest_voice', # 语音验证按钮
}
for key, selector in selectors.items():
try:
elements[key] = self.driver.find_element(By.CSS_SELECTOR, selector)
except:
elements[key] = None
return elements
def trigger_verification(self):
"""
触发验证码显示
"""
try:
# 点击验证按钮
verify_btn = self.wait.until(
EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_btn'))
)
verify_btn.click()
time.sleep(2)
return True
except Exception as e:
print(f"触发验证失败: {e}")
return False
def get_captcha_images(self):
"""
获取验证码图片
Returns:
dict: 包含背景图、目标图等图片的字典
"""
images = {}
try:
# 等待验证码弹窗出现
self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_popup')))
time.sleep(1)
# 获取背景图片
bg_element = self.driver.find_element(By.CLASS_NAME, 'geetest_bg')
bg_style = bg_element.get_attribute('style')
# 从style中提取背景图URL
bg_match = re.search(r'url\("?(.*?)"?\)', bg_style)
if bg_match:
bg_url = bg_match.group(1)
images['background'] = self.download_image(bg_url)
# 获取提示文字
try:
prompt_element = self.driver.find_element(By.CLASS_NAME, 'geetest_prompt')
images['prompt_text'] = prompt_element.text
print(f"验证提示: {images['prompt_text']}")
except:
images['prompt_text'] = "请点击图中文字"
return images
except Exception as e:
print(f"获取图片失败: {e}")
return images
def download_image(self, url):
"""
下载图片
Args:
url: 图片URL
Returns:
numpy.ndarray: OpenCV格式的图片
"""
try:
if url.startswith('data:image'):
# 处理base64编码的图片
header, encoded = url.split(",", 1)
image_data = base64.b64decode(encoded)
image = Image.open(io.BytesIO(image_data))
else:
# 处理普通URL
response = requests.get(url, timeout=10)
image = Image.open(io.BytesIO(response.content))
# 转换为OpenCV格式
return cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
except Exception as e:
print(f"下载图片失败: {e}")
return None
def preprocess_image(self, image):
"""
预处理图片
Args:
image: 输入图片
Returns:
numpy.ndarray: 预处理后的图片
"""
if image is None:
return None
# 转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 二值化
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 去噪
kernel = np.ones((2, 2), np.uint8)
processed = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
return processed
def find_text_positions(self, image, target_text):
"""
在图片中查找目标文字的位置
Args:
image: 背景图片
target_text: 目标文字
Returns:
list: 文字位置列表,每个元素为(x, y, w, h)
"""
positions = []
if image is None or not target_text:
return positions
# 预处理图片
processed = self.preprocess_image(image)
if processed is None:
return positions
# 使用OCR或模板匹配识别文字位置
# 这里使用简单的轮廓检测作为示例
# 实际应用中可以使用pytesseract、paddleocr等OCR库
# 查找轮廓
contours, _ = cv2.findContours(processed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
# 过滤小面积轮廓
area = cv2.contourArea(contour)
if area < 100: # 根据实际情况调整阈值
continue
# 获取边界框
x, y, w, h = cv2.boundingRect(contour)
# 过滤异常尺寸
if w < 20 or h < 20 or w > 200 or h > 200:
continue
positions.append((x, y, w, h))
return positions
def match_target_positions(self, image, target_text):
"""
根据目标文字匹配位置
Args:
image: 背景图片
target_text: 目标文字
Returns:
list: 匹配的位置坐标列表
"""
# 这里实现具体的文字识别和匹配逻辑
# 示例:简单的关键词匹配
keyword_positions = []
all_positions = self.find_text_positions(image, target_text)
if not all_positions:
return keyword_positions
# 示例:选择几个位置作为目标位置
# 实际应用中需要根据OCR识别结果进行匹配
for i, (x, y, w, h) in enumerate(all_positions):
if i < 3: # 示例:选择前3个位置
center_x = x + w // 2
center_y = y + h // 2
keyword_positions.append((center_x, center_y))
return keyword_positions
def click_positions(self, positions):
"""
点击目标位置
Args:
positions: 目标位置列表
Returns:
bool: 是否点击成功
"""
try:
# 获取验证码容器
captcha_container = self.driver.find_element(By.CLASS_NAME, 'geetest_widget')
for idx, (x, y) in enumerate(positions):
# 在容器内执行点击
self.actions.move_to_element_with_offset(captcha_container, x, y)
self.actions.click()
self.actions.pause(0.5) # 点击间隔
print(f"点击位置 {idx + 1}: ({x}, {y})")
self.actions.perform()
time.sleep(1)
return True
except Exception as e:
print(f"点击失败: {e}")
return False
def verify_success(self):
"""
验证是否成功
Returns:
bool: 验证是否成功
"""
try:
# 等待验证结果
time.sleep(2)
# 检查成功提示
success_elements = [
(By.CLASS_NAME, 'geetest_success'),
(By.CLASS_NAME, 'geetest_success_animate'),
(By.XPATH, '//*[contains(text(), "验证成功")]')
]
for by, value in success_elements:
try:
element = self.driver.find_element(by, value)
if element.is_displayed():
print("验证成功!")
return True
except:
continue
return False
except Exception as e:
print(f"验证结果检查失败: {e}")
return False
def retry_verification(self):
"""
重试验证
"""
try:
refresh_btn = self.driver.find_element(By.CLASS_NAME, 'geetest_refresh')
refresh_btn.click()
time.sleep(2)
return True
except:
return False
def run_verification(self, url, target_text=None):
"""
运行完整验证流程
Args:
url: 目标页面URL
target_text: 目标文字(可选)
Returns:
bool: 验证是否成功
"""
print(f"开始验证: {url}")
# 1. 加载页面
self.load_page(url)
# 2. 检测验证码组件
elements = self.detect_geetest_widget()
if not elements.get('widget'):
print("未找到验证码组件")
return False
# 3. 触发验证
if not self.trigger_verification():
return False
# 最大重试次数
max_retries = 3
for attempt in range(max_retries):
print(f"\n--- 第 {attempt + 1} 次尝试 ---")
# 4. 获取验证码图片
images = self.get_captcha_images()
if not images.get('background'):
print("获取验证码图片失败")
if not self.retry_verification():
break
continue
# 5. 确定目标文字
prompt_text = images.get('prompt_text', '')
if target_text:
search_text = target_text
elif prompt_text:
# 从提示文字中提取关键词
search_text = self.extract_keywords(prompt_text)
else:
search_text = "验证码"
print(f"搜索目标: {search_text}")
# 6. 识别目标位置
bg_image = images['background']
target_positions = self.match_target_positions(bg_image, search_text)
if not target_positions:
print("未找到目标位置")
if not self.retry_verification():
break
continue
print(f"找到 {len(target_positions)} 个目标位置")
# 7. 点击目标位置
if not self.click_positions(target_positions):
if not self.retry_verification():
break
continue
# 8. 验证结果
if self.verify_success():
return True
# 等待下一次尝试
time.sleep(1)
print("验证失败")
return False
def extract_keywords(self, prompt):
"""
从提示文字中提取关键词
Args:
prompt: 提示文字
Returns:
str: 提取的关键词
"""
# 常见的点选验证提示模式
patterns = [
r'请依次点击(.*?)的汉字',
r'请点击图中的(.*?)字',
r'请点击(.*?)文字',
r'请点击(.*?)图标',
r'点击(.*?)进行验证'
]
for pattern in patterns:
match = re.search(pattern, prompt)
if match:
return match.group(1)
# 如果没有匹配到模式,返回原提示
return prompt
def close(self):
"""关闭浏览器"""
self.driver.quit()
def test_geetest_verification():
"""
测试极验3验证码验证
"""
# 使用极验官方演示页面进行测试
test_url = "https://www.geetest.com/demo/"
# 初始化验证器
verifier = GeetestClickVerifier()
try:
# 运行验证
success = verifier.run_verification(
url=test_url,
target_text="验证" # 指定目标文字
)
if success:
print("\n✅ 验证测试成功完成")
else:
print("\n❌ 验证测试失败")
return success
except Exception as e:
print(f"测试过程中发生错误: {e}")
return False
finally:
# 等待用户查看结果
input("\n按Enter键关闭浏览器...")
verifier.close()
if __name__ == "__main__":
# 运行测试
test_result = test_geetest_verification()
if test_result:
print("测试通过!")
else:
print("测试失败,请检查配置和网络连接。")
关键技术点说明:
-
Selenium自动化控制:
-
使用Selenium WebDriver进行浏览器自动化操作
-
添加反检测机制,隐藏自动化特征
-
通过ActionChains模拟人类点击行为
-
-
验证码组件识别:
-
通过CSS选择器定位极验验证码相关元素
-
支持检测多种极验验证码组件
-
自动触发验证码弹出
-
-
图片处理流程:
-
从网页中提取背景图片和数据URI
-
支持base64编码图片解码
-
图片预处理(灰度化、二值化、去噪)
-
-
文字识别策略:
-
轮廓检测定位文字区域
-
可扩展集成OCR库(如pytesseract、paddleocr)
-
关键词匹配算法
-
-
验证流程管理:
-
完整的验证状态机
-
失败重试机制
-
成功验证检测
-
-
反反爬虫策略:
-
添加随机延迟模拟人类操作
-
处理动态加载内容
-
错误恢复机制
-
使用说明:
-
安装依赖:
pip install selenium opencv-python numpy pillow requests -
下载对应版本的ChromeDriver
-
根据实际验证码类型调整目标文字识别逻辑
-
可通过继承和重写相关方法适应不同站点的验证码
注意:实际应用中可能需要根据具体网站的验证码实现进行调整,特别是文字识别部分可能需要使用更先进的OCR技术。
