Selenium Actions 链操作 #

ActionChains 概述 #

什么是 ActionChains #

text
┌─────────────────────────────────────────────────────────────┐
│                    ActionChains 工作原理                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ActionChains 是 Selenium 提供的低级交互 API                  │
│  允许模拟复杂的用户交互操作                                    │
│                                                             │
│  特点:                                                      │
│  • 链式调用 - 多个操作可以串联执行                             │
│  • 延迟执行 - 操作添加到队列,调用 perform() 时执行            │
│  • 精确控制 - 可以精确控制鼠标和键盘操作                        │
│                                                             │
│  工作流程:                                                   │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  actions = ActionChains(driver)                     │   │
│  │       │                                             │   │
│  │       ├── click(element)     ──> 队列: [click]      │   │
│  │       ├── send_keys("text")  ──> 队列: [click,      │   │
│  │       │                               send_keys]    │   │
│  │       ├── double_click()     ──> 队列: [click,      │   │
│  │       │                               send_keys,    │   │
│  │       │                               double_click] │   │
│  │       │                                             │   │
│  │       └── perform()          ──> 按顺序执行所有操作  │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

基本用法 #

python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

driver = webdriver.Chrome()
driver.get("https://example.com")

# 创建 ActionChains 对象
actions = ActionChains(driver)

# 单个操作
element = driver.find_element(By.ID, "button")
actions.click(element).perform()

# 链式操作
actions.click(element).send_keys("Hello").perform()

driver.quit()

鼠标操作 #

点击操作 #

python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

driver = webdriver.Chrome()
driver.get("https://example.com")

element = driver.find_element(By.ID, "button")
actions = ActionChains(driver)

# 单击元素
actions.click(element).perform()

# 在当前位置单击
actions.click().perform()

# 双击元素
actions.double_click(element).perform()

# 右键点击(上下文菜单)
actions.context_click(element).perform()

driver.quit()

悬停操作 #

python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

driver = webdriver.Chrome()
driver.get("https://example.com")

actions = ActionChains(driver)

# 悬停到元素
menu = driver.find_element(By.ID, "menu")
actions.move_to_element(menu).perform()

# 悬停后点击子菜单
submenu = driver.find_element(By.ID, "submenu")
actions.move_to_element(menu).move_to_element(submenu).click().perform()

# 多级菜单悬停
menu1 = driver.find_element(By.ID, "menu1")
menu2 = driver.find_element(By.ID, "menu2")
menu3 = driver.find_element(By.ID, "menu3")

actions.move_to_element(menu1).pause(0.5).move_to_element(menu2).pause(0.5).move_to_element(menu3).click().perform()

driver.quit()

移动操作 #

python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

driver = webdriver.Chrome()
driver.get("https://example.com")

element = driver.find_element(By.ID, "target")
actions = ActionChains(driver)

# 移动到元素中心
actions.move_to_element(element).perform()

# 移动到元素的偏移位置
actions.move_to_element_with_offset(element, 10, 20).perform()

# 从当前位置移动指定偏移量
actions.move_by_offset(100, 50).perform()

# 重置鼠标位置
actions.move_to_element(element).perform()
actions.move_by_offset(-50, -50).perform()

driver.quit()

拖拽操作 #

python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

driver = webdriver.Chrome()
driver.get("https://example.com/drag-drop")

source = driver.find_element(By.ID, "draggable")
target = driver.find_element(By.ID, "droppable")
actions = ActionChains(driver)

# 方式一:使用 drag_and_drop
actions.drag_and_drop(source, target).perform()

# 方式二:手动组合操作
actions.click_and_hold(source).move_to_element(target).release().perform()

# 拖拽到偏移位置
actions.drag_and_drop_by_offset(source, 100, 50).perform()

# 手动拖拽到偏移位置
actions.click_and_hold(source).move_by_offset(100, 50).release().perform()

# 拖拽过程中暂停
actions.click_and_hold(source).pause(1).move_to_element(target).pause(1).release().perform()

driver.quit()

键盘操作 #

