Python爬虫超级鹰(Super Eagle)验证码识别终极指南:从入门到企业级应用的完整解决方案

一、引言:验证码识别——爬虫工程的最后一公里

在当今数据驱动的时代,验证码已成为网站反爬体系中的最后一道防线。根据2025年爬虫安全报告显示,87.4%的商业网站已部署至少一种验证码机制,其中:

  • 42.3% 使用文字/图形验证码
  • 35.6% 使用滑块验证码
  • 15.8% 使用点选验证码
  • 6.3% 使用其他复杂验证码(旋转、语序等)

面对如此复杂的验证码体系,手动识别已无法满足工程化需求。超级鹰(Super Eagle)作为国内领先的验证码识别服务平台,凭借其高准确率、多类型支持和合理定价,已成为众多爬虫工程师的首选解决方案。

本文将系统性地讲解超级鹰平台的使用方法、各类验证码的识别技巧、与爬虫框架的深度集成,以及企业级应用的最佳实践。通过本文,你将掌握从基础使用到高级优化的完整技能体系,构建一套可复用、可扩展、高性价比的验证码识别解决方案。


二、超级鹰平台全面解析

2.1 平台概述

超级鹰(Super Eagle)是国内专业的验证码识别服务平台,提供包括文字验证码、滑块验证码、点选验证码等在内的15+种验证码类型的识别服务。其核心优势包括:

  • 高准确率:文字验证码识别率>95%,滑块验证码识别率>90%
  • 多类型支持:覆盖主流验证码类型,持续更新
  • 快速响应:平均识别时间<1.5秒
  • 合理定价:按次计费,无月租费用
  • API友好:提供完善的Python SDK和详细文档

2.2 注册与认证流程

2.2.1 账号注册
  1. 访问超级鹰官网:https://www.chaojiying.com/
  2. 点击"注册",填写邮箱、手机号和密码
  3. 完成邮箱验证和手机验证
  4. 登录后进入"账户中心"
2.2.2 实名认证(必要步骤)
  1. 进入"账户中心" → "实名认证"
  2. 填写真实姓名、身份证号
  3. 上传身份证正反面照片
  4. 等待审核(通常24小时内)

重要提示:未完成实名认证的账号无法使用API服务

2.2.3 充值与套餐选择

超级鹰采用预付费模式,充值后按识别次数扣费:

验证码类型单价(元)推荐场景
文字/图形验证码0.01常规登录、搜索
滑块验证码0.03极验、腾讯防水墙
点选验证码0.0212306、阿里云
旋转验证码0.04特殊场景
语序验证码0.05高难度场景

充值建议

  • 新用户:先充值50元体验服务
  • 中小型项目:充值300-500元
  • 企业级应用:联系客服获取定制套餐

三、超级鹰API基础使用

3.1 API认证机制

超级鹰API采用用户名+密码+软件ID三重认证:

  • 用户名:注册时填写的账号
  • 密码:账号登录密码
  • 软件ID:在"开发者中心" → "我的软件ID"中获取

3.2 安装官方SDK

超级鹰提供官方Python SDK,安装方式:

pip install chaojiying

3.3 基础API调用

3.3.1 初始化客户端
from chaojiying import ChaojiyingClient

# 初始化客户端
chaojiying = ChaojiyingClient('your_username', 'your_password', 'your_soft_id')
3.3.2 识别文字验证码
def recognize_captcha(image_path):
    """
    识别文字验证码
    :param image_path: 验证码图片路径
    :return: 识别结果
    """
    # 1. 读取图片文件
    with open(image_path, 'rb') as f:
        image_data = f.read()
    
    # 2. 调用API识别 (验证码类型: 1902)
    result = chaojiying.PostPic(image_data, 1902)
    
    # 3. 解析结果
    if result['err_no'] == 0:
        return result['pic_str']
    else:
        raise Exception(f"识别失败: {result['err_str']}")

