自动化极验3点选验证码的识别与验证方案

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("测试失败,请检查配置和网络连接。")

关键技术点说明:

  1. Selenium自动化控制:

    • 使用Selenium WebDriver进行浏览器自动化操作

    • 添加反检测机制,隐藏自动化特征

    • 通过ActionChains模拟人类点击行为

  2. 验证码组件识别:

    • 通过CSS选择器定位极验验证码相关元素

    • 支持检测多种极验验证码组件

    • 自动触发验证码弹出

  3. 图片处理流程:

    • 从网页中提取背景图片和数据URI

    • 支持base64编码图片解码

    • 图片预处理(灰度化、二值化、去噪)

  4. 文字识别策略:

    • 轮廓检测定位文字区域

    • 可扩展集成OCR库(如pytesseract、paddleocr)

    • 关键词匹配算法

  5. 验证流程管理:

    • 完整的验证状态机

    • 失败重试机制

    • 成功验证检测

  6. 反反爬虫策略:

    • 添加随机延迟模拟人类操作

    • 处理动态加载内容

    • 错误恢复机制

使用说明:

  1. 安装依赖: pip install selenium opencv-python numpy pillow requests

  2. 下载对应版本的ChromeDriver

  3. 根据实际验证码类型调整目标文字识别逻辑

  4. 可通过继承和重写相关方法适应不同站点的验证码

注意:实际应用中可能需要根据具体网站的验证码实现进行调整,特别是文字识别部分可能需要使用更先进的OCR技术。

相关推荐
heze0916 小时前
sqli-labs-Less-12自动化注入方法
mysql·网络安全·自动化
阳光九叶草LXGZXJ16 小时前
达梦数据库-学习-43-定时备份模式和删除备份(Python+Crontab)
linux·运维·开发语言·数据库·python·学习
首席拯救HMI官16 小时前
【拯救HMI】HMI容错设计:如何减少操作失误并快速纠错?
大数据·运维·前端·javascript·网络·学习
深蓝电商API16 小时前
Scrapy与Splash结合爬取JavaScript渲染页面
javascript·爬虫·python·scrapy
AIFQuant16 小时前
2026 澳大利亚证券交易所(ASX)API 接入与 Python 量化策略
开发语言·python·websocket·金融·restful
木头左16 小时前
VIX期货基差异常下的指数期权波动率互换套利策略实现
python
HIT_Weston16 小时前
99、【Ubuntu】【Hugo】搭建私人博客:搜索功能(三)
linux·运维·ubuntu
heze0916 小时前
sqli-labs-Less-13自动化注入方法
mysql·网络安全·自动化
人工干智能16 小时前
python的高级技巧:Pandas中的`iloc[]`和`loc[]`
开发语言·python·pandas
小蜗的房子16 小时前
Oracle 19c RAC重建AWR步骤详解
linux·运维·数据库·sql·oracle·操作系统·oracle rac