按键操作 #

python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains

driver = webdriver.Chrome()
driver.get("https://example.com")

input_field = driver.find_element(By.ID, "input")
actions = ActionChains(driver)

# 发送文本
actions.send_keys("Hello World").perform()

# 发送到特定元素
actions.send_keys_to_element(input_field, "Hello World").perform()

# 发送特殊按键
actions.send_keys(Keys.ENTER).perform()
actions.send_keys(Keys.TAB).perform()
actions.send_keys(Keys.ESCAPE).perform()

driver.quit()

修饰键操作 #

python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains

driver = webdriver.Chrome()
driver.get("https://example.com")

input_field = driver.find_element(By.ID, "input")
actions = ActionChains(driver)

# 按下 Shift 输入大写
actions.key_down(Keys.SHIFT).send_keys("hello").key_up(Keys.SHIFT).perform()

# 全选 (Ctrl+A)
actions.key_down(Keys.CONTROL).send_keys('a').key_up(Keys.CONTROL).perform()

# 复制 (Ctrl+C)
actions.key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()

# 粘贴 (Ctrl+V)
actions.key_down(Keys.CONTROL).send_keys('v').key_up(Keys.CONTROL).perform()

# 剪切 (Ctrl+X)
actions.key_down(Keys.CONTROL).send_keys('x').key_up(Keys.CONTROL).perform()

# macOS 使用 COMMAND 键
actions.key_down(Keys.COMMAND).send_keys('a').key_up(Keys.COMMAND).perform()

driver.quit()

组合键操作 #

python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains

driver = webdriver.Chrome()
driver.get("https://example.com")

input_field = driver.find_element(By.ID, "input")
actions = ActionChains(driver)

# Ctrl+Shift+End (选择到末尾)
actions.key_down(Keys.CONTROL).key_down(Keys.SHIFT).send_keys(Keys.END).key_up(Keys.SHIFT).key_up(Keys.CONTROL).perform()

# Alt+F4 (关闭窗口)
actions.key_down(Keys.ALT).send_keys(Keys.F4).key_up(Keys.ALT).perform()

# Ctrl+Shift+T (重新打开关闭的标签页)
actions.key_down(Keys.CONTROL).key_down(Keys.SHIFT).send_keys('t').key_up(Keys.SHIFT).key_up(Keys.CONTROL).perform()

driver.quit()

高级操作 #

暂停操作 #

python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

driver = webdriver.Chrome()
driver.get("https://example.com")

element = driver.find_element(By.ID, "target")
actions = ActionChains(driver)

# pause(seconds) - 在操作链中添加暂停
actions.move_to_element(element).pause(1).click().pause(0.5).double_click().perform()

# 用于等待动画完成
actions.click(element).pause(2).send_keys("text").perform()

driver.quit()

点击并按住 #

python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

driver = webdriver.Chrome()
driver.get("https://example.com")

element = driver.find_element(By.ID, "target")
actions = ActionChains(driver)

# 点击并按住
actions.click_and_hold(element).perform()

# 移动后释放
actions.click_and_hold(element).move_by_offset(100, 0).release().perform()

# 长按(模拟长按操作)
actions.click_and_hold(element).pause(2).release().perform()

driver.quit()

释放操作 #

python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

driver = webdriver.Chrome()
driver.get("https://example.com")

element = driver.find_element(By.ID, "target")
actions = ActionChains(driver)

# release() - 释放鼠标按键
actions.click_and_hold(element).move_by_offset(50, 50).release().perform()

# release(element) - 在指定元素上释放
target = driver.find_element(By.ID, "drop-target")
actions.click_and_hold(element).move_to_element(target).release().perform()

driver.quit()

复杂场景示例 #

拖拽排序 #

python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

def drag_to_sort():
    driver = webdriver.Chrome()
    driver.get("https://example.com/sortable")
    
    try:
        items = driver.find_elements(By.CSS_SELECTOR, ".sortable-item")
        actions = ActionChains(driver)
        
        # 将第一项拖到最后一项后面
        actions.drag_and_drop(items[0], items[-1]).perform()
        
        # 或者使用偏移量精确控制
        first_item = items[0]
        actions.click_and_hold(first_item).move_by_offset(0, 200).release().perform()
        
        print("拖拽排序完成")
        
    finally:
        driver.quit()