# 使用示例
captcha_text = recognize_captcha('captcha.jpg')
print(f"识别结果: {captcha_text}")
3.3.3 识别滑块验证码
def recognize_slider(image_path, bg_path):
    """
    识别滑块验证码
    :param image_path: 滑块图片路径
    :param bg_path: 背景图片路径
    :return: 滑块位置
    """
    # 读取图片文件
    with open(image_path, 'rb') as f:
        slider_data = f.read()
    with open(bg_path, 'rb') as f:
        bg_data = f.read()
    
    # 调用API识别 (验证码类型: 9101)
    result = chaojiying.PostPic3(image_data=slider_data, image_data2=bg_data, codetype=9101)
    
    # 解析结果
    if result['err_no'] == 0:
        # 返回滑块的x坐标
        return int(result['pic_str'])
    else:
        raise Exception(f"识别失败: {result['err_str']}")

# 使用示例
slider_x = recognize_slider('slider.jpg', 'background.jpg')
print(f"滑块位置: x={slider_x}")

3.4 API响应结构详解

超级鹰API返回标准JSON格式:

{
  "err_no": 0,
  "err_str": "OK",
  "pic_id": "1234567890",
  "pic_str": "abcd",
  "md5": "d41d8cd98f00b204e9800998ecf8427e"
}
字段类型说明
err_noint错误代码(0=成功)
err_strstring错误信息
pic_idstring图片ID(用于纠错)
pic_strstring识别结果
md5string图片MD5(用于验证)

常见错误代码

  • -1001: 账号余额不足
  • -1002: 软件ID错误
  • -1003: 用户名或密码错误
  • -1004: 验证码类型错误
  • -1005: 上传图片失败
  • -1006: 识别超时

四、文字验证码识别深度实践

4.1 文字验证码类型详解

超级鹰支持多种文字验证码类型:

类型ID说明适用场景
1001纯数字4位数字验证码
1002纯英文大小写混合
1003数字+英文常规验证码
1004仅汉字中文验证码
1005带干扰线线条干扰
1006带干扰点点状干扰
1007带波浪线曲线干扰
1008带扭曲字符扭曲
1009带旋转字符旋转
1902复杂类型综合干扰

4.2 实战案例:政府网站登录

4.2.1 案例背景

某政府网站使用4位数字验证码,类型为1001,需要实现自动登录。

4.2.2 完整代码实现
import requests
from chaojiying import ChaojiyingClient
from PIL import Image
import io
import time

class GovernmentSiteLogin:
    def __init__(self, username, password, cjy_username, cjy_password, cjy_soft_id):
        self.session = requests.Session()
        self.username = username
        self.password = password
        self.chaojiying = ChaojiyingClient(cjy_username, cjy_password, cjy_soft_id)
        
        # 设置请求头
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Referer': 'https://www.gov-example.com/login',
            'Origin': 'https://www.gov-example.com'
        }
    
    def get_captcha(self):
        """获取验证码图片"""
        captcha_url = "https://www.gov-example.com/captcha"
        params = {
            't': str(int(time.time() * 1000))
        }
        
        response = self.session.get(captcha_url, params=params, headers=self.headers)
        if response.status_code != 200:
            raise Exception("获取验证码失败")
        
        # 保存验证码图片(用于调试)
        with open('captcha.jpg', 'wb') as f:
            f.write(response.content)
        
        return response.content
    
    def recognize_captcha(self, image_data):
        """识别验证码"""
        result = self.chaojiying.PostPic(image_data, 1001)
        
        if result['err_no'] == 0:
            return result['pic_str']
        elif result['err_no'] == -1001:
            raise Exception("超级鹰账号余额不足")
        else:
            raise Exception(f"验证码识别失败: {result['err_str']}")
    
    def login(self):
        """执行登录流程"""
        # 1. 获取验证码
        captcha_image = self.get_captcha()
        
        # 2. 识别验证码
        captcha_text = self.recognize_captcha(captcha_image)
        print(f"识别的验证码: {captcha_text}")
        
        # 3. 构建登录数据
        login_data = {
            'username': self.username,
            'password': self.password,
            'captcha': captcha_text,
            'remember': 'on'
        }
        
        # 4. 发送登录请求
        login_url = "https://www.gov-example.com/login"
        response = self.session.post(login_url, data=login_data, headers=self.headers)
        
        # 5. 检查登录结果
        if "欢迎登录" in response.text:
            print("登录成功!")
            return True
        elif "验证码错误" in response.text:
            print("验证码识别错误,尝试重新识别...")
            # 提交错误反馈
            self.chaojiying.ReportError(result['pic_id'])
            return False
        else:
            print("登录失败,可能密码错误")
            return False
    
    def get_user_data(self):
        """获取用户数据"""
        # 需要先登录
        if not self.login():
            # 尝试重新登录
            if not self.login():
                raise Exception("多次登录失败")
        
        # 获取用户数据
        user_url = "https://www.gov-example.com/user/profile"
        response = self.session.get(user_url, headers=self.headers)
        
        if response.status_code == 200:
            return response.json()
        else:
            raise Exception("获取用户数据失败")

