第一章:Selenium基础与环境搭建
1.1 Selenium WebDriver架构原理与组件
Selenium是一个用于Web应用程序测试的自动化工具,其核心架构基于WebDriver协议。理解其架构原理对于编写高质量的自动化测试脚本至关重要。
浏览器层
驱动层
通信层
客户端库层
测试脚本层
Python/Java/JavaScript测试代码
Selenium Client Library
JSON Wire Protocol / W3C WebDriver Protocol
ChromeDriver
GeckoDriver
EdgeDriver
IEDriver
Chrome
Firefox
Edge
IE
核心组件说明:
| 组件 | 作用 | 说明 |
|---|---|---|
| Selenium Client Library | 提供编程语言绑定 | 支持Python、Java、JavaScript、C#等 |
| JSON Wire Protocol | 通信协议 | 将命令序列化为JSON格式传输 |
| WebDriver Driver | 浏览器驱动 | 负责与特定浏览器通信 |
| Browser | 目标浏览器 | 执行实际的页面操作 |
W3C WebDriver协议演进:
Selenium 4完全采用W3C WebDriver标准协议,取代了之前的JSON Wire Protocol。这一变化带来了以下优势:
- 标准化的API接口
- 更好的跨浏览器兼容性
- 更高效的通信机制
- 减少协议转换开销
1.2 Selenium 3与Selenium 4核心差异
Selenium 4
Selenium 3
升级
重构
简化
JSON Wire Protocol
find_element_by_* 方法
独立Grid服务器
W3C WebDriver Protocol
find_element方法统一
内置Grid
相对定位器
BiDi API支持
CDP支持
主要差异对比表:
| 特性 | Selenium 3 | Selenium 4 |
|---|---|---|
| 通信协议 | JSON Wire Protocol | W3C WebDriver Protocol |
| 元素定位方法 | find_element_by_id() 等 | find_element(By.ID, "value") |
| Grid部署 | 需要独立JAR包 | 内置Grid,单命令启动 |
| 相对定位器 | 不支持 | 支持 |
| 双向API | 不支持 | 支持BiDi API |
| Chrome DevTools | 不支持 | 支持CDP集成 |
| 录屏功能 | 不支持 | 支持 |
1.3 环境配置与驱动管理(WebDriverManager)
传统驱动管理方式的问题:
- 手动下载驱动程序繁琐
- 浏览器版本更新后驱动不匹配
- 不同操作系统需要不同驱动
- 团队协作时环境不一致
WebDriverManager解决方案:
python
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from webdriver_manager.firefox import GeckoDriverManager
from webdriver_manager.microsoft import EdgeChromiumDriverManager
# 自动下载并配置Chrome驱动
# WebDriverManager会自动检测浏览器版本并下载匹配的驱动
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
# Firefox浏览器配置
driver = webdriver.Firefox(service=Service(GeckoDriverManager().install()))
# Edge浏览器配置
driver = webdriver.Edge(service=Service(EdgeChromiumDriverManager().install()))
driver.quit()
WebDriverManager核心功能:
python
from webdriver_manager.chrome import ChromeDriverManager
from webdriver_manager.core.os_manager import ChromeType
# 指定驱动版本
driver = webdriver.Chrome(
service=Service(ChromeDriverManager(driver_version="114.0.5735.90").install())
)
# 配置缓存路径
driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install())
)
# 离线模式(使用已缓存的驱动)
driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install())
)
1.4 多浏览器支持与兼容性处理
python
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.firefox.service import Service as FirefoxService
from selenium.webdriver.edge.service import Service as EdgeService
from selenium.webdriver.safari.service import Service as SafariService
from webdriver_manager.chrome import ChromeDriverManager
from webdriver_manager.firefox import GeckoDriverManager
from webdriver_manager.microsoft import EdgeChromiumDriverManager
class BrowserFactory:
"""
浏览器工厂类
用于创建不同类型的浏览器实例
支持Chrome、Firefox、Edge、Safari四种主流浏览器
"""
@staticmethod
def create_browser(browser_type: str, headless: bool = False):
"""
根据浏览器类型创建对应的WebDriver实例
参数:
browser_type: 浏览器类型,支持 'chrome', 'firefox', 'edge', 'safari'
headless: 是否启用无头模式,默认为False
返回:
对应浏览器的WebDriver实例
异常:
ValueError: 当传入不支持的浏览器类型时抛出
"""
browser_type = browser_type.lower()
if browser_type == "chrome":
options = webdriver.ChromeOptions()
if headless:
options.add_argument("--headless=new")
return webdriver.Chrome(
service=ChromeService(ChromeDriverManager().install()),
options=options
)
elif browser_type == "firefox":
options = webdriver.FirefoxOptions()
if headless:
options.add_argument("--headless")
return webdriver.Firefox(
service=FirefoxService(GeckoDriverManager().install()),
options=options
)
elif browser_type == "edge":
options = webdriver.EdgeOptions()
if headless:
options.add_argument("--headless=new")
return webdriver.Edge(
service=EdgeService(EdgeChromiumDriverManager().install()),
options=options
)
elif browser_type == "safari":
# Safari不需要额外下载驱动,macOS系统自带
return webdriver.Safari(service=SafariService())
else:
raise ValueError(f"不支持的浏览器类型: {browser_type}")
# 使用示例
driver = BrowserFactory.create_browser("chrome", headless=True)
driver.get("https://www.example.com")
print(f"页面标题: {driver.title}")
driver.quit()
1.5 WebDriver初始化与Options核心参数详解
Chrome Options核心参数分类:
启动参数类型
Chrome Options参数分类
启动参数 add_argument
偏好设置 set_preference
实验选项 set_capability
扩展程序 add_extension
窗口控制
性能优化
隐私安全
调试选项
Options完整配置示例:
python
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
def create_optimized_chrome_driver():
"""
创建优化配置的Chrome浏览器实例
包含常用的启动参数、偏好设置和性能优化选项
"""
options = Options()
# ========== 窗口控制参数 ==========
# 无头模式:不显示浏览器窗口,适用于CI/CD环境
# Selenium 4推荐使用 --headless=new 替代旧的 --headless
options.add_argument("--headless=new")
# 最大化窗口启动
options.add_argument("--start-maximized")
# 设置窗口大小和位置
options.add_argument("--window-size=1920,1080")
options.add_argument("--window-position=0,0")
# ========== 性能优化参数 ==========
# 禁用GPU加速(无头模式下推荐)
options.add_argument("--disable-gpu")
# 禁用图片加载(提升页面加载速度)
# options.add_argument("--blink-settings=imagesEnabled=false")
# 禁用JavaScript(特殊测试场景)
# options.add_argument("--disable-javascript")
# 禁用扩展程序
options.add_argument("--disable-extensions")
# 禁用开发者模式扩展
options.add_argument("--disable-dev-shm-usage")
# 禁用沙箱模式(Docker环境必需)
options.add_argument("--no-sandbox")
# ========== 隐私安全参数 ==========
# 禁用密码保存提示
options.add_argument("--disable-save-password-bubble")
# 禁用自动填充
options.add_argument("--disable-autofill")
# 禁用通知弹窗
options.add_argument("--disable-notifications")
# 隐身模式
options.add_argument("--incognito")
# ========== 调试选项 ==========
# 禁用自动化检测标志
options.add_argument("--disable-blink-features=AutomationControlled")
# 设置User-Agent
options.add_argument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
# 忽略证书错误
options.add_argument("--ignore-certificate-errors")
# 允许运行不安全内容
options.add_argument("--allow-running-insecure-content")
# ========== 偏好设置 ==========
# 设置下载目录
prefs = {
"download.default_directory": r"C:\Downloads",
"download.prompt_for_download": False,
"download.directory_upgrade": True,
"safebrowsing.enabled": True,
"profile.default_content_setting_values.notifications": 2,
}
options.add_experimental_option("prefs", prefs)
# ========== 排除开关 ==========
# 排除"启用自动化"开关
options.add_experimental_option("excludeSwitches", ["enable-automation"])
# 设置开发者模式
options.add_experimental_option("useAutomationExtension", False)
return webdriver.Chrome(options=options)
driver = create_optimized_chrome_driver()
driver.get("https://www.example.com")
driver.quit()
1.6 Chrome/Edge常用启动参数详解
启动参数完整列表:
| 参数 | 作用 | 取值/示例 | 使用场景 |
|---|---|---|---|
--headless=new |
无头模式 | 无参数 | CI/CD环境、后台运行 |
--start-maximized |
最大化启动 | 无参数 | 需要全屏显示的测试 |
--window-size=width,height |
设置窗口大小 | 1920,1080 | 固定窗口尺寸测试 |
--no-sandbox |
禁用沙箱 | 无参数 | Docker容器环境 |
--disable-gpu |
禁用GPU | 无参数 | 无头模式、服务器环境 |
--disable-dev-shm-usage |
避免共享内存问题 | 无参数 | Docker环境 |
--incognito |
隐身模式 | 无参数 | 隐私测试、无痕浏览 |
--disable-extensions |
禁用扩展 | 无参数 | 提升性能、纯净环境 |
--disable-notifications |
禁用通知 | 无参数 | 避免弹窗干扰 |
--ignore-certificate-errors |
忽略证书错误 | 无参数 | 测试环境HTTPS |
--user-agent=xxx |
设置UA | 自定义字符串 | 模拟不同设备 |
--proxy-server=host:port |
设置代理 | 127.0.0.1:8080 | 网络代理测试 |
--lang=zh-CN |
设置语言 | zh-CN/en-US | 多语言测试 |
--disable-blink-features=AutomationControlled |
隐藏自动化特征 | 无参数 | 反爬虫测试 |
1.7 无头模式配置与应用场景
python
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
class HeadlessBrowser:
"""
无头浏览器配置类
适用于CI/CD环境和服务器端自动化测试
"""
@staticmethod
def create_headless_chrome():
"""
创建无头模式Chrome浏览器
无头模式特点:
1. 不显示浏览器窗口,节省资源
2. 适合服务器环境和CI/CD流水线
3. 执行速度更快
4. 无法进行可视化调试
"""
options = Options()
# Selenium 4新的无头模式参数
# --headless=new 比 --headless 更稳定,支持更多功能
options.add_argument("--headless=new")
# 无头模式必需参数
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--disable-gpu")
# 设置窗口大小(无头模式下仍需指定)
options.add_argument("--window-size=1920,1080")
return webdriver.Chrome(options=options)
@staticmethod
def create_headless_firefox():
"""
创建无头模式Firefox浏览器
"""
options = webdriver.FirefoxOptions()
options.add_argument("--headless")
return webdriver.Firefox(options=options)
# 无头模式应用场景示例
def run_ci_test():
"""
CI环境下的测试示例
"""
driver = HeadlessBrowser.create_headless_chrome()
try:
driver.get("https://www.example.com")
# 验证页面标题
assert "Example" in driver.title, "页面标题验证失败"
# 截图保存(无头模式仍可截图)
driver.save_screenshot("ci_screenshot.png")
print("CI测试执行成功")
finally:
driver.quit()
run_ci_test()
1.8 第一个自动化脚本示例
python
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
def first_automation_script():
"""
第一个完整的Selenium自动化脚本示例
演示内容:
1. 浏览器初始化与配置
2. 页面导航
3. 元素定位与交互
4. 等待机制
5. 断言验证
6. 资源清理
"""
# 步骤1:配置浏览器选项
options = Options()
options.add_argument("--start-maximized")
options.add_argument("--disable-blink-features=AutomationControlled")
# 步骤2:初始化WebDriver
# 使用WebDriverManager自动管理驱动
driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install()),
options=options
)
# 设置隐式等待(全局等待时间)
driver.implicitly_wait(10)
try:
# 步骤3:导航到目标页面
driver.get("https://www.baidu.com")
# 步骤4:验证页面加载成功
assert "百度" in driver.title, "页面标题不包含'百度'"
print(f"页面标题: {driver.title}")
# 步骤5:定位搜索框并输入内容
# 使用显式等待确保元素可交互
search_box = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "kw"))
)
search_box.send_keys("Selenium自动化测试")
# 步骤6:点击搜索按钮
search_button = driver.find_element(By.ID, "su")
search_button.click()
# 步骤7:等待搜索结果加载
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "#content_left"))
)
# 步骤8:获取搜索结果
results = driver.find_elements(By.CSS_SELECTOR, ".result.c-container")
print(f"搜索结果数量: {len(results)}")
# 步骤9:截图保存
driver.save_screenshot("search_result.png")
# 步骤10:打印当前URL
print(f"当前URL: {driver.current_url}")
print("自动化脚本执行成功!")
except Exception as e:
print(f"脚本执行出错: {e}")
driver.save_screenshot("error_screenshot.png")
finally:
# 步骤11:关闭浏览器,释放资源
driver.quit()
# 执行脚本
if __name__ == "__main__":
first_automation_script()
第二章:元素定位策略详解
2.1 By类完整枚举详解
Selenium提供了By类来支持多种元素定位策略。理解每种定位方式的特点和适用场景,是编写稳定自动化脚本的基础。
python
from selenium.webdriver.common.by import By
# By类支持的定位策略枚举
class By:
ID = "id" # 通过ID属性定位
NAME = "name" # 通过name属性定位
XPATH = "xpath" # 通过XPath表达式定位
TAG_NAME = "tag name" # 通过标签名定位
CLASS_NAME = "class name" # 通过class属性定位
CSS_SELECTOR = "css selector" # 通过CSS选择器定位
LINK_TEXT = "link text" # 通过完整链接文本定位
PARTIAL_LINK_TEXT = "partial link text" # 通过部分链接文本定位
定位策略对比:
定位策略选择决策树
是
否
是
否
是
否
是
否
需要定位元素
元素是否有唯一ID?
使用By.ID
元素是否有唯一name?
使用By.NAME
需要精确定位?
使用XPath
使用CSS选择器
是链接元素吗?
使用LINK_TEXT
使用其他策略
2.2 八种基本定位方法及参数详解
python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get("https://www.example.com")
# ========== 1. ID定位 ==========
# 最快速、最可靠的定位方式
# 前提:元素必须有唯一的id属性
# 参数:id属性的值(字符串)
element_by_id = driver.find_element(By.ID, "username")
print("ID定位成功")
# ========== 2. NAME定位 ==========
# 适用于表单元素
# 参数:name属性的值(字符串)
# 注意:name可能不唯一,find_element返回第一个匹配项
element_by_name = driver.find_element(By.NAME, "password")
print("NAME定位成功")
# ========== 3. CLASS_NAME定位 ==========
# 通过class属性定位
# 参数:class属性的值(字符串)
# 注意:只能传单个class名,多个class用CSS选择器
element_by_class = driver.find_element(By.CLASS_NAME, "btn-primary")
print("CLASS_NAME定位成功")
# ========== 4. TAG_NAME定位 ==========
# 通过HTML标签名定位
# 参数:标签名(字符串)
# 注意:通常返回多个元素,适合批量操作
elements_by_tag = driver.find_elements(By.TAG_NAME, "input")
print(f"找到 {len(elements_by_tag)} 个input元素")
# ========== 5. LINK_TEXT定位 ==========
# 通过完整的链接文本定位
# 参数:链接的完整文本内容(字符串)
# 仅适用于<a>标签
element_by_link = driver.find_element(By.LINK_TEXT, "更多信息")
print("LINK_TEXT定位成功")
# ========== 6. PARTIAL_LINK_TEXT定位 ==========
# 通过部分链接文本定位
# 参数:链接文本的部分内容(字符串)
# 适用于链接文本较长或动态变化的情况
element_by_partial = driver.find_element(By.PARTIAL_LINK_TEXT, "更多")
print("PARTIAL_LINK_TEXT定位成功")
# ========== 7. CSS_SELECTOR定位 ==========
# 通过CSS选择器语法定位
# 参数:CSS选择器表达式(字符串)
# 功能强大,推荐使用
element_by_css = driver.find_element(By.CSS_SELECTOR, "#main .content > p")
print("CSS_SELECTOR定位成功")
# ========== 8. XPATH定位 ==========
# 通过XPath表达式定位
# 参数:XPath表达式(字符串)
# 最灵活的定位方式,支持复杂查询
element_by_xpath = driver.find_element(By.XPATH, "//div[@class='container']//input[@type='text']")
print("XPATH定位成功")
driver.quit()
2.3 相对定位器(Relative Locators)- Selenium 4新特性
Selenium 4引入了相对定位器,允许基于元素之间的空间关系进行定位,这是对传统定位方式的重要补充。
相对定位器示意图
above
below
toLeftOf
toRightOf
near
目标元素
above: 上方元素
below: 下方元素
toLeftOf: 左侧元素
toRightOf: 右侧元素
near: 附近元素
相对定位器完整示例:
python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.relative_locator import locate_with
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get("https://www.example.com/form")
# ========== 相对定位器使用示例 ==========
# 1. above - 定位某元素上方的元素
# 场景:已知密码输入框,定位其上方的用户名输入框
password_field = driver.find_element(By.ID, "password")
username_field = driver.find_element(
locate_with(By.TAG_NAME, "input").above(password_field)
)
# 2. below - 定位某元素下方的元素
# 场景:已知用户名输入框,定位其下方的密码输入框
username = driver.find_element(By.ID, "username")
password = driver.find_element(
locate_with(By.TAG_NAME, "input").below(username)
)
# 3. toLeftOf - 定位某元素左侧的元素
# 场景:已知登录按钮,定位其左侧的取消按钮
login_button = driver.find_element(By.ID, "login")
cancel_button = driver.find_element(
locate_with(By.TAG_NAME, "button").to_left_of(login_button)
)
# 4. toRightOf - 定位某元素右侧的元素
# 场景:已知取消按钮,定位其右侧的登录按钮
cancel_btn = driver.find_element(By.ID, "cancel")
login_btn = driver.find_element(
locate_with(By.TAG_NAME, "button").to_right_of(cancel_btn)
)
# 5. near - 定位某元素附近的元素(约50px范围内)
# 场景:定位标签附近的输入框
label = driver.find_element(By.XPATH, "//label[contains(text(), '邮箱')]")
email_input = driver.find_element(
locate_with(By.TAG_NAME, "input").near(label)
)
# ========== 组合使用相对定位器 ==========
# 可以链式调用多个相对定位器
# 场景:定位密码输入框下方、登录按钮左侧的"忘记密码"链接
password_input = driver.find_element(By.ID, "password")
login_btn = driver.find_element(By.ID, "login")
forgot_link = driver.find_element(
locate_with(By.TAG_NAME, "a")
.below(password_input)
.to_left_of(login_btn)
)
print("相对定位器使用成功")
driver.quit()
2.4 CSS选择器定位语法与应用
CSS选择器是Web开发中广泛使用的元素选择方式,在Selenium中同样高效可靠。
python
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://www.example.com")
# ========== 基础选择器 ==========
# 元素选择器
divs = driver.find_elements(By.CSS_SELECTOR, "div")
# ID选择器(#id)
element = driver.find_element(By.CSS_SELECTOR, "#username")
# 类选择器(.class)
elements = driver.find_elements(By.CSS_SELECTOR, ".btn-primary")
# ========== 组合选择器 ==========
# 后代选择器(空格)- 选择所有后代元素
all_links = driver.find_elements(By.CSS_SELECTOR, "div a")
# 子选择器(>)- 只选择直接子元素
direct_children = driver.find_elements(By.CSS_SELECTOR, "ul > li")
# 相邻兄弟选择器(+)- 选择紧邻的下一个兄弟元素
next_sibling = driver.find_element(By.CSS_SELECTOR, "h1 + p")
# 通用兄弟选择器(~)- 选择后面所有兄弟元素
all_siblings = driver.find_elements(By.CSS_SELECTOR, "h1 ~ p")
# ========== 属性选择器 ==========
# 存在属性
inputs_with_type = driver.find_elements(By.CSS_SELECTOR, "input[type]")
# 属性等于特定值
text_inputs = driver.find_elements(By.CSS_SELECTOR, "input[type='text']")
# 属性包含特定值
inputs_with_btn = driver.find_elements(By.CSS_SELECTOR, "input[class*='btn']")
# 属性以特定值开头
inputs_start_with = driver.find_elements(By.CSS_SELECTOR, "input[id^='user']")
# 属性以特定值结尾
inputs_end_with = driver.find_elements(By.CSS_SELECTOR, "input[id$='name']")
# ========== 伪类选择器 ==========
# 第一个子元素
first_item = driver.find_element(By.CSS_SELECTOR, "li:first-child")
# 最后一个子元素
last_item = driver.find_element(By.CSS_SELECTOR, "li:last-child")
# 第n个子元素
nth_item = driver.find_element(By.CSS_SELECTOR, "li:nth-child(2)")
# 偶数位置的元素
even_items = driver.find_elements(By.CSS_SELECTOR, "tr:nth-child(even)")
# 奇数位置的元素
odd_items = driver.find_elements(By.CSS_SELECTOR, "tr:nth-child(odd)")
# ========== 表单相关选择器 ==========
# 启用状态的元素
enabled_inputs = driver.find_elements(By.CSS_SELECTOR, "input:enabled")
# 禁用状态的元素
disabled_inputs = driver.find_elements(By.CSS_SELECTOR, "input:disabled")
# 选中状态的复选框
checked_boxes = driver.find_elements(By.CSS_SELECTOR, "input:checked")
# 获得焦点的元素
focused = driver.find_element(By.CSS_SELECTOR, "input:focus")
driver.quit()
2.5 XPath定位语法与最佳实践
XPath是最强大的定位方式,支持复杂的元素查询和条件筛选。
python
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://www.example.com")
# ========== XPath基础语法 ==========
# 绝对路径(从根节点开始)
# 不推荐使用,页面结构变化会导致定位失败
element = driver.find_element(By.XPATH, "/html/body/div/form/input")
# 相对路径(推荐)
# 从任意位置开始查找
element = driver.find_element(By.XPATH, "//input")
# ========== 节点选择 ==========
# 选择所有div元素
divs = driver.find_elements(By.XPATH, "//div")
# 选择当前节点的所有子元素
children = driver.find_elements(By.XPATH, "//div/*")
# 选择当前节点的所有后代元素
descendants = driver.find_elements(By.XPATH, "//div//*")
# ========== 属性定位 ==========
# 通过属性定位
element = driver.find_element(By.XPATH, "//input[@id='username']")
# 多属性组合定位
element = driver.find_element(
By.XPATH, "//input[@type='text' and @name='username']"
)
# 属性包含特定值
element = driver.find_element(
By.XPATH, "//div[contains(@class, 'container')]"
)
# 属性以特定值开头
element = driver.find_element(
By.XPATH, "//input[starts-with(@id, 'user')]"
)
# ========== 文本定位 ==========
# 精确文本匹配
element = driver.find_element(By.XPATH, "//a[text()='登录']")
# 包含文本
element = driver.find_element(
By.XPATH, "//button[contains(text(), '提交')]"
)
# 文本以特定值开头
element = driver.find_element(
By.XPATH, "//label[starts-with(text(), '用户')]"
)
# ========== 轴定位 ==========
# parent轴 - 选择父节点
parent = driver.find_element(By.XPATH, "//input[@id='username']/parent::div")
# ancestor轴 - 选择所有祖先节点
ancestors = driver.find_elements(
By.XPATH, "//input[@id='username']/ancestor::*"
)
# child轴 - 选择所有子节点
children = driver.find_elements(By.XPATH, "//div[@id='container']/child::*")
# following-sibling轴 - 选择后面的兄弟节点
next_siblings = driver.find_elements(
By.XPATH, "//input[@id='username']/following-sibling::*"
)
# preceding-sibling轴 - 选择前面的兄弟节点
prev_siblings = driver.find_elements(
By.XPATH, "//input[@id='password']/preceding-sibling::*"
)
# ========== 位置定位 ==========
# 选择第一个匹配的元素
first_div = driver.find_element(By.XPATH, "//div[1]")
# 选择最后一个匹配的元素
last_div = driver.find_element(By.XPATH, "//div[last()]")
# 选择倒数第二个元素
second_last = driver.find_element(By.XPATH, "//div[last()-1]")
# 选择位置小于3的元素
first_two = driver.find_elements(By.XPATH, "//div[position()<3]")
# ========== 复杂条件组合 ==========
# 使用and、or、not
element = driver.find_element(
By.XPATH, "//input[@type='text' and not(@disabled)]"
)
# 嵌套条件
element = driver.find_element(
By.XPATH, "//div[@class='form-group']//input[@type='text']"
)
# 使用last()和position()
element = driver.find_element(
By.XPATH, "//ul/li[last()-1]"
)
driver.quit()
2.6 Shadow DOM元素定位方法
Shadow DOM是Web组件技术的一部分,用于封装组件内部的DOM结构。Selenium 4提供了对Shadow DOM的支持。
python
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
driver = webdriver.Chrome()
driver.get("https://www.example.com/shadow-dom-page")
# ========== Shadow DOM定位方法 ==========
# 方法1:使用JavaScript获取Shadow Root
# Shadow DOM元素无法直接通过常规方式定位
shadow_host = driver.find_element(By.CSS_SELECTOR, "#shadow-host")
# 获取Shadow Root
shadow_root = driver.execute_script(
"return arguments[0].shadowRoot", shadow_host
)
# 通过Shadow Root查找内部元素
inner_element = shadow_root.find_element(By.CSS_SELECTOR, "#inner-element")
# 方法2:使用CSS选择器穿透Shadow DOM(Selenium 4)
# 使用 >>> 或 /deep/ 选择器(部分浏览器支持)
# 注意:这种方式在某些浏览器中可能不被支持
try:
element = driver.find_element(
By.CSS_SELECTOR, "#shadow-host >>> #inner-element"
)
except:
print("当前浏览器不支持CSS穿透Shadow DOM")
# 方法3:封装Shadow DOM定位工具函数
def find_element_in_shadow_dom(driver, shadow_host_selector, element_selector):
"""
在Shadow DOM中查找元素的通用方法
参数:
driver: WebDriver实例
shadow_host_selector: Shadow DOM宿主元素选择器
element_selector: Shadow DOM内部元素选择器
返回:
找到的WebElement对象
"""
# 定位Shadow Host
shadow_host = driver.find_element(By.CSS_SELECTOR, shadow_host_selector)
# 获取Shadow Root
shadow_root = driver.execute_script(
"return arguments[0].shadowRoot", shadow_host
)
# 在Shadow Root中查找元素
return shadow_root.find_element(By.CSS_SELECTOR, element_selector)
# 使用封装函数
element = find_element_in_shadow_dom(
driver,
"#shadow-host",
"#inner-element"
)
# 方法4:处理多层嵌套Shadow DOM
def find_element_in_nested_shadow_dom(driver, selectors_chain):
"""
在多层嵌套Shadow DOM中查找元素
参数:
driver: WebDriver实例
selectors_chain: 选择器链列表,格式为:
[(host_selector1, element_selector1), (host_selector2, element_selector2), ...]
返回:
找到的WebElement对象
"""
current_root = driver
for host_selector, element_selector in selectors_chain[:-1]:
shadow_host = current_root.find_element(By.CSS_SELECTOR, host_selector)
current_root = driver.execute_script(
"return arguments[0].shadowRoot", shadow_host
)
# 最后一个选择器查找目标元素
last_host, last_element = selectors_chain[-1]
shadow_host = current_root.find_element(By.CSS_SELECTOR, last_host)
shadow_root = driver.execute_script(
"return arguments[0].shadowRoot", shadow_host
)
return shadow_root.find_element(By.CSS_SELECTOR, last_element)
driver.quit()
2.7 动态元素与模糊定位技巧
动态元素是指属性值会动态变化的元素,如ID包含随机数字、class动态生成等。
python
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://www.example.com/dynamic-page")
# ========== 动态ID处理 ==========
# 问题:ID包含动态生成的数字,如 id="user_12345", id="user_67890"
# 方法1:使用XPath的contains函数
element = driver.find_element(
By.XPATH, "//input[contains(@id, 'user_')]"
)
# 方法2:使用XPath的starts-with函数
element = driver.find_element(
By.XPATH, "//input[starts-with(@id, 'user_')]"
)
# 方法3:使用CSS属性选择器
element = driver.find_element(
By.CSS_SELECTOR, "input[id^='user_']" # 以'user_'开头
)
element = driver.find_element(
By.CSS_SELECTOR, "input[id*='user']" # 包含'user'
)
# ========== 动态Class处理 ==========
# 问题:class包含动态部分,如 class="btn-primary-abc123"
# 方法1:使用contains匹配固定部分
element = driver.find_element(
By.XPATH, "//button[contains(@class, 'btn-primary')]"
)
# 方法2:使用CSS部分匹配
element = driver.find_element(
By.CSS_SELECTOR, "button[class*='btn-primary']"
)
# ========== 动态属性处理 ==========
# 问题:属性值动态变化,如 data-id="item_abc123"
# 使用多个属性组合定位,减少对动态属性的依赖
element = driver.find_element(
By.XPATH, "//div[@role='button' and contains(@data-id, 'item')]"
)
# ========== 相对定位处理动态元素 ==========
# 利用稳定的相邻元素定位动态元素
# 例如:标签文本稳定,输入框ID动态
element = driver.find_element(
By.XPATH, "//label[text()='用户名']/following-sibling::input"
)
# ========== 父子关系定位 ==========
# 通过稳定的父元素定位动态子元素
element = driver.find_element(
By.XPATH, "//div[@id='stable-container']//input[contains(@class, 'dynamic')]"
)
# ========== 文本内容定位 ==========
# 当属性都不可靠时,使用文本内容定位
element = driver.find_element(
By.XPATH, "//button[contains(text(), '提交')]"
)
# ========== 索引定位 ==========
# 当元素位置相对稳定时,使用索引
element = driver.find_element(
By.XPATH, "(//input[@type='text'])[1]" # 第一个文本输入框
)
driver.quit()
2.8 定位策略选择与性能对比
定位策略性能排序
ID定位
最快
CSS选择器
快
NAME定位
较快
XPath
较慢
复杂XPath
最慢
性能对比与选择建议:
python
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
def compare_locator_performance():
"""
定位策略性能对比测试
"""
driver = webdriver.Chrome()
driver.get("https://www.example.com")
test_results = {}
# 测试ID定位
start = time.time()
for _ in range(100):
try:
driver.find_element(By.ID, "username")
except:
pass
test_results["ID"] = time.time() - start
# 测试CSS选择器定位
start = time.time()
for _ in range(100):
try:
driver.find_element(By.CSS_SELECTOR, "#username")
except:
pass
test_results["CSS"] = time.time() - start
# 测试XPath定位
start = time.time()
for _ in range(100):
try:
driver.find_element(By.XPATH, "//input[@id='username']")
except:
pass
test_results["XPath"] = time.time() - start
driver.quit()
return test_results
# 定位策略选择建议表
"""
+------------------+------------+------------------+------------------------+
| 定位策略 | 性能 | 可靠性 | 适用场景 |
+------------------+------------+------------------+------------------------+
| By.ID | 最快 | 高 | 元素有唯一ID |
| By.CSS_SELECTOR | 快 | 高 | 复杂选择、推荐使用 |
| By.NAME | 较快 | 中 | 表单元素 |
| By.CLASS_NAME | 较快 | 中 | 单一class名 |
| By.XPATH | 较慢 | 高 | 复杂条件、文本定位 |
| By.LINK_TEXT | 中 | 中 | 链接元素 |
| By.TAG_NAME | 快 | 低 | 批量操作 |
| 相对定位器 | 中 | 高 | 元素空间关系明确 |
+------------------+------------+------------------+------------------------+
"""
第三章:浏览器操作与窗口管理
3.1 浏览器启动参数完整配置
python
from selenium import webdriver
from selenium.webdriver.chrome.options import Options as ChromeOptions
from selenium.webdriver.firefox.options import Options as FirefoxOptions
from selenium.webdriver.edge.options import Options as EdgeOptions
class BrowserConfig:
"""
浏览器配置管理类
提供完整的浏览器启动参数配置
"""
@staticmethod
def get_chrome_options(
headless: bool = False,
disable_images: bool = False,
proxy: str = None,
user_agent: str = None,
download_dir: str = None
) -> ChromeOptions:
"""
获取Chrome浏览器配置
参数:
headless: 是否启用无头模式
disable_images: 是否禁用图片加载
proxy: 代理服务器地址,格式: "host:port"
user_agent: 自定义User-Agent
download_dir: 下载目录路径
返回:
配置好的ChromeOptions对象
"""
options = ChromeOptions()
# 基础配置
if headless:
options.add_argument("--headless=new")
# 性能优化配置
options.add_argument("--disable-gpu")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--disable-extensions")
# 禁用图片加载(提升性能)
if disable_images:
prefs = {"profile.managed_default_content_settings.images": 2}
options.add_experimental_option("prefs", prefs)
# 代理配置
if proxy:
options.add_argument(f"--proxy-server={proxy}")
# User-Agent配置
if user_agent:
options.add_argument(f"--user-agent={user_agent}")
# 下载目录配置
if download_dir:
prefs = {
"download.default_directory": download_dir,
"download.prompt_for_download": False,
"download.directory_upgrade": True,
"safebrowsing.enabled": True
}
options.add_experimental_option("prefs", prefs)
# 隐藏自动化特征
options.add_argument("--disable-blink-features=AutomationControlled")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
return options
@staticmethod
def get_firefox_options(
headless: bool = False,
proxy: str = None,
download_dir: str = None
) -> FirefoxOptions:
"""
获取Firefox浏览器配置
"""
options = FirefoxOptions()
if headless:
options.add_argument("--headless")
if proxy:
options.set_preference("network.proxy.type", 1)
host, port = proxy.split(":")
options.set_preference("network.proxy.http", host)
options.set_preference("network.proxy.http_port", int(port))
if download_dir:
options.set_preference("browser.download.folderList", 2)
options.set_preference("browser.download.dir", download_dir)
options.set_preference("browser.download.manager.showWhenStarting", False)
return options
# 使用示例
options = BrowserConfig.get_chrome_options(
headless=True,
proxy="127.0.0.1:8080",
download_dir=r"C:\Downloads"
)
driver = webdriver.Chrome(options=options)
3.2 Navigation接口详解(导航操作)
python
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
# ========== 页面导航操作 ==========
# 访问URL
driver.get("https://www.baidu.com")
print(f"当前页面: {driver.title}")
# 获取当前URL
current_url = driver.current_url
print(f"当前URL: {current_url}")
# ========== Navigation接口方法 ==========
# back() - 后退到上一页
driver.get("https://www.example.com")
driver.back()
print(f"后退后页面: {driver.title}")
# forward() - 前进到下一页
driver.forward()
print(f"前进后页面: {driver.title}")
# refresh() - 刷新当前页面
driver.refresh()
print("页面已刷新")
# 注意:Python中没有navigate.to()方法,Java中可使用driver.navigate().to()
# Python中导航到URL统一使用driver.get()方法
# ========== 页面源码获取 ==========
page_source = driver.page_source
print(f"页面源码长度: {len(page_source)}")
# ========== 页面加载状态检查 ==========
# 使用document.readyState检查页面加载状态
ready_state = driver.execute_script("return document.readyState")
print(f"页面状态: {ready_state}") # complete, interactive, loading
driver.quit()
3.3 页面加载策略参数
Selenium支持三种页面加载策略,可根据测试需求选择合适的策略以优化执行效率。
页面加载策略
速度
速度
可靠性
可靠性
normal
等待完全加载
eager
等待DOM就绪
none
不等待
python
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
# ========== 页面加载策略详解 ==========
# 策略1: normal(默认)
# 等待页面完全加载(包括所有资源:图片、CSS、JavaScript)
# 适用于:需要确保页面完全加载的测试场景
options_normal = Options()
options_normal.page_load_strategy = "normal"
driver_normal = webdriver.Chrome(options=options_normal)
# 策略2: eager
# 等待DOMContentLoaded事件触发
# 不等待图片、CSS等资源加载完成
# 适用于:需要快速执行、不依赖图片资源的测试
options_eager = Options()
options_eager.page_load_strategy = "eager"
driver_eager = webdriver.Chrome(options=options_eager)
# 策略3: none
# 不等待任何页面加载事件
# 立即返回控制权
# 适用于:需要完全自定义等待逻辑的高级场景
options_none = Options()
options_none.page_load_strategy = "none"
driver_none = webdriver.Chrome(options=options_none)
# ========== 页面加载超时设置 ==========
# 设置页面加载超时时间(秒)
driver = webdriver.Chrome()
driver.set_page_load_timeout(30) # 30秒超时
try:
driver.get("https://www.example.com")
except Exception as e:
print(f"页面加载超时: {e}")
# 超时后可继续操作已加载的内容
driver.quit()
页面加载策略对比表:
| 策略 | 触发条件 | 等待资源 | 速度 | 可靠性 | 适用场景 |
|---|---|---|---|---|---|
| normal | load事件 | 全部资源 | 慢 | 高 | 完整功能测试 |
| eager | DOMContentLoaded | DOM结构 | 中 | 中 | 快速回归测试 |
| none | 立即返回 | 不等待 | 快 | 低 | 自定义等待逻辑 |
3.4 窗口管理(尺寸、位置、最大化)
python
from selenium import webdriver
driver = webdriver.Chrome()
driver.get("https://www.example.com")
# ========== 窗口尺寸管理 ==========
# 获取当前窗口尺寸
window_size = driver.get_window_size()
print(f"窗口宽度: {window_size['width']}, 高度: {window_size['height']}")
# 设置窗口尺寸
driver.set_window_size(1280, 720)
print("窗口尺寸已设置为 1280x720")
# ========== 窗口位置管理 ==========
# 获取窗口位置
window_position = driver.get_window_position()
print(f"窗口位置 X: {window_position['x']}, Y: {window_position['y']}")
# 设置窗口位置
driver.set_window_position(100, 100)
print("窗口位置已设置为 (100, 100)")
# ========== 窗口最大化/最小化/全屏 ==========
# 最大化窗口
driver.maximize_window()
print("窗口已最大化")
# 最小化窗口(部分浏览器支持)
driver.minimize_window()
print("窗口已最小化")
# 全屏模式(类似F11效果)
driver.fullscreen_window()
print("窗口已全屏")
# ========== 窗口矩形操作(同时设置位置和尺寸)==========
driver.set_window_rect(x=0, y=0, width=1920, height=1080)
print("窗口矩形已设置")
# 获取窗口矩形
rect = driver.get_window_rect()
print(f"窗口矩形: {rect}")
driver.quit()
3.5 多窗口与多标签页管理
python
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://www.example.com")
# ========== 获取窗口句柄 ==========
# 获取当前窗口句柄(当前活动窗口)
main_window = driver.current_window_handle
print(f"主窗口句柄: {main_window}")
# 获取所有窗口句柄
all_windows = driver.window_handles
print(f"所有窗口句柄: {all_windows}")
# ========== 打开新窗口/标签页 ==========
# 方法1:通过JavaScript打开新窗口
driver.execute_script("window.open('https://www.baidu.com');")
# 方法2:点击链接打开新窗口
# driver.find_element(By.LINK_TEXT, "在新窗口打开").click()
# ========== 切换窗口 ==========
# 获取所有窗口句柄(包括新打开的)
all_windows = driver.window_handles
# 切换到新窗口(最后一个打开的窗口)
driver.switch_to.window(all_windows[-1])
print(f"当前窗口标题: {driver.title}")
# 切换回主窗口
driver.switch_to.window(main_window)
print(f"回到主窗口: {driver.title}")
# ========== 关闭窗口 ==========
# 关闭当前窗口
driver.close()
# 切换到其他窗口继续操作
driver.switch_to.window(all_windows[0])
# ========== 多窗口管理工具类 ==========
class WindowManager:
"""
多窗口管理工具类
提供窗口切换、关闭等便捷操作
"""
def __init__(self, driver):
self.driver = driver
self.main_window = driver.current_window_handle
def switch_to_new_window(self):
"""切换到最新打开的窗口"""
all_windows = self.driver.window_handles
self.driver.switch_to.window(all_windows[-1])
def switch_to_main_window(self):
"""切换回主窗口"""
self.driver.switch_to.window(self.main_window)
def close_current_and_switch_to_main(self):
"""关闭当前窗口并切换回主窗口"""
self.driver.close()
self.driver.switch_to.window(self.main_window)
def get_window_count(self):
"""获取当前打开的窗口数量"""
return len(self.driver.window_handles)
def close_all_except_main(self):
"""关闭除主窗口外的所有窗口"""
for handle in self.driver.window_handles:
if handle != self.main_window:
self.driver.switch_to.window(handle)
self.driver.close()
self.driver.switch_to.window(self.main_window)
# 使用示例
window_manager = WindowManager(driver)
driver.execute_script("window.open('https://www.baidu.com');")
window_manager.switch_to_new_window()
print(f"新窗口标题: {driver.title}")
window_manager.close_current_and_switch_to_main()
driver.quit()
3.6 TargetLocator接口详解(switch_to完整用法)
python
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://www.example.com")
# ========== switch_to接口完整用法 ==========
# switch_to是一个TargetLocator对象
# 提供多种切换目标的方法
# ========== 1. 切换窗口 ==========
# 获取所有窗口句柄
handles = driver.window_handles
driver.switch_to.window(handles[-1])
# ========== 2. 切换iframe ==========
# 通过iframe元素切换
iframe_element = driver.find_element(By.CSS_SELECTOR, "iframe")
driver.switch_to.frame(iframe_element)
# 通过iframe的name或id属性切换
driver.switch_to.frame("frame_name")
driver.switch_to.frame("frame_id")
# 通过索引切换(从0开始)
driver.switch_to.frame(0)
# 切换回主文档
driver.switch_to.default_content()
# 切换到父级frame(嵌套iframe场景)
driver.switch_to.parent_frame()
# ========== 3. 切换Alert弹窗 ==========
# 切换到Alert并接受(点击确定)
alert = driver.switch_to.alert
alert.accept()
# 切换到Alert并取消(点击取消)
alert.dismiss()
# 获取Alert文本
alert_text = alert.text
# 在Prompt类型的Alert中输入文本
alert.send_keys("输入内容")
# ========== 4. 切换到活动元素 ==========
# 获取当前获得焦点的元素
active_element = driver.switch_to.active_element
print(f"活动元素标签: {active_element.tag_name}")
# ========== switch_to方法汇总表 ==========
"""
+------------------------+------------------------------------------+
| 方法 | 说明 |
+------------------------+------------------------------------------+
| window(handle) | 切换到指定窗口 |
| frame(reference) | 切换到指定iframe |
| default_content() | 切换回主文档 |
| parent_frame() | 切换到父级frame |
| alert | 切换到Alert弹窗 |
| active_element | 获取当前活动元素 |
+------------------------+------------------------------------------+
"""
driver.quit()
3.7 Session管理:quit()与close()区别
python
from selenium import webdriver
from selenium.webdriver.common.by import By
# ========== close()方法 ==========
driver = webdriver.Chrome()
driver.get("https://www.example.com")
# 打开新窗口
driver.execute_script("window.open('');")
# close()只关闭当前窗口
# 如果有多个窗口,其他窗口仍然保持打开
driver.close()
# 此时需要切换到其他窗口继续操作
if len(driver.window_handles) > 0:
driver.switch_to.window(driver.window_handles[0])
else:
print("所有窗口已关闭")
# ========== quit()方法 ==========
driver = webdriver.Chrome()
driver.get("https://www.example.com")
# 打开多个窗口
driver.execute_script("window.open('');")
driver.execute_script("window.open('');")
# quit()关闭所有窗口并结束浏览器会话
# 释放所有资源,包括:
# 1. 关闭所有窗口和标签页
# 2. 结束浏览器进程
# 3. 释放WebDriver资源
# 4. 清理临时文件
driver.quit()
# ========== quit() vs close() 对比表 ==========
"""
+------------------+--------------------------------+--------------------------------+
| 特性 | close() | quit() |
+------------------+--------------------------------+--------------------------------+
| 作用范围 | 仅关闭当前窗口 | 关闭所有窗口 |
| 浏览器进程 | 保持运行 | 完全终止 |
| WebDriver会话 | 保持活动 | 结束会话 |
| 临时文件 | 不清理 | 清理临时文件 |
| 内存释放 | 部分释放 | 完全释放 |
| 后续操作 | 可继续操作其他窗口 | 无法继续操作 |
| 推荐场景 | 关闭特定窗口 | 测试结束清理 |
+------------------+--------------------------------+--------------------------------+
"""
# ========== 最佳实践:使用上下文管理器 ==========
class WebDriverManager:
"""
WebDriver上下文管理器
确保资源正确释放
"""
def __init__(self, driver_factory):
self.driver_factory = driver_factory
self.driver = None
def __enter__(self):
self.driver = self.driver_factory()
return self.driver
def __exit__(self, exc_type, exc_val, exc_tb):
if self.driver:
self.driver.quit()
return False
# 使用示例
def create_driver():
return webdriver.Chrome()
with WebDriverManager(create_driver) as driver:
driver.get("https://www.example.com")
print(driver.title)
# 自动调用quit()
3.8 代理设置与网络配置
python
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
# ========== HTTP代理配置 ==========
options = Options()
# 方法1:通过启动参数设置代理
options.add_argument("--proxy-server=http://127.0.0.1:8080")
# 方法2:通过偏好设置配置代理
prefs = {
"proxy": {
"httpProxy": "127.0.0.1:8080",
"ftpProxy": "127.0.0.1:8080",
"sslProxy": "127.0.0.1:8080",
"proxyType": "MANUAL"
}
}
options.add_experimental_option("prefs", prefs)
driver = webdriver.Chrome(options=options)
# ========== 需要认证的代理 ==========
# 带用户名密码的代理
# 格式:http://username:password@host:port
options.add_argument("--proxy-server=http://user:pass@127.0.0.1:8080")
# 或者使用扩展程序处理认证(更可靠)
# 需要创建一个代理认证扩展
# ========== SOCKS代理配置 ==========
options.add_argument("--proxy-server=socks5://127.0.0.1:1080")
# ========== 代理自动配置(PAC) ==========
options.add_argument("--proxy-pac-url=http://example.com/proxy.pac")
# ========== 禁用代理 ==========
# 对某些地址不使用代理
options.add_argument("--proxy-bypass-list=localhost,127.0.0.1")
# ========== Firefox代理配置 ==========
from selenium.webdriver.firefox.options import Options as FirefoxOptions
firefox_options = FirefoxOptions()
firefox_options.set_preference("network.proxy.type", 1) # 手动配置
firefox_options.set_preference("network.proxy.http", "127.0.0.1")
firefox_options.set_preference("network.proxy.http_port", 8080)
firefox_options.set_preference("network.proxy.ssl", "127.0.0.1")
firefox_options.set_preference("network.proxy.ssl_port", 8080)
driver = webdriver.Firefox(options=firefox_options)
driver.quit()
3.9 浏览器偏好设置参数详解
python
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
# ========== 下载相关偏好设置 ==========
download_prefs = {
# 设置默认下载目录
"download.default_directory": r"C:\Downloads",
# 禁用下载提示
"download.prompt_for_download": False,
# 升级下载目录
"download.directory_upgrade": True,
# 禁用安全浏览
"safebrowsing.enabled": False,
# 允许的文件类型(不提示直接下载)
"download.extensions_to_open": "exe,zip,pdf",
}
# ========== 通知相关偏好设置 ==========
notification_prefs = {
# 禁用通知
"profile.default_content_setting_values.notifications": 2,
# 禁用地理位置
"profile.default_content_setting_values.geolocation": 2,
# 禁用摄像头
"profile.default_content_setting_values.media_stream_camera": 2,
# 禁用麦克风
"profile.default_content_setting_values.media_stream_mic": 2,
}
# ========== 密码相关偏好设置 ==========
password_prefs = {
# 禁用密码保存提示
"credentials_enable_service": False,
"profile.password_manager_enabled": False,
}
# ========== 语言相关偏好设置 ==========
language_prefs = {
# 设置浏览器语言
"intl.accept_languages": "zh-CN,zh",
}
# ========== 合并所有偏好设置 ==========
all_prefs = {}
all_prefs.update(download_prefs)
all_prefs.update(notification_prefs)
all_prefs.update(password_prefs)
all_prefs.update(language_prefs)
options.add_experimental_option("prefs", all_prefs)
driver = webdriver.Chrome(options=options)
# ========== 偏好设置值说明表 ==========
"""
+------------------------------------------+--------+------------------------+
| 偏好设置键 | 值 | 说明 |
+------------------------------------------+--------+------------------------+
| notifications | 1 | 允许 |
| notifications | 2 | 禁止 |
| notifications | 3 | 询问 |
| credentials_enable_service | true | 启用密码保存 |
| credentials_enable_service | false | 禁用密码保存 |
| download.prompt_for_download | true | 下载前询问 |
| download.prompt_for_download | false | 直接下载 |
+------------------------------------------+--------+------------------------+
"""
driver.quit()
3.10 移动端模拟与设备模拟
python
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
# ========== 移动设备模拟 ==========
options = Options()
# 方法1:使用设备名称模拟
mobile_emulation = {
"deviceName": "iPhone X"
}
options.add_experimental_option("mobileEmulation", mobile_emulation)
driver = webdriver.Chrome(options=options)
driver.get("https://www.example.com")
# 方法2:自定义设备参数
mobile_emulation = {
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1",
"deviceMetrics": {
"width": 375,
"height": 812,
"pixelRatio": 3.0,
"touch": True
}
}
options.add_experimental_option("mobileEmulation", mobile_emulation)
# ========== 常用设备预设 ==========
DEVICE_PRESETS = {
"iphone_x": {
"deviceName": "iPhone X"
},
"iphone_12": {
"deviceName": "iPhone 12 Pro"
},
"pixel_5": {
"deviceName": "Pixel 5"
},
"galaxy_s20": {
"deviceName": "Samsung Galaxy S20 Ultra"
},
"ipad_pro": {
"deviceName": "iPad Pro"
}
}
# ========== 创建移动端测试工具类 ==========
class MobileEmulator:
"""
移动端模拟工具类
"""
DEVICES = {
"iphone_x": {
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.34",
"width": 375,
"height": 812,
"pixelRatio": 3.0
},
"pixel_5": {
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36",
"width": 393,
"height": 851,
"pixelRatio": 2.75
},
"ipad": {
"userAgent": "Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34",
"width": 768,
"height": 1024,
"pixelRatio": 2.0
}
}
@staticmethod
def create_mobile_driver(device_name: str):
"""
创建模拟移动设备的WebDriver
参数:
device_name: 设备名称,支持 'iphone_x', 'pixel_5', 'ipad'
"""
options = Options()
if device_name in MobileEmulator.DEVICES:
device = MobileEmulator.DEVICES[device_name]
mobile_emulation = {
"userAgent": device["userAgent"],
"deviceMetrics": {
"width": device["width"],
"height": device["height"],
"pixelRatio": device["pixelRatio"],
"touch": True
}
}
options.add_experimental_option("mobileEmulation", mobile_emulation)
else:
# 使用预设设备名称
options.add_experimental_option(
"mobileEmulation",
{"deviceName": device_name}
)
return webdriver.Chrome(options=options)
# 使用示例
driver = MobileEmulator.create_mobile_driver("iphone_x")
driver.get("https://m.example.com")
print(f"当前窗口大小: {driver.get_window_size()}")
driver.quit()
3.11 地理位置模拟
python
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
# ========== 地理位置模拟 ==========
driver = webdriver.Chrome()
# 方法1:使用CDP命令设置地理位置
# 需要先授予地理位置权限
driver.execute_cdp_cmd("Page.setGeolocationOverride", {
"latitude": 39.9042, # 北京纬度
"longitude": 116.4074, # 北京经度
"accuracy": 100 # 精度(米)
})
# 方法2:通过偏好设置默认位置
options = Options()
options.add_experimental_option("prefs", {
"profile.default_content_setting_values.geolocation": 1, # 允许
})
# ========== 地理位置测试工具类 ==========
class GeolocationSimulator:
"""
地理位置模拟工具类
"""
LOCATIONS = {
"beijing": {"latitude": 39.9042, "longitude": 116.4074},
"shanghai": {"latitude": 31.2304, "longitude": 121.4737},
"new_york": {"latitude": 40.7128, "longitude": -74.0060},
"london": {"latitude": 51.5074, "longitude": -0.1278},
"tokyo": {"latitude": 35.6762, "longitude": 139.6503}
}
def __init__(self, driver):
self.driver = driver
def set_location(self, location_name: str, accuracy: int = 100):
"""
设置模拟地理位置
参数:
location_name: 位置名称
accuracy: 精度(米)
"""
if location_name not in self.LOCATIONS:
raise ValueError(f"未知位置: {location_name}")
location = self.LOCATIONS[location_name]
self.driver.execute_cdp_cmd("Page.setGeolocationOverride", {
"latitude": location["latitude"],
"longitude": location["longitude"],
"accuracy": accuracy
})
print(f"地理位置已设置为: {location_name}")
def set_custom_location(self, latitude: float, longitude: float, accuracy: int = 100):
"""
设置自定义地理位置
参数:
latitude: 纬度
longitude: 经度
accuracy: 精度(米)
"""
self.driver.execute_cdp_cmd("Page.setGeolocationOverride", {
"latitude": latitude,
"longitude": longitude,
"accuracy": accuracy
})
print(f"地理位置已设置为: ({latitude}, {longitude})")
def clear_location_override(self):
"""清除地理位置覆盖设置"""
self.driver.execute_cdp_cmd("Page.clearGeolocationOverride", {})
# 使用示例
driver = webdriver.Chrome()
driver.get("https://www.example.com/location-test")
geo_simulator = GeolocationSimulator(driver)
geo_simulator.set_location("beijing")
# 验证地理位置
location_info = driver.execute_script(
"return new Promise((resolve) => navigator.geolocation.getCurrentPosition(resolve))"
)
print(f"当前位置: {location_info}")
driver.quit()
第四章:页面元素交互操作
4.1 WebElement接口方法完整详解
WebElement是Selenium中表示页面元素的核心接口,提供了丰富的元素操作方法。
定位查询方法
属性获取方法
状态查询方法
交互操作方法
WebElement方法分类
交互操作
状态查询
属性获取
定位查询
click
send_keys
clear
submit
is_displayed
is_enabled
is_selected
get_attribute
get_property
text
tag_name
size
location
rect
get_dom_attribute
get_shadow_root
aria_role
accessible_name
find_element
find_elements
python
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://www.example.com/form")
# ========== WebElement完整方法演示 ==========
# 获取WebElement对象
element = driver.find_element(By.ID, "username")
# ========== 交互操作方法 ==========
# click() - 点击元素
button = driver.find_element(By.ID, "submit")
button.click()
# send_keys(*value) - 输入内容
# 支持输入字符串、特殊按键、文件路径
from selenium.webdriver.common.keys import Keys
input_field = driver.find_element(By.ID, "username")
input_field.send_keys("test_user")
input_field.send_keys(Keys.ENTER) # 输入回车键
# clear() - 清除输入内容
input_field.clear()
# submit() - 提交表单
# 如果元素在表单内,会提交整个表单
form = driver.find_element(By.ID, "login-form")
form.submit()
# ========== 状态查询方法 ==========
# is_displayed() - 元素是否可见
# 返回True/False
is_visible = element.is_displayed()
print(f"元素是否可见: {is_visible}")
# is_enabled() - 元素是否可用(未禁用)
# 返回True/False,适用于按钮、输入框等
is_active = element.is_enabled()
print(f"元素是否可用: {is_active}")
# is_selected() - 元素是否被选中
# 适用于复选框、单选框、下拉选项
checkbox = driver.find_element(By.ID, "agree")
is_checked = checkbox.is_selected()
print(f"复选框是否选中: {is_checked}")
# ========== 属性获取方法 ==========
# text - 获取元素的文本内容
# 只获取可见文本,不包括隐藏文本
paragraph = driver.find_element(By.TAG_NAME, "p")
text_content = paragraph.text
print(f"元素文本: {text_content}")
# tag_name - 获取元素标签名
tag = element.tag_name
print(f"标签名: {tag}") # 例如: input, div, span
# get_attribute(name) - 获取元素属性值
# 获取HTML属性,如id, class, href, src等
input_type = element.get_attribute("type")
input_value = element.get_attribute("value")
input_class = element.get_attribute("class")
print(f"类型: {input_type}, 值: {input_value}")
# get_property(name) - 获取元素DOM属性
# 获取JavaScript属性,与HTML属性可能不同
checked_property = checkbox.get_property("checked")
value_property = input_field.get_property("value")
# get_dom_attribute(name) - 获取DOM属性(Selenium 4新增)
# 直接获取HTML中定义的属性值
dom_value = element.get_dom_attribute("data-id")
# size - 获取元素尺寸
# 返回字典: {'width': 100, 'height': 30}
element_size = element.size
print(f"元素尺寸: {element_size}")
# location - 获取元素位置(相对于视口左上角)
# 返回字典: {'x': 100, 'y': 200}
element_location = element.location
print(f"元素位置: {element_location}")
# rect - 获取元素矩形信息(位置+尺寸)
# 返回字典: {'x': 100, 'y': 200, 'width': 100, 'height': 30}
element_rect = element.rect
print(f"元素矩形: {element_rect}")
# aria_role - 获取元素的ARIA角色(Selenium 4新增)
aria = element.aria_role
print(f"ARIA角色: {aria}")
# accessible_name - 获取元素的无障碍名称(Selenium 4新增)
acc_name = element.accessible_name
print(f"无障碍名称: {acc_name}")
# get_shadow_root() - 获取Shadow DOM根节点(Selenium 4新增)
shadow_host = driver.find_element(By.CSS_SELECTOR, "#shadow-host")
shadow_root = shadow_host.shadow_root
# screenshot(filename) - 元素截图
element.screenshot("element_screenshot.png")
# ========== 定位查询方法 ==========
# find_element - 在当前元素内部查找子元素
# 相对于当前元素进行定位
container = driver.find_element(By.ID, "container")
inner_element = container.find_element(By.CLASS_NAME, "item")
# find_elements - 在当前元素内部查找所有匹配的子元素
items = container.find_elements(By.TAG_NAME, "li")
print(f"找到 {len(items)} 个列表项")
driver.quit()
4.2 基础操作(点击、输入、清除)及参数
python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get("https://www.example.com/form")
# ========== 点击操作 click() ==========
# 基本点击
button = driver.find_element(By.ID, "submit")
button.click()
# 点击前等待元素可点击
clickable_button = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.ID, "submit"))
)
clickable_button.click()
# 使用JavaScript点击(当常规点击失败时)
driver.execute_script("arguments[0].click();", button)
# ========== 输入操作 send_keys() ==========
# 基本文本输入
input_field = driver.find_element(By.ID, "username")
input_field.send_keys("test_user")
# 输入后追加内容(不清除原有内容)
input_field.send_keys("_additional")
# 输入特殊按键
input_field.send_keys(Keys.ENTER) # 回车
input_field.send_keys(Keys.TAB) # Tab键
input_field.send_keys(Keys.ESCAPE) # ESC键
input_field.send_keys(Keys.BACKSPACE) # 退格键
input_field.send_keys(Keys.DELETE) # 删除键
input_field.send_keys(Keys.CONTROL, 'a') # Ctrl+A 全选
# 组合键输入
from selenium.webdriver import ActionChains
actions = ActionChains(driver)
actions.key_down(Keys.CONTROL).send_keys('a').key_up(Keys.CONTROL).perform()
# 模拟键盘输入(逐字符输入,模拟真实打字)
import time
def slow_type(element, text, delay=0.1):
"""
模拟慢速输入
参数:
element: 目标元素
text: 要输入的文本
delay: 每个字符之间的延迟(秒)
"""
for char in text:
element.send_keys(char)
time.sleep(delay)
slow_type(input_field, "slow_typing_text")
# ========== 清除操作 clear() ==========
# 清除输入框内容
input_field.clear()
# 清除后输入新内容
input_field.clear()
input_field.send_keys("new_value")
# ========== 输入操作最佳实践 ==========
def safe_input(element, text, clear_first=True):
"""
安全的输入操作
参数:
element: 目标元素
text: 要输入的文本
clear_first: 是否先清除原有内容
"""
# 等待元素可见
WebDriverWait(driver, 10).until(
EC.visibility_of(element)
)
# 点击元素获取焦点
element.click()
# 清除原有内容
if clear_first:
element.clear()
# 使用Ctrl+A全选删除(更可靠)
element.send_keys(Keys.CONTROL + "a")
element.send_keys(Keys.DELETE)
# 输入新内容
element.send_keys(text)
# 验证输入结果
actual_value = element.get_attribute("value")
assert actual_value == text, f"输入验证失败: 期望 '{text}', 实际 '{actual_value}'"
driver.quit()
4.3 Keys类与特殊按键操作详解
python
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://www.example.com")
# ========== Keys类完整枚举 ==========
# 功能键
Keys.F1, Keys.F2, Keys.F3, Keys.F4, Keys.F5, Keys.F6
Keys.F7, Keys.F8, Keys.F9, Keys.F10, Keys.F11, Keys.F12
# 控制键
Keys.CONTROL # Ctrl键
Keys.ALT # Alt键
Keys.SHIFT # Shift键
Keys.META # Meta键(Mac的Command键)
# 导航键
Keys.TAB # Tab键
Keys.ENTER # 回车键
Keys.ESCAPE # ESC键
Keys.BACKSPACE # 退格键
Keys.DELETE # 删除键
Keys.SPACE # 空格键
# 方向键
Keys.ARROW_UP # 上箭头
Keys.ARROW_DOWN # 下箭头
Keys.ARROW_LEFT # 左箭头
Keys.ARROW_RIGHT # 右箭头
# 页面导航键
Keys.HOME # Home键
Keys.END # End键
Keys.PAGE_UP # Page Up键
Keys.PAGE_DOWN # Page Down键
# ========== 常用组合键示例 ==========
input_field = driver.find_element(By.ID, "search")
# Ctrl+A 全选
input_field.send_keys(Keys.CONTROL, 'a')
# Ctrl+C 复制
input_field.send_keys(Keys.CONTROL, 'c')
# Ctrl+V 粘贴
input_field.send_keys(Keys.CONTROL, 'v')
# Ctrl+X 剪切
input_field.send_keys(Keys.CONTROL, 'x')
# Ctrl+Z 撤销
input_field.send_keys(Keys.CONTROL, 'z')
# ========== 特殊按键使用场景 ==========
# 场景1:表单提交
search_box = driver.find_element(By.ID, "search")
search_box.send_keys("Selenium")
search_box.send_keys(Keys.ENTER) # 按回车提交
# 场景2:Tab键导航
input1 = driver.find_element(By.ID, "field1")
input1.send_keys("value")
input1.send_keys(Keys.TAB) # 移动到下一个输入框
# 场景3:清空输入框
input_field.send_keys(Keys.CONTROL, 'a') # 全选
input_field.send_keys(Keys.DELETE) # 删除
# 场景4:页面滚动
body = driver.find_element(By.TAG_NAME, "body")
body.send_keys(Keys.PAGE_DOWN) # 向下翻页
body.send_keys(Keys.END) # 滚动到底部
body.send_keys(Keys.HOME) # 滚动到顶部
# ========== Keys类方法汇总表 ==========
"""
+------------------+------------------+----------------------------------+
| 按键常量 | 对应按键 | 常用场景 |
+------------------+------------------+----------------------------------+
| Keys.ENTER | 回车 | 表单提交、确认 |
| Keys.TAB | Tab | 表单导航 |
| Keys.ESCAPE | ESC | 关闭弹窗、取消操作 |
| Keys.BACKSPACE | 退格 | 删除前一个字符 |
| Keys.DELETE | Delete | 删除选中内容 |
| Keys.CONTROL | Ctrl | 组合快捷键 |
| Keys.SHIFT | Shift | 大写、组合快捷键 |
| Keys.ARROW_* | 方向键 | 导航、选择 |
| Keys.HOME/END | Home/End | 跳转到开头/结尾 |
| Keys.PAGE_* | Page Up/Down | 翻页 |
| Keys.F1-F12 | 功能键 | 特殊功能 |
+------------------+------------------+----------------------------------+
"""
driver.quit()
4.4 Select类完整API详解
python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select
driver = webdriver.Chrome()
driver.get("https://www.example.com/form")
# ========== Select类初始化 ==========
# 定位select元素
select_element = driver.find_element(By.ID, "country")
select = Select(select_element)
# ========== 选择选项方法 ==========
# 方法1:通过可见文本选择
# select_by_visible_text(text)
select.select_by_visible_text("中国")
# 方法2:通过value属性值选择
# select_by_value(value)
select.select_by_value("cn")
# 方法3:通过索引选择(从0开始)
# select_by_index(index)
select.select_by_index(0) # 选择第一个选项
# ========== 取消选择方法(多选下拉框) ==========
# 取消选择所有选项
select.deselect_all()
# 通过可见文本取消选择
select.deselect_by_visible_text("中国")
# 通过value属性值取消选择
select.deselect_by_value("cn")
# 通过索引取消选择
select.deselect_by_index(0)
# ========== 获取选项信息方法 ==========
# 获取所有选项
all_options = select.options
print(f"选项总数: {len(all_options)}")
for option in all_options:
print(f"文本: {option.text}, 值: {option.get_attribute('value')}")
# 获取所有已选中的选项
selected_options = select.all_selected_options
for option in selected_options:
print(f"已选: {option.text}")
# 获取第一个选中的选项
first_selected = select.first_selected_option
print(f"首个选中项: {first_selected.text}")
# ========== 判断下拉框类型 ==========
# 是否为多选下拉框
is_multiple = select.is_multiple
print(f"是否多选: {is_multiple}")
# ========== Select类完整API示例 ==========
def select_demo():
"""
Select类完整使用示例
"""
driver = webdriver.Chrome()
driver.get("https://www.example.com/select-demo")
# 单选下拉框
single_select = Select(driver.find_element(By.ID, "single-select"))
single_select.select_by_visible_text("选项1")
print(f"单选结果: {single_select.first_selected_option.text}")
# 多选下拉框
multi_select = Select(driver.find_element(By.ID, "multi-select"))
# 选择多个选项
multi_select.select_by_index(0)
multi_select.select_by_index(1)
multi_select.select_by_index(2)
# 验证选中项
selected = multi_select.all_selected_options
print(f"已选择 {len(selected)} 个选项")
# 取消选择
multi_select.deselect_by_index(0)
# 取消所有选择
multi_select.deselect_all()
driver.quit()
# ========== Select类API汇总表 ==========
"""
+----------------------------+------------------+----------------------------------+
| 方法 | 参数类型 | 说明 |
+----------------------------+------------------+----------------------------------+
| select_by_visible_text | str | 通过可见文本选择 |
| select_by_value | str | 通过value属性选择 |
| select_by_index | int | 通过索引选择(从0开始) |
| deselect_by_visible_text | str | 通过可见文本取消选择 |
| deselect_by_value | str | 通过value属性取消选择 |
| deselect_by_index | int | 通过索引取消选择 |
| deselect_all | 无 | 取消所有选择 |
| options | 属性 | 返回所有选项列表 |
| all_selected_options | 属性 | 返回所有已选选项列表 |
| first_selected_option | 属性 | 返回第一个已选选项 |
| is_multiple | 属性 | 返回是否为多选 |
+----------------------------+------------------+----------------------------------+
"""
driver.quit()
4.5 单选框与复选框操作
python
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
driver = webdriver.Chrome()
driver.get("https://www.example.com/form")
# ========== 复选框操作 ==========
# 定位复选框
checkbox = driver.find_element(By.ID, "agree")
# 检查是否选中
is_checked = checkbox.is_selected()
print(f"复选框状态: {'已选中' if is_checked else '未选中'}")
# 选中复选框(如果未选中)
if not checkbox.is_selected():
checkbox.click()
# 取消选中(如果已选中)
if checkbox.is_selected():
checkbox.click()
# ========== 单选框操作 ==========
# 定位单选框组
radio_buttons = driver.find_elements(By.NAME, "gender")
# 选择特定单选框
for radio in radio_buttons:
if radio.get_attribute("value") == "male":
radio.click()
break
# 验证选中状态
for radio in radio_buttons:
if radio.is_selected():
print(f"已选中: {radio.get_attribute('value')}")
# ========== 封装复选框/单选框操作工具类 ==========
class CheckboxRadioHandler:
"""
复选框和单选框操作工具类
"""
def __init__(self, driver):
self.driver = driver
def select_checkbox(self, locator, force_select=True):
"""
操作复选框
参数:
locator: 元素定位器
force_select: True-确保选中, False-确保取消选中
"""
checkbox = self.driver.find_element(*locator)
if force_select and not checkbox.is_selected():
checkbox.click()
elif not force_select and checkbox.is_selected():
checkbox.click()
def select_radio_by_value(self, name, value):
"""
通过value选择单选框
参数:
name: 单选框组的name属性
value: 目标单选框的value属性
"""
radio = self.driver.find_element(
By.CSS_SELECTOR, f'input[type="radio"][name="{name}"][value="{value}"]'
)
if not radio.is_selected():
radio.click()
def get_selected_radio_value(self, name):
"""
获取选中的单选框value值
参数:
name: 单选框组的name属性
返回:
选中单选框的value,如果没有选中返回None
"""
radios = self.driver.find_elements(
By.CSS_SELECTOR, f'input[type="radio"][name="{name}"]'
)
for radio in radios:
if radio.is_selected():
return radio.get_attribute("value")
return None
def get_all_checked_checkboxes(self, locator):
"""
获取所有选中的复选框
参数:
locator: 复选框组定位器
返回:
已选中复选框的WebElement列表
"""
checkboxes = self.driver.find_elements(*locator)
return [cb for cb in checkboxes if cb.is_selected()]
# 使用示例
handler = CheckboxRadioHandler(driver)
# 操作复选框
handler.select_checkbox((By.ID, "agree"), force_select=True)
# 选择单选框
handler.select_radio_by_value("gender", "male")
# 获取选中的单选框值
selected_gender = handler.get_selected_radio_value("gender")
print(f"选中的性别: {selected_gender}")
driver.quit()
4.6 元素属性与状态获取方法
python
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://www.example.com")
element = driver.find_element(By.ID, "username")
# ========== 获取元素属性 ==========
# get_attribute(name) - 获取HTML属性
# 包括:id, class, name, type, value, href, src, style等
attr_id = element.get_attribute("id")
attr_class = element.get_attribute("class")
attr_name = element.get_attribute("name")
attr_type = element.get_attribute("type")
attr_value = element.get_attribute("value")
attr_placeholder = element.get_attribute("placeholder")
# 获取自定义属性
data_id = element.get_attribute("data-id")
data_type = element.get_attribute("data-type")
# get_property(name) - 获取DOM属性
# 与HTML属性的区别:获取JavaScript运行时的属性值
prop_value = element.get_property("value") # 当前输入值
prop_checked = element.get_property("checked") # 是否选中
# get_dom_attribute(name) - 获取HTML定义的属性(Selenium 4新增)
# 只返回HTML中明确定义的属性值
dom_attr = element.get_dom_attribute("class")
# ========== 获取元素CSS样式 ==========
# value_of_css_property(property_name) - 获取计算后的CSS样式值
color = element.value_of_css_property("color")
font_size = element.value_of_css_property("font-size")
display = element.value_of_css_property("display")
visibility = element.value_of_css_property("visibility")
# ========== 获取元素文本 ==========
# text - 获取可见文本
text = element.text
# 获取包含隐藏文本的所有文本(使用JavaScript)
all_text = driver.execute_script("return arguments[0].textContent;", element)
inner_text = driver.execute_script("return arguments[0].innerText;", element)
# ========== 获取元素位置和尺寸 ==========
# location - 获取位置(相对于视口)
location = element.location
x = location['x']
y = location['y']
# size - 获取尺寸
size = element.size
width = size['width']
height = size['height']
# rect - 获取位置和尺寸
rect = element.rect
# {'x': 10, 'y': 20, 'width': 100, 'height': 30}
# ========== 获取元素状态 ==========
# 是否显示(可见)
is_displayed = element.is_displayed()
# 是否可用(未禁用)
is_enabled = element.is_enabled()
# 是否选中(复选框、单选框)
is_selected = element.is_selected()
# ========== 属性获取方法对比表 ==========
"""
+---------------------+----------------+----------------------------------+
| 方法 | 返回内容 | 示例 |
+---------------------+----------------+----------------------------------+
| get_attribute | HTML属性值 | value, class, id |
| get_property | DOM属性值 | checked, value(实时) |
| get_dom_attribute | HTML定义属性 | 只返回HTML中定义的值 |
| text | 可见文本 | 元素显示的文本 |
| tag_name | 标签名 | input, div, span |
| value_of_css_property| CSS样式值 | color, font-size |
| location | 位置 | {'x': 10, 'y': 20} |
| size | 尺寸 | {'width': 100, 'height': 30} |
| rect | 位置+尺寸 | {'x': 10, 'y': 20, ...} |
+---------------------+----------------+----------------------------------+
"""
driver.quit()
4.7 批量元素定位与操作
python
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://www.example.com/list")
# ========== 批量定位元素 ==========
# find_elements - 返回匹配的所有元素列表
# 如果没有匹配项,返回空列表(不抛异常)
items = driver.find_elements(By.CSS_SELECTOR, ".list-item")
print(f"找到 {len(items)} 个元素")
# ========== 批量操作示例 ==========
# 示例1:获取所有链接文本
links = driver.find_elements(By.TAG_NAME, "a")
link_texts = [link.text for link in links]
print(f"所有链接文本: {link_texts}")
# 示例2:点击所有按钮
buttons = driver.find_elements(By.CSS_SELECTOR, ".btn")
for button in buttons:
if button.is_displayed() and button.is_enabled():
button.click()
# 示例3:获取所有输入框的值
inputs = driver.find_elements(By.CSS_SELECTOR, "input[type='text']")
values = {inp.get_attribute("name"): inp.get_attribute("value") for inp in inputs}
print(f"所有输入值: {values}")
# 示例4:批量验证元素状态
checkboxes = driver.find_elements(By.CSS_SELECTOR, "input[type='checkbox']")
checked_count = sum(1 for cb in checkboxes if cb.is_selected())
print(f"已选中 {checked_count}/{len(checkboxes)} 个复选框")
# ========== 使用列表推导式批量操作 ==========
# 过滤可见元素
visible_items = [item for item in items if item.is_displayed()]
# 过滤可用元素
enabled_buttons = [btn for btn in buttons if btn.is_enabled()]
# 获取特定属性的元素
elements_with_data = [
elem for elem in items
if elem.get_attribute("data-id")
]
# ========== 批量操作工具类 ==========
class BatchElementHandler:
"""
批量元素操作工具类
"""
def __init__(self, driver):
self.driver = driver
def get_all_texts(self, locator):
"""获取所有匹配元素的文本"""
elements = self.driver.find_elements(*locator)
return [elem.text for elem in elements]
def get_all_attributes(self, locator, attribute):
"""获取所有匹配元素的指定属性"""
elements = self.driver.find_elements(*locator)
return [elem.get_attribute(attribute) for elem in elements]
def click_all(self, locator, filter_visible=True):
"""点击所有匹配元素"""
elements = self.driver.find_elements(*locator)
for elem in elements:
if filter_visible and not elem.is_displayed():
continue
if elem.is_enabled():
elem.click()
def filter_by_condition(self, locator, condition_func):
"""根据条件过滤元素"""
elements = self.driver.find_elements(*locator)
return [elem for elem in elements if condition_func(elem)]
def find_element_by_text(self, locator, text):
"""在匹配元素中查找包含特定文本的元素"""
elements = self.driver.find_elements(*locator)
for elem in elements:
if text in elem.text:
return elem
return None
# 使用示例
handler = BatchElementHandler(driver)
# 获取所有列表项文本
texts = handler.get_all_texts((By.CSS_SELECTOR, ".list-item"))
# 点击所有可见按钮
handler.click_all((By.CSS_SELECTOR, ".btn"))
# 过滤包含特定文本的元素
important_items = handler.filter_by_condition(
(By.CSS_SELECTOR, ".item"),
lambda elem: "重要" in elem.text
)
driver.quit()
4.8 Actions类高级交互(鼠标、键盘操作)
python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
driver = webdriver.Chrome()
driver.get("https://www.example.com")
# 创建ActionChains对象
actions = ActionChains(driver)
# ========== 鼠标操作 ==========
# 单击(等同于element.click())
element = driver.find_element(By.ID, "button")
actions.click(element).perform()
# 双击
actions.double_click(element).perform()
# 右键点击
actions.context_click(element).perform()
# 鼠标悬停
hover_element = driver.find_element(By.ID, "menu")
actions.move_to_element(hover_element).perform()
# 拖拽操作
source = driver.find_element(By.ID, "draggable")
target = driver.find_element(By.ID, "droppable")
actions.drag_and_drop(source, target).perform()
# 按偏移量拖拽
actions.click_and_hold(source).move_by_offset(100, 0).release().perform()
# 移动到元素中心偏移位置
actions.move_to_element_with_offset(element, 10, 10).perform()
# 按住鼠标左键
actions.click_and_hold(element).perform()
# 释放鼠标
actions.release(element).perform()
# ========== 键盘操作 ==========
# 按下按键
actions.key_down(Keys.SHIFT).perform()
# 释放按键
actions.key_up(Keys.SHIFT).perform()
# 发送按键
actions.send_keys("Hello").perform()
# 发送按键到特定元素
actions.send_keys_to_element(element, "Hello").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()
# Shift+点击(多选)
item1 = driver.find_element(By.CSS_SELECTOR, ".item:nth-child(1)")
item2 = driver.find_element(By.CSS_SELECTOR, ".item:nth-child(3)")
actions.key_down(Keys.SHIFT).click(item1).click(item2).key_up(Keys.SHIFT).perform()
# ========== 链式操作 ==========
# 多个操作组合执行
actions.move_to_element(element).click().send_keys("test").send_keys(Keys.ENTER).perform()
# 拖拽并放置
actions.drag_and_drop(source, target).click(target).perform()
# ========== Actions类方法汇总表 ==========
"""
+------------------------+------------------+----------------------------------+
| 方法 | 参数 | 说明 |
+------------------------+------------------+----------------------------------+
| click(element) | WebElement | 单击元素 |
| click_and_hold(elem) | WebElement | 按住鼠标左键 |
| context_click(elem) | WebElement | 右键点击 |
| double_click(elem) | WebElement | 双击 |
| drag_and_drop(src,dst) | WebElement,WebElement | 拖拽 |
| move_by_offset(x, y) | int, int | 按偏移量移动鼠标 |
| move_to_element(elem) | WebElement | 移动到元素中心 |
| move_to_element_with_offset(elem, x, y) | WebElement, int, int | 移动到元素偏移位置 |
| release(element) | WebElement | 释放鼠标 |
| send_keys(keys) | str/Keys | 发送按键 |
| send_keys_to_element(elem, keys) | WebElement, str | 发送按键到元素 |
| key_down(key) | Keys | 按下按键 |
| key_up(key) | Keys | 释放按键 |
| pause(seconds) | float | 暂停 |
| perform() | 无 | 执行所有操作 |
| reset_actions() | 无 | 重置操作队列 |
+------------------------+------------------+----------------------------------+
"""
driver.quit()
4.9 JavaScript执行器应用
python
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://www.example.com")
# ========== execute_script 基本用法 ==========
# 执行JavaScript代码
driver.execute_script("alert('Hello World');")
# 执行并获取返回值
title = driver.execute_script("return document.title;")
print(f"页面标题: {title}")
# ========== 传递参数 ==========
element = driver.find_element(By.ID, "username")
# arguments数组接收参数
driver.execute_script("arguments[0].value = 'test_user';", element)
# 多个参数
driver.execute_script(
"arguments[0].setAttribute(arguments[1], arguments[2]);",
element, "data-test", "value"
)
# ========== 常用JavaScript操作 ==========
# 滚动到元素可见
element = driver.find_element(By.ID, "footer")
driver.execute_script("arguments[0].scrollIntoView(true);", element)
# 滚动到页面底部
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
# 滚动到页面顶部
driver.execute_script("window.scrollTo(0, 0);")
# 按像素滚动
driver.execute_script("window.scrollBy(0, 500);")
# 点击元素(绕过遮挡)
driver.execute_script("arguments[0].click();", element)
# 获取元素属性
value = driver.execute_script("return arguments[0].value;", element)
# 设置元素属性
driver.execute_script("arguments[0].disabled = false;", element)
# 移除元素属性
driver.execute_script("arguments[0].removeAttribute('disabled');", element)
# 高亮元素(调试用)
driver.execute_script(
"arguments[0].style.border='3px solid red';",
element
)
# 获取页面源码
source = driver.execute_script("return document.documentElement.outerHTML;")
# 获取元素的所有文本(包括隐藏)
text = driver.execute_script("return arguments[0].textContent;", element)
# ========== execute_async_script 异步执行 ==========
# 执行异步脚本
# 需要调用callback表示完成
result = driver.execute_async_script("""
var callback = arguments[arguments.length - 1];
setTimeout(function() {
callback('Async result');
}, 1000);
""")
print(f"异步结果: {result}")
# ========== JavaScript执行器工具类 ==========
class JavaScriptHelper:
"""
JavaScript执行器工具类
"""
def __init__(self, driver):
self.driver = driver
def scroll_to_element(self, element):
"""滚动到元素可见"""
self.driver.execute_script(
"arguments[0].scrollIntoView({block: 'center'});",
element
)
def click_by_js(self, element):
"""使用JavaScript点击元素"""
self.driver.execute_script("arguments[0].click();", element)
def highlight(self, element, color="red", duration=2000):
"""高亮元素(调试用)"""
original_style = element.get_attribute("style")
self.driver.execute_script(
f"arguments[0].style.border='3px solid {color}';",
element
)
if duration > 0:
import time
time.sleep(duration / 1000)
self.driver.execute_script(
f"arguments[0].style='{original_style}';",
element
)
def set_value(self, element, value):
"""设置输入框的值(绕过某些限制)"""
self.driver.execute_script(
"arguments[0].value = arguments[1];",
element, value
)
def get_all_text(self, element):
"""获取元素所有文本(包括隐藏)"""
return self.driver.execute_script(
"return arguments[0].textContent;",
element
)
def remove_attribute(self, element, attribute):
"""移除元素属性"""
self.driver.execute_script(
"arguments[0].removeAttribute(arguments[1]);",
element, attribute
)
def set_attribute(self, element, name, value):
"""设置元素属性"""
self.driver.execute_script(
"arguments[0].setAttribute(arguments[1], arguments[2]);",
element, name, value
)
# 使用示例
js_helper = JavaScriptHelper(driver)
element = driver.find_element(By.ID, "username")
js_helper.highlight(element)
js_helper.set_value(element, "test")
js_helper.scroll_to_element(element)
driver.quit()