drag_to_sort()

多级菜单导航 #

python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

def navigate_multi_level_menu():
    driver = webdriver.Chrome()
    driver.get("https://example.com")
    
    try:
        actions = ActionChains(driver)
        
        # 三级菜单导航
        menu1 = driver.find_element(By.CSS_SELECTOR, ".menu-level-1")
        menu2 = driver.find_element(By.CSS_SELECTOR, ".menu-level-2")
        menu3 = driver.find_element(By.CSS_SELECTOR, ".menu-level-3")
        
        # 链式悬停操作
        actions.move_to_element(menu1).pause(0.3).move_to_element(menu2).pause(0.3).move_to_element(menu3).click().perform()
        
        print("菜单导航完成")
        
    finally:
        driver.quit()

navigate_multi_level_menu()

画布绘图 #

python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

def draw_on_canvas():
    driver = webdriver.Chrome()
    driver.get("https://example.com/canvas")
    
    try:
        canvas = driver.find_element(By.ID, "drawing-canvas")
        actions = ActionChains(driver)
        
        # 移动到画布中心
        actions.move_to_element(canvas)
        
        # 绘制正方形
        actions.click_and_hold().move_by_offset(100, 0).move_by_offset(0, 100).move_by_offset(-100, 0).move_by_offset(0, -100).release().perform()
        
        # 绘制直线
        actions.move_to_element(canvas).move_by_offset(-50, -50).click_and_hold().move_by_offset(100, 100).release().perform()
        
        print("画布绘图完成")
        
    finally:
        driver.quit()

draw_on_canvas()

滑块验证 #

python
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

def slider_verification():
    driver = webdriver.Chrome()
    driver.get("https://example.com/slider-captcha")
    
    try:
        wait = WebDriverWait(driver, 10)
        
        # 等待滑块出现
        slider = wait.until(EC.presence_of_element_located((By.ID, "slider-button")))
        
        actions = ActionChains(driver)
        
        # 点击滑块并拖动
        actions.click_and_hold(slider).move_by_offset(200, 0).pause(0.5).release().perform()
        
        # 验证是否成功
        success = driver.find_element(By.ID, "success-message")
        if success.is_displayed():
            print("滑块验证成功")
        
    finally:
        driver.quit()

slider_verification()

文本选择 #

python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains

def select_text():
    driver = webdriver.Chrome()
    driver.get("https://example.com")
    
    try:
        textarea = driver.find_element(By.ID, "content")
        textarea.send_keys("这是一段需要选择的文本内容,用于演示文本选择功能。")
        
        actions = ActionChains(driver)
        
        # 方式一:双击选择单词
        actions.double_click(textarea).perform()
        
        # 方式二:拖拽选择文本
        actions.click(textarea).click_and_hold().move_by_offset(100, 0).release().perform()
        
        # 方式三:使用键盘选择
        actions.click(textarea).key_down(Keys.SHIFT).send_keys(Keys.ARROW_RIGHT * 10).key_up(Keys.SHIFT).perform()
        
        # 方式四:全选
        actions.click(textarea).key_down(Keys.CONTROL).send_keys('a').key_up(Keys.CONTROL).perform()
        
        print("文本选择完成")
        
    finally:
        driver.quit()

select_text()

复制粘贴操作 #

python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains

def copy_paste_operation():
    driver = webdriver.Chrome()
    driver.get("https://example.com")
    
    try:
        source = driver.find_element(By.ID, "source")
        target = driver.find_element(By.ID, "target")
        
        actions = ActionChains(driver)
        
        # 选择源文本
        actions.click(source).key_down(Keys.CONTROL).send_keys('a').key_up(Keys.CONTROL).perform()
        
        # 复制
        actions.key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
        
        # 移动到目标并粘贴
        actions.click(target).key_down(Keys.CONTROL).send_keys('v').key_up(Keys.CONTROL).perform()
        
        print("复制粘贴完成")
        
    finally:
        driver.quit()