# 使用示例
if __name__ == "__main__":
    login = GovernmentSiteLogin(
        username="your_username",
        password="your_password",
        cjy_username="super_eagle_user",
        cjy_password="super_eagle_pass",
        cjy_soft_id="your_soft_id"
    )
    
    try:
        user_data = login.get_user_data()
        print(f"用户数据: {user_data}")
    except Exception as e:
        print(f"发生错误: {str(e)}")
4.2.3 验证码预处理技巧

对于质量较差的验证码,可进行预处理提高识别率:

def preprocess_captcha(image_path, output_path=None):
    """
    验证码预处理(去噪、二值化等)
    :param image_path: 原始图片路径
    :param output_path: 处理后图片保存路径
    :return: 处理后的图片字节数据
    """
    from PIL import Image
    import numpy as np
    
    # 1. 打开图片
    img = Image.open(image_path)
    
    # 2. 转为灰度
    img = img.convert('L')
    
    # 3. 二值化处理
    threshold = 128
    img = img.point(lambda p: p > threshold and 255)
    
    # 4. 去噪处理(8邻域去噪)
    img_array = np.array(img)
    rows, cols = img_array.shape
    
    for i in range(1, rows-1):
        for j in range(1, cols-1):
            # 统计8邻域内白色像素数量
            white_count = 0
            for x in range(-1, 2):
                for y in range(-1, 2):
                    if img_array[i+x, j+y] == 255:
                        white_count += 1
            
            # 如果白色像素少于3个,认为是噪点,设为黑色
            if white_count < 3:
                img_array[i, j] = 0
    
    # 5. 转回Image对象
    img = Image.fromarray(img_array)
    
    # 6. 保存处理后的图片
    if output_path:
        img.save(output_path)
    
    # 7. 返回字节数据
    img_byte_arr = io.BytesIO()
    img.save(img_byte_arr, format='JPEG')
    return img_byte_arr.getvalue()

# 使用示例
processed_image = preprocess_captcha('captcha.jpg', 'processed.jpg')
result = chaojiying.PostPic(processed_image, 1001)

五、滑块验证码识别深度实践

5.1 滑块验证码原理

滑块验证码通过要求用户将滑块拖动到正确位置来验证是否为人类。主要分为两类:

  1. 极验类型:滑块与背景有缺口,需要计算缺口位置
  2. 腾讯防水墙类型:滑块需要匹配背景图案

5.2 超级鹰API接口详解

超级鹰提供两种滑块识别接口:

5.2.1 PostPic3(推荐)
result = chaojiying.PostPic3(
    image_data=slider_image,  # 滑块图片
    image_data2=bg_image,     # 背景图片
    codetype=9101             # 验证码类型
)

返回结果

  • pic_str: 滑块应拖动的X坐标(单位:像素)
  • 例如:"230" 表示需要拖动到X=230的位置
5.2.2 PostPic (旧版)
# 旧版接口(不推荐)
result = chaojiying.PostPic(
    image_data=combined_image,  # 合并的滑块和背景图片
    codetype=9004               # 验证码类型
)

推荐使用PostPic3:分离的滑块和背景图片识别更准确

5.3 实战案例:电商网站登录

5.3.1 案例背景

某电商网站使用极验滑块验证码,需要实现自动登录。

5.3.2 完整代码实现
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
from selenium.webdriver.common.action_chains import ActionChains
import time
import random
from chaojiying import ChaojiyingClient

class EcommerceSliderLogin:
    def __init__(self, username, password, cjy_username, cjy_password, cjy_soft_id):
        self.username = username
        self.password = password
        self.chaojiying = ChaojiyingClient(cjy_username, cjy_password, cjy_soft_id)
        
        # 初始化浏览器
        options = webdriver.ChromeOptions()
        options.add_argument("--disable-blink-features=AutomationControlled")
        options.add_experimental_option("excludeSwitches", ["enable-automation"])
        options.add_experimental_option('useAutomationExtension', False)
        
        self.driver = webdriver.Chrome(options=options)
        self.driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
            'source': '''
                Object.defineProperty(navigator, 'webdriver', {
                    get: () => undefined
                })
            '''
        })
    
    def download_image(self, element, save_path):
        """下载图片元素"""
        # 获取图片URL
        img_url = element.get_attribute('src')
        if not img_url:
            # 可能是base64编码
            img_url = element.get_attribute('src')
        
        # 下载图片
        if img_url.startswith('data:image'):
            # 处理base64图片
            img_data = img_url.split(',')[1]
            img_bytes = base64.b64decode(img_data)
        else:
            # 下载网络图片
            img_bytes = requests.get(img_url).content
        
        # 保存图片
        with open(save_path, 'wb') as f:
            f.write(img_bytes)
        
        return img_bytes
    
    def get_slider_position(self):
        """获取滑块位置"""
        # 1. 等待滑块元素出现
        WebDriverWait(self.driver, 10).until(
            EC.presence_of_element_located((By.CLASS_NAME, 'geetest_slider_button'))
        )
        
        # 2. 获取滑块和背景图片
        slider_img = self.driver.find_element(By.CLASS_NAME, 'geetest_slider')
        bg_img = self.driver.find_element(By.CLASS_NAME, 'geetest_bg')
        
        # 3. 下载图片
        slider_bytes = self.download_image(slider_img, 'slider.jpg')
        bg_bytes = self.download_image(bg_img, 'background.jpg')
        
        # 4. 识别滑块位置
        result = self.chaojiying.PostPic3(slider_bytes, bg_bytes, 9101)
        
        if result['err_no'] == 0:
            return int(result['pic_str'])
        else:
            raise Exception(f"滑块识别失败: {result['err_str']}")
    
    def human_like_drag(self, slider_element, target_x):
        """模拟人类拖动行为"""
        # 初始位置
        ActionChains(self.driver).click_and_hold(slider_element).perform()
        time.sleep(0.5)
        
        # 生成人类拖动轨迹
        tracks = self.generate_drag_tracks(target_x)
        
        # 模拟拖动
        for x, y, t in tracks:
            ActionChains(self.driver).move_by_offset(x, y).perform()
            time.sleep(t)
        
        # 释放鼠标
        time.sleep(0.5)
        ActionChains(self.driver).release().perform()
        time.sleep(2)
    
    def generate_drag_tracks(self, target_x, distance=None):
        """
        生成模拟人类的拖动轨迹
        :param target_x: 目标位置
        :param distance: 拖动距离(可选)
        """
        if distance is None:
            distance = target_x
        
        # 1. 加速阶段(占总距离40%)
        acceleration_distance = int(distance * 0.4)
        # 2. 匀速阶段(占总距离20%)
        constant_speed_distance = int(distance * 0.2)
        # 3. 减速阶段(占总距离40%)
        deceleration_distance = distance - acceleration_distance - constant_speed_distance
        
        tracks = []
        current_x = 0
        current_y = 0
        start_time = time.time()
        
        # 加速阶段
        for i in range(1, acceleration_distance + 1):
            # 模拟加速度
            v = 0.2 * i**0.5
            x = i - (i-1)
            y = random.randint(-1, 1)  # 小幅度上下波动
            t = random.uniform(0.01, 0.03)
            tracks.append((x, y, t))
            current_x += x
        
        # 匀速阶段
        for i in range(constant_speed_distance):
            x = 1
            y = random.randint(-1, 1)
            t = random.uniform(0.02, 0.05)
            tracks.append((x, y, t))
            current_x += x
        
        # 减速阶段
        for i in range(deceleration_distance, 0, -1):
            # 模拟减速度
            v = 0.5 * i**0.5
            x = 1 if i > 1 else i
            y = random.randint(-1, 1)
            t = random.uniform(0.03, 0.08)
            tracks.append((x, y, t))
            current_x += x
        
        # 微调(避免精确到位)
        if current_x < target_x:
            remaining = target_x - current_x
            for i in range(remaining):
                tracks.append((1, 0, random.uniform(0.05, 0.1)))
        elif current_x > target_x:
            overshoot = current_x - target_x
            for i in range(overshoot):
                tracks.append((-1, 0, random.uniform(0.05, 0.1)))
        
        return tracks
    
    def login(self):
        """执行登录流程"""
        try:
            # 1. 打开登录页面
            self.driver.get("https://www.ecommerce-example.com/login")
            
            # 2. 输入用户名和密码
            self.driver.find_element(By.ID, "username").send_keys(self.username)
            self.driver.find_element(By.ID, "password").send_keys(self.password)
            
            # 3. 点击登录触发滑块验证
            self.driver.find_element(By.CLASS_NAME, "login-btn").click()
            
            # 4. 获取滑块位置
            slider_position = self.get_slider_position()
            print(f"识别的滑块位置: x={slider_position}")
            
            # 5. 定位滑块元素
            slider_element = self.driver.find_element(By.CLASS_NAME, "geetest_slider_button")
            
            # 6. 模拟人类拖动
            self.human_like_drag(slider_element, slider_position)
            
            # 7. 检查验证结果
            time.sleep(3)
            if "验证成功" in self.driver.page_source:
                print("滑块验证通过!")
                return True
            else:
                print("滑块验证失败")
                # 提交错误反馈
                self.chaojiying.ReportError(result['pic_id'])
                return False
        
        except Exception as e:
            print(f"登录过程中发生错误: {str(e)}")
            return False
    
    def close(self):
        """关闭浏览器"""
        self.driver.quit()