copy_paste_operation()

ActionChains 工具类 #

python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains

class ActionsHelper:
    def __init__(self, driver):
        self.driver = driver
        self.actions = ActionChains(driver)
    
    def hover_and_click(self, hover_element, click_element):
        """悬停后点击"""
        self.actions.move_to_element(hover_element).click(click_element).perform()
    
    def drag_to_element(self, source, target):
        """拖拽到目标元素"""
        self.actions.drag_and_drop(source, target).perform()
    
    def drag_by_offset(self, element, x, y):
        """拖拽指定偏移量"""
        self.actions.drag_and_drop_by_offset(element, x, y).perform()
    
    def select_all(self, element=None):
        """全选"""
        if element:
            self.actions.click(element).key_down(Keys.CONTROL).send_keys('a').key_up(Keys.CONTROL).perform()
        else:
            self.actions.key_down(Keys.CONTROL).send_keys('a').key_up(Keys.CONTROL).perform()
    
    def copy(self):
        """复制"""
        self.actions.key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
    
    def paste(self, element=None):
        """粘贴"""
        if element:
            self.actions.click(element).key_down(Keys.CONTROL).send_keys('v').key_up(Keys.CONTROL).perform()
        else:
            self.actions.key_down(Keys.CONTROL).send_keys('v').key_up(Keys.CONTROL).perform()
    
    def cut(self):
        """剪切"""
        self.actions.key_down(Keys.CONTROL).send_keys('x').key_up(Keys.CONTROL).perform()
    
    def type_with_delay(self, element, text, delay=0.1):
        """带延迟的输入"""
        import time
        for char in text:
            element.send_keys(char)
            time.sleep(delay)
    
    def draw_line(self, canvas, start_offset, end_offset):
        """在画布上画线"""
        self.actions.move_to_element(canvas).move_by_offset(*start_offset).click_and_hold().move_by_offset(
            end_offset[0] - start_offset[0], end_offset[1] - start_offset[1]
        ).release().perform()

# 使用示例
driver = webdriver.Chrome()
driver.get("https://example.com")

helper = ActionsHelper(driver)

source = driver.find_element(By.ID, "source")
target = driver.find_element(By.ID, "target")

helper.drag_to_element(source, target)

driver.quit()

最佳实践 #

操作稳定性 #

python
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

def stable_actions():
    driver = webdriver.Chrome()
    wait = WebDriverWait(driver, 10)
    
    try:
        driver.get("https://example.com")
        
        # 等待元素可交互
        element = wait.until(EC.element_to_be_clickable((By.ID, "button")))
        
        # 滚动到元素可见
        driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", element)
        
        # 添加暂停确保稳定
        actions = ActionChains(driver)
        actions.move_to_element(element).pause(0.5).click().perform()
        
    finally:
        driver.quit()

跨平台兼容 #

python
import platform
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains

def get_modifier_key():
    """根据操作系统返回修饰键"""
    return Keys.COMMAND if platform.system() == 'Darwin' else Keys.CONTROL

def cross_platform_shortcut(driver, action):
    """跨平台快捷键"""
    modifier = get_modifier_key()
    actions = ActionChains(driver)
    
    shortcuts = {
        'select_all': lambda: actions.key_down(modifier).send_keys('a').key_up(modifier).perform(),
        'copy': lambda: actions.key_down(modifier).send_keys('c').key_up(modifier).perform(),
        'paste': lambda: actions.key_down(modifier).send_keys('v').key_up(modifier).perform(),
        'cut': lambda: actions.key_down(modifier).send_keys('x').key_up(modifier).perform(),
    }
    
    if action in shortcuts:
        shortcuts[action]()

下一步 #

掌握了 Actions 链操作后,接下来学习 Pytest 集成 了解如何构建完整的测试框架!

最后更新:2026-03-28