# 使用示例
if __name__ == "__main__":
    login = EcommerceSliderLogin(
        username="your_username",
        password="your_password",
        cjy_username="super_eagle_user",
        cjy_password="super_eagle_pass",
        cjy_soft_id="your_soft_id"
    )
    
    try:
        if login.login():
            print("登录成功,可以继续操作")
            # 这里可以添加后续操作,如爬取数据
        else:
            print("登录失败")
    finally:
        login.close()
5.3.3 滑块轨迹优化技巧

人类拖动滑块的行为具有以下特征,模拟这些特征可提高通过率:

  1. 加速度变化:开始慢,中间快,结束慢
  2. 小幅波动:X/Y方向有微小抖动
  3. 不精确到位:通常会略微超过或不足目标位置
  4. 速度变化:速度不均匀,有停顿
def generate_human_drag_tracks(target_x, total_time=2.5):
    """
    生成高度模拟人类的拖动轨迹
    :param target_x: 目标X坐标
    :param total_time: 总拖动时间(秒)
    """
    # 1. 基础参数
    total_points = int(total_time * 30)  # 每秒30个点
    current_x = 0
    current_y = 0
    current_time = 0
    
    # 2. 生成随机波动参数
    wave_amplitude = random.uniform(0.5, 1.5)  # 波动幅度
    wave_frequency = random.uniform(0.8, 1.2)  # 波动频率
    
    # 3. 生成加速度曲线(S型曲线)
    acceleration_curve = []
    for i in range(total_points):
        t = i / total_points
        # S型曲线:开始慢,中间快,结束慢
        speed = 0.5 - 0.5 * math.cos(math.pi * t)
        acceleration_curve.append(speed)
    
    # 4. 生成轨迹点
    tracks = []
    for i in range(total_points):
        # 计算当前应到达的位置比例
        progress = sum(acceleration_curve[:i+1]) / sum(acceleration_curve)
        target_progress = progress * target_x
        
        # 计算X增量(考虑波动)
        x_increment = target_progress - current_x
        # 添加随机波动
        x_jitter = wave_amplitude * math.sin(wave_frequency * current_time * 2 * math.pi)
        x = x_increment + x_jitter
        
        # Y方向随机波动(小幅度)
        y = random.uniform(-1.5, 1.5)
        
        # 时间增量
        time_increment = total_time / total_points
        
        tracks.append((x, y, time_increment))
        
        # 更新当前位置
        current_x += x
        current_y += y
        current_time += time_increment
    
    # 5. 微调确保到达目标位置
    final_x = sum(point[0] for point in tracks)
    if abs(final_x - target_x) > 0.5:
        adjustment = target_x - final_x
        tracks[-1] = (tracks[-1][0] + adjustment, tracks[-1][1], tracks[-1][2])
    
    return tracks

六、点选验证码识别深度实践

6.1 点选验证码原理

点选验证码要求用户点击图片中指定的文字或图案,常见类型包括:

  • 文字点选:点击"点击{文字}"中的对应文字
  • 图案点选:点击指定的图案(如交通标志)
  • 语序点选:按顺序点击多个文字

6.2 超级鹰API接口详解

result = chaojiying.PostPic(
    image_data=image_bytes,  # 验证码图片
    codetype=9004            # 点选验证码类型
)

返回结果

  • pic_str: 点击坐标,格式为"x1,y1|x2,y2|..."
  • 例如:"100,200|300,400" 表示需要点击(100,200)和(300,400)

6.3 实战案例:12306登录

6.3.1 案例背景

12306网站使用文字点选验证码,需要识别并点击指定文字。

6.3.2 完整代码实现
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
from selenium.webdriver.common.action_chains import ActionChains
import time
import requests
from chaojiying import ChaojiyingClient
import base64
import json

class Train12306Login:
    def __init__(self, username, password, cjy_username, cjy_password, cjy_soft_id):
        self.username = username
        self.password = password
        self.chaojiying = ChaojiyingClient(cjy_username, cjy_password, cjy_soft_id)
        
        # 初始化浏览器
        options = webdriver.ChromeOptions()
        options.add_argument("--disable-blink-features=AutomationControlled")
        options.add_experimental_option("excludeSwitches", ["enable-automation"])
        options.add_experimental_option('useAutomationExtension', False)
        
        self.driver = webdriver.Chrome(options=options)
        self.driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
            'source': '''
                Object.defineProperty(navigator, 'webdriver', {
                    get: () => undefined
                })
            '''
        })
    
    def get_captcha_image(self):
        """获取12306验证码图片"""
        # 1. 等待验证码图片加载
        WebDriverWait(self.driver, 10).until(
            EC.presence_of_element_located((By.ID, "nc_1_n1z"))
        )
        
        # 2. 获取验证码图片元素
        captcha_img = self.driver.find_element(By.ID, "nc_1_n1z")
        
        # 3. 获取图片数据
        img_url = captcha_img.get_attribute('src')
        
        # 4. 处理base64图片
        if img_url.startswith('data:image'):
            img_data = img_url.split(',')[1]
            img_bytes = base64.b64decode(img_data)
        else:
            # 下载网络图片
            img_bytes = requests.get(img_url).content
        
        return img_bytes
    
    def recognize_captcha_points(self, image_bytes):
        """识别点选验证码坐标"""
        # 调用超级鹰API (12306点选验证码类型: 9501)
        result = self.chaojiying.PostPic(image_bytes, 9501)
        
        if result['err_no'] == 0:
            # 解析坐标
            points = []
            for point_str in result['pic_str'].split('|'):
                x, y = map(int, point_str.split(','))
                points.append((x, y))
            return points, result['pic_id']
        else:
            raise Exception(f"点选验证码识别失败: {result['err_str']}")
    
    def click_points(self, points):
        """点击指定坐标"""
        # 获取验证码图片元素
        captcha_img = self.driver.find_element(By.ID, "nc_1_n1z")
        # 获取元素位置
        location = captcha_img.location
        size = captcha_img.size
        
        for x, y in points:
            # 计算实际点击位置(相对于整个页面)
            click_x = location['x'] + x
            click_y = location['y'] + y
            
            # 模拟人类点击
            ActionChains(self.driver).move_to_element_with_offset(
                captcha_img, x, y
            ).click().perform()
            time.sleep(random.uniform(0.3, 0.8))
    
    def human_like_click(self, element, x, y):
        """模拟人类点击行为"""
        # 随机移动到目标点附近
        offset_x = random.randint(-5, 5)
        offset_y = random.randint(-5, 5)
        
        # 移动到目标点
        ActionChains(self.driver).move_to_element_with_offset(
            element, x + offset_x, y + offset_y
        ).perform()
        time.sleep(random.uniform(0.1, 0.3))
        
        # 微调到精确位置
        ActionChains(self.driver).move_by_offset(-offset_x, -offset_y).perform()
        time.sleep(random.uniform(0.05, 0.15))
        
        # 点击
        ActionChains(self.driver).click().perform()
        time.sleep(random.uniform(0.1, 0.3))
    
    def login(self):
        """执行登录流程"""
        try:
            # 1. 打开12306登录页面
            self.driver.get("https://www.12306.cn/index/")
            
            # 2. 点击登录按钮
            WebDriverWait(self.driver, 10).until(
                EC.element_to_be_clickable((By.ID, "login"))
            ).click()
            
            # 3. 等待点选验证码出现
            time.sleep(2)
            
            # 4. 获取验证码图片
            captcha_image = self.get_captcha_image()
            
            # 5. 识别点选坐标
            points, pic_id = self.recognize_captcha_points(captcha_image)
            print(f"识别的点选坐标: {points}")
            
            # 6. 点击坐标
            for x, y in points:
                self.human_like_click(
                    self.driver.find_element(By.ID, "nc_1_n1z"), 
                    x, y
                )
            
            # 7. 等待验证结果
            time.sleep(2)
            if "验证通过" in self.driver.page_source:
                print("点选验证通过!")
                
                # 8. 输入用户名和密码
                self.driver.find_element(By.ID, "username").send_keys(self.username)
                self.driver.find_element(By.ID, "password").send_keys(self.password)
                
                # 9. 点击登录
                self.driver.find_element(By.ID, "loginBtn").click()
                
                # 10. 检查登录结果
                time.sleep(3)
                if "我的12306" in self.driver.title:
                    print("登录成功!")
                    return True
                else:
                    print("登录失败,可能密码错误")
                    return False
            else:
                print("点选验证失败")
                # 提交错误反馈
                self.chaojiying.ReportError(pic_id)
                return False
        
        except Exception as e:
            print(f"登录过程中发生错误: {str(e)}")
            return False
    
    def close(self):
        """关闭浏览器"""
        self.driver.quit()

# 使用示例
if __name__ == "__main__":
    login = Train12306Login(
        username="your_username",
        password="your_password",
        cjy_username="super_eagle_user",
        cjy_password="super_eagle_pass",
        cjy_soft_id="your_soft_id"
    )
    
    try:
        if login.login():
            print("成功登录12306,可以继续操作")
            # 这里可以添加后续操作,如查询车票
        else:
            print("登录失败")
    finally:
        login.close()
6.3.3 坐标校正技巧

由于屏幕分辨率、缩放比例等因素,识别出的坐标可能需要校正:

def calibrate_coordinates(points, element, scale_factor=1.0, offset_x=0, offset_y=0):
    """
    校正点击坐标
    :param points: 原始坐标列表[(x1,y1), (x2,y2), ...]
    :param element: 验证码图片元素
    :param scale_factor: 缩放比例(如页面缩放)
    :param offset_x: X方向偏移
    :param offset_y: Y方向偏移
    :return: 校正后的坐标列表
    """
    # 获取元素位置和尺寸
    location = element
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZTLJQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值