Selenium 教程
Selenium 教程
Selenium 是目前最流行的 Web 自动化测试框架,支持多种编程语言(Python、Java、C#、JavaScript 等)和主流浏览器(Chrome、Firefox、Edge、Safari),用于浏览器自动化、UI 测试、爬虫等场景。
Selenium 组件
| 组件 | 用途 |
|---|---|
| Selenium WebDriver | 核心 API,驱动浏览器自动化 |
| Selenium IDE | 浏览器插件,录制回放测试 |
| Selenium Grid | 分布式测试,多机并行 |
支持的语言
Python from selenium import webdriver
Java import org.openqa.selenium.WebDriver;
C# using OpenQA.Selenium;
JavaScript require("selenium-webdriver")
Ruby require "selenium-webdriver"
第一个自动化脚本(Python)
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://www.example.com")
title = driver.title
print("页面标题:", title)
driver.quit()
学习路线
- 安装与环境配置
- WebDriver 与元素定位
- 元素操作与等待机制
- 浏览器、鼠标键盘高级操作
- 文件上传下载、Grid 分布式
- 测试框架集成与实战项目
Selenium 简介
Selenium 简介
1. 什么是 Selenium
Selenium 是一套用于 Web 应用自动化的工具,最初由 Jason Huggins 于 2004 年在 ThoughtWorks 开发,现已发展为开源社区的明星项目,广泛用于功能测试、回归测试和网页数据采集。
2. 核心组件
- WebDriver:直接驱动浏览器,模拟真实用户操作(点击、输入、滚动)
- IDE:Chrome/Firefox 扩展,录制操作并导出脚本,适合快速入门
- Grid:把测试分发到多台机器/多个浏览器并行执行,加速大型测试套件
3. 为什么选择 Selenium
- 开源免费,社区庞大
- 多语言多浏览器支持
- 模拟真实用户,测试真实度高
- 与其他测试框架(TestNG、JUnit、pytest)无缝集成
4. 适用场景
- Web UI 自动化测试
- 回归测试与冒烟测试
- 网页数据抓取(爬虫)
- 重复性网页操作自动化
5. 限制
- 无法测试桌面应用(需 Appium 等)
- 对动态内容依赖等待机制
- 浏览器升级可能导致兼容性问题
Selenium 安装
Selenium 安装
1. Python 环境安装
# 安装 selenium 库 pip install selenium # 验证 python -c "import selenium; print(selenium.__version__)"
2. 浏览器驱动(WebDriver)
Selenium 需要浏览器驱动才能控制浏览器:
# Chrome → ChromeDriver # 下载:https://chromedriver.chromium.org/downloads # 版本必须与 Chrome 浏览器大版本一致 # Firefox → geckodriver # https://github.com/mozilla/geckodriver/releases # Edge → msedgedriver
3. 驱动放置与配置
# 方法一:把驱动放到系统 PATH
mv chromedriver /usr/local/bin/
# 方法二:显式指定路径
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
service = Service("/path/to/chromedriver")
driver = webdriver.Chrome(service=service)
# 方法三(推荐):自动管理驱动
pip install webdriver-manager
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
4. Java 环境
# Maven 依赖
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.18.0</version>
</dependency>
5. 验证安装
from selenium import webdriver
driver = webdriver.Chrome()
driver.get("https://www.baidu.com")
print(driver.title) # 百度一下,你就知道
driver.quit()
print("Selenium 安装成功!")
Selenium WebDriver
Selenium WebDriver
1. 创建 WebDriver
from selenium import webdriver # Chrome driver = webdriver.Chrome() # Firefox driver = webdriver.Firefox() # Edge driver = webdriver.Edge() # Safari(macOS) driver = webdriver.Safari()
2. 浏览器选项(Options)
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--headless") # 无头模式
options.add_argument("--disable-gpu")
options.add_argument("--window-size=1920,1080")
options.add_argument("--user-agent=Mozilla/5.0 ...")
options.add_experimental_option("excludeSwitches", ["enable-logging"])
driver = webdriver.Chrome(options=options)
3. 常用 API
driver.get("https://example.com") # 打开网页
driver.title # 页面标题
driver.current_url # 当前 URL
driver.page_source # 页面源码
driver.back() # 后退
driver.forward() # 前进
driver.refresh() # 刷新
driver.quit() # 关闭浏览器并退出
driver.close() # 关闭当前标签页
driver.maximize_window() # 最大化窗口
driver.get_window_size() # 窗口尺寸
4. 窗口与标签管理
# 打开新标签页
driver.switch_to.new_window("tab")
driver.get("https://example.com")
# 切换窗口
windows = driver.window_handles
driver.switch_to.window(windows[-1])
# 切换 iframe
iframe = driver.find_element(By.TAG_NAME, "iframe")
driver.switch_to.frame(iframe)
# 切回主文档
driver.switch_to.default_content()
5. 生命周期最佳实践
try:
driver = webdriver.Chrome()
driver.get("https://example.com")
# 测试逻辑
finally:
driver.quit() # 确保释放资源
# 或用上下文管理器
with webdriver.Chrome() as driver:
driver.get("https://example.com")
print(driver.title)
Selenium 元素定位
Selenium 元素定位
定位元素是自动化的基础,Selenium 提供多种定位策略(By)。
1. 定位方式
from selenium.webdriver.common.by import By # 按 ID(最快,优先使用) driver.find_element(By.ID, "username") # 按 Class driver.find_element(By.CLASS_NAME, "btn-primary") # 按 Name driver.find_element(By.NAME, "password") # 按标签名 driver.find_element(By.TAG_NAME, "h1") # 按链接文本(精确) driver.find_element(By.LINK_TEXT, "点击登录") # 按部分链接文本 driver.find_element(By.PARTIAL_LINK_TEXT, "登录") # 按 CSS 选择器 driver.find_element(By.CSS_SELECTOR, "#nav > ul > li:first-child") # 按 XPath driver.find_element(By.XPATH, "//input[@name='email']")
2. 查找多个元素
# 返回列表
links = driver.find_elements(By.TAG_NAME, "a")
for link in links:
print(link.text, link.get_attribute("href"))
# 存在性判断
if driver.find_elements(By.ID, "error-msg"):
print("页面有错误提示")
3. XPath 常用语法
//div 所有 div 元素 //div[@class='a'] 按属性 //div[contains(text(), '登录')] 文本包含 //div[@id='a']/span 子元素 //input[starts-with(@name, 'user')] //a[@href and @title] 多条件 //ul/li[position()=3] 第3个 li //span[normalize-space()='确定'] 文本精确匹配
4. CSS 选择器常用语法
#id ID .class 类 input[name=x] 属性 div > p 子元素 ul li 后代 a:hover 伪类 input[type=text] 属性选择
5. 定位失败处理
from selenium.common.exceptions import NoSuchElementException
try:
el = driver.find_element(By.ID, "not-exist")
except NoSuchElementException:
print("元素不存在,降级处理")
# 元素不存在返回空列表(不抛异常)
els = driver.find_elements(By.ID, "not-exist")
if not els:
print("无此元素")
Selenium 元素操作
Selenium 元素操作
1. 基本操作
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com/login")
# 输入文本
username = driver.find_element(By.ID, "username")
username.send_keys("admin")
# 清除输入
username.clear()
# 点击
btn = driver.find_element(By.ID, "login-btn")
btn.click()
# 提交表单
form = driver.find_element(By.TAG_NAME, "form")
form.submit()
2. 获取信息
el = driver.find_element(By.CLASS_NAME, "card")
el.text # 元素文本
el.get_attribute("href") # 属性值
el.get_attribute("value") # 输入框值
el.get_attribute("class")
el.is_displayed() # 是否可见
el.is_enabled() # 是否可用
el.is_selected() # 是否选中(checkbox/radio)
el.size # 尺寸 {"width":..,"height":..}
el.location # 坐标
3. 下拉框操作(Select)
from selenium.webdriver.support.ui import Select
select = Select(driver.find_element(By.ID, "city"))
select.select_by_index(1) # 按索引
select.select_by_value("beijing") # 按 value
select.select_by_visible_text("北京") # 按可见文本
select.deselect_all() # 取消所有(多选)
# 获取所有选项
options = select.options
for opt in options:
print(opt.text, opt.get_attribute("value"))
4. 复选框与单选
# 勾选
checkbox = driver.find_element(By.ID, "agree")
if not checkbox.is_selected():
checkbox.click()
# 单选按钮
radio = driver.find_element(By.XPATH, "//input[@value='male']")
radio.click()
5. JS 执行(高级)
# 执行 JavaScript
driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
# 滚动到元素
el = driver.find_element(By.ID, "target")
driver.execute_script("arguments[0].scrollIntoView();", el)
# 修改属性
driver.execute_script("arguments[0].value = '设置值';", input_el)
Selenium 等待机制
Selenium 等待机制
页面元素动态加载,需要等待机制保证元素可用。等待机制是自动化稳定性的关键。
1. 强制等待(不推荐)
import time time.sleep(3) # 固定等 3 秒,慢且脆弱
2. 隐式等待(Implicit Wait)
from selenium import webdriver
driver = webdriver.Chrome()
# 全局等待:查找元素时最多等 10 秒
driver.implicitly_wait(10)
# 之后所有 find_element 都会等待元素出现
driver.get("https://example.com")
el = driver.find_element(By.ID, "dynamic-content")
3. 显式等待(Explicit Wait,推荐)
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com")
# 等待元素可点击,最多 10 秒
element = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.ID, "submit-btn"))
)
element.click()
# 等待元素可见
WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.CLASS_NAME, "loading"))
)
# 等待元素消失
WebDriverWait(driver, 10).until_not(
EC.visibility_of_element_located((By.CLASS_NAME, "loading"))
)
# 等待文本出现
WebDriverWait(driver, 10).until(
EC.text_to_be_present_in_element((By.ID, "msg"), "成功")
)
4. 常用 Expected Conditions
EC.presence_of_element_located 元素出现在 DOM EC.visibility_of_element_located 元素可见 EC.element_to_be_clickable 元素可点击 EC.text_to_be_present_in_element 文本出现 EC.alert_is_present 弹窗出现 EC.frame_to_be_available_and_switch_to_it iframe 可切换 EC.number_of_windows_to_be 窗口数量变化 EC.staleness_of_element 元素被移除
5. 自定义等待条件
from selenium.webdriver.support.ui import WebDriverWait
# 自定义条件:等待元素文本等于某值
def text_is(driver, element_id, expected):
el = driver.find_element(By.ID, element_id)
return el.text == expected
WebDriverWait(driver, 10).until(
lambda d: text_is(d, "status", "完成")
)
6. 等待策略建议
- 页面加载用隐式等待兜底
- 关键交互用显式等待(指定元素条件)
- 避免 sleep 固定等待
- 等待超时捕获 TimeoutException 处理
Selenium 浏览器操作
Selenium 浏览器操作
1. 导航与标签
from selenium import webdriver
driver = webdriver.Chrome()
driver.get("https://example.com")
# 后退 / 前进 / 刷新
driver.back()
driver.forward()
driver.refresh()
# 打开新标签页并切换
driver.switch_to.new_window("tab")
driver.get("https://another.com")
# 关闭当前标签页
driver.close()
# 切回第一个标签
driver.switch_to.window(driver.window_handles[0])
2. 窗口管理
# 最大化 driver.maximize_window() # 全屏 driver.fullscreen_window() # 设置尺寸 driver.set_window_size(1280, 800) # 获取/设置位置 driver.get_window_position() driver.set_window_position(100, 100) # 获取所有窗口句柄 print(driver.window_handles)
3. Cookie 管理
# 获取所有 cookie
cookies = driver.get_cookies()
for c in cookies:
print(c["name"], c["value"])
# 添加 cookie(登录态)
driver.add_cookie({"name": "session_id", "value": "abc123"})
# 删除
driver.delete_cookie("session_id")
driver.delete_all_cookies()
4. 弹窗处理
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 触发弹窗
driver.find_element(By.ID, "show-alert").click()
# 切换并处理
alert = WebDriverWait(driver, 5).until(EC.alert_is_present())
print("弹窗文本:", alert.text)
alert.accept() # 确认
# alert.dismiss() # 取消
# alert.send_keys("text") # 输入(仅 prompt)
5. 页面截图
# 整页截图
driver.save_screenshot("screenshot.png")
# 元素截图
el = driver.find_element(By.CLASS_NAME, "card")
el.screenshot("element.png")
# 带尺寸截图
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--window-size=1920,1080")
driver = webdriver.Chrome(options=options)
driver.save_screenshot("full.png")
6. 下载文件配置
options = Options()
prefs = {
"download.default_directory": "/path/to/downloads",
"download.prompt_for_download": False
}
options.add_experimental_option("prefs", prefs)
driver = webdriver.Chrome(options=options)
Selenium 鼠标和键盘操作
Selenium 鼠标和键盘操作
1. ActionChains 鼠标操作
from selenium.webdriver.common.action_chains import ActionChains
driver = webdriver.Chrome()
driver.get("https://example.com")
el = driver.find_element(By.ID, "target")
actions = ActionChains(driver)
# 悬停
actions.move_to_element(el).perform()
# 双击
actions.double_click(el).perform()
# 右键
actions.context_click(el).perform()
# 拖拽(拖到目标)
source = driver.find_element(By.ID, "drag-source")
target = driver.find_element(By.ID, "drop-target")
actions.drag_and_drop(source, target).perform()
# 按住不放
actions.click_and_hold(el).perform()
# 链式操作
actions.move_to_element(el).pause(1).double_click().perform()
2. 键盘操作
from selenium.webdriver.common.keys import Keys
input_box = driver.find_element(By.ID, "search")
# 输入
input_box.send_keys("python")
# 回车
input_box.send_keys(Keys.ENTER)
# 组合键 Ctrl+A / Ctrl+C / Ctrl+V
input_box.send_keys(Keys.CONTROL, "a")
input_box.send_keys(Keys.CONTROL, "c")
input_box.send_keys(Keys.CONTROL, "v")
# 常用键
Keys.BACK_SPACE # 退格
Keys.TAB # Tab
Keys.ESCAPE # Esc
Keys.SPACE # 空格
Keys.ARROW_DOWN # 下箭头
Keys.F5 # 刷新
Keys.HOME # Home
3. 滚动操作
# 滚动到元素
el = driver.find_element(By.ID, "bottom")
actions = ActionChains(driver)
actions.move_to_element(el).perform()
# JS 滚动
driver.execute_script("window.scrollTo(0, 500);")
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
# 键盘滚动
driver.find_element(By.TAG_NAME, "body").send_keys(Keys.END)
driver.find_element(By.TAG_NAME, "body").send_keys(Keys.HOME)
4. 文件上传
# input[type=file] 直接 send_keys 路径
upload = driver.find_element(By.ID, "file-upload")
upload.send_keys("/path/to/file.txt")
# 拖拽上传(需要 JS 辅助,复杂场景)
# 用 AutoIT / pywinauto 处理 Windows 系统弹窗
5. 滑动验证码处理思路
# 拖拽滑块 slider = driver.find_element(By.ID, "slider") btn = driver.find_element(By.ID, "slider-btn") actions = ActionChains(driver) actions.click_and_hold(btn).move_by_offset(300, 0).release().perform()
Selenium 文件上传和下载
Selenium 文件上传和下载
1. 文件上传
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com/upload")
# 方式一:input[type=file] 直接 send_keys(最常用)
upload_input = driver.find_element(By.ID, "file")
upload_input.send_keys("/absolute/path/to/file.pdf")
# 方式二:多个文件
upload_input.send_keys("/path/a.txt", "/path/b.txt")
# 方式三:非 input 元素的拖拽上传(需要 JS)
driver.execute_script("""
var input = document.createElement('input');
input.type = 'file';
document.body.appendChild(input);
""")
2. 上传验证
# 等待上传完成
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.CLASS_NAME, "upload-success"))
)
print("上传成功提示:", driver.find_element(By.CLASS_NAME, "upload-success").text)
3. 文件下载
from selenium.webdriver.chrome.options import Options
options = Options()
prefs = {
"download.default_directory": "/path/to/downloads", # 下载目录
"download.prompt_for_download": False, # 不弹确认
"download.directory_upgrade": True,
"safebrowsing.enabled": True
}
options.add_experimental_option("prefs", prefs)
driver = webdriver.Chrome(options=options)
# 点击下载按钮
driver.get("https://example.com/files")
driver.find_element(By.ID, "download-btn").click()
# 等待文件出现
import os, time
download_dir = "/path/to/downloads"
for _ in range(30): # 最多等 30 秒
files = os.listdir(download_dir)
if files and not any(f.endswith(".crdownload") for f in files):
print("下载完成:", files)
break
time.sleep(1)
4. 下载验证与清理
import glob
# 检查文件大小大于 0
downloaded = glob.glob("/path/to/downloads/*.pdf")
if downloaded and os.path.getsize(downloaded[0]) > 0:
print("文件有效:", downloaded[0])
# 清理
for f in downloaded:
os.remove(f)
Selenium Grid
Selenium Grid
1. 什么是 Selenium Grid
Selenium Grid 用于分布式测试:一个 Hub 管理多个 Node,把测试任务分发到不同机器、不同浏览器上并行执行,大幅缩短测试时间。
2. 架构
┌──────────┐ ┌─────────┐
│ Test 代码 ├─────►│ Hub │
└──────────┘ └────┬────┘
│ 分发
┌──────────────┼──────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Node 1 │ │ Node 2 │ │ Node 3 │
│ Chrome │ │ Firefox │ │ Edge │
└─────────┘ └─────────┘ └─────────┘
3. 启动 Grid(Selenium 4)
# 下载 selenium-server jar # https://github.com/SeleniumHQ/selenium/releases # 启动 Hub java -jar selenium-server-4.18.0.jar hub # 启动 Node(注册到 Hub) java -jar selenium-server-4.18.0.jar node --hub http://localhost:4444
4. 配置 Node 浏览器
# 指定 Node 支持的浏览器和并发数 java -jar selenium-server-4.18.0.jar node --hub http://localhost:4444 --detect-drivers true --max-sessions 5 # 查看 Grid 状态 # http://localhost:4444/ui
5. 远程执行测试
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.set_capability("browserName", "chrome")
options.set_capability("platformName", "linux")
driver = webdriver.Remote(
command_executor="http://localhost:4444/wd/hub",
options=options
)
driver.get("https://example.com")
print(driver.title)
driver.quit()
6. 并行测试(pytest)
# 安装 pytest-xdist
# pip install pytest-xdist
# 多进程运行测试
pytest test_cases.py -n 4
# 测试代码中连接 Grid
import pytest
from selenium import webdriver
@pytest.fixture
def driver():
options = webdriver.ChromeOptions()
d = webdriver.Remote(
command_executor="http://localhost:4444/wd/hub",
options=options
)
yield d
d.quit()
Selenium 测试框架集成
Selenium 测试框架集成
1. pytest 集成(Python)
# 安装
# pip install pytest
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
@pytest.fixture
def driver():
driver = webdriver.Chrome()
driver.implicitly_wait(10)
yield driver
driver.quit()
def test_login(driver):
driver.get("https://example.com/login")
driver.find_element(By.ID, "username").send_keys("admin")
driver.find_element(By.ID, "password").send_keys("123456")
driver.find_element(By.ID, "submit").click()
assert "欢迎" in driver.page_source
2. 参数化测试
@pytest.mark.parametrize("user, pwd, expected", [
("admin", "123456", "成功"),
("user1", "wrong", "密码错误"),
("", "123456", "用户名不能为空"),
])
def test_login_cases(driver, user, pwd, expected):
driver.get("https://example.com/login")
driver.find_element(By.ID, "username").send_keys(user)
driver.find_element(By.ID, "password").send_keys(pwd)
driver.find_element(By.ID, "submit").click()
assert expected in driver.page_source
3. TestNG 集成(Java)
import org.testng.annotations.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
public class LoginTest {
WebDriver driver;
@BeforeMethod
public void setup() {
driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
}
@Test
public void testLogin() {
driver.get("https://example.com/login");
driver.findElement(By.id("username")).sendKeys("admin");
driver.findElement(By.id("password")).sendKeys("123456");
driver.findElement(By.id("submit")).click();
assertTrue(driver.getPageSource().contains("欢迎"));
}
@AfterMethod
public void teardown() {
driver.quit();
}
}
4. 数据驱动(读取 CSV/JSON)
import json, csv
# CSV 数据源
def load_csv(path):
with open(path) as f:
return list(csv.DictReader(f))
@pytest.mark.parametrize("data", load_csv("cases.csv"))
def test_from_csv(driver, data):
assert data["expected"] in login(driver, data["user"], data["pwd"])
5. 报告与截图
# pytest 报告
# pip install pytest-html
# pytest --html=report.html
import time
def test_failure_screenshot(driver):
try:
# 测试步骤
pass
except Exception:
name = f"fail_{time.strftime('%Y%m%d_%H%M%S')}.png"
driver.save_screenshot(name)
raise
Selenium 高级功能
Selenium 高级功能
1. 网络请求拦截
# Chrome DevTools Protocol(CDP)
from selenium.webdriver.common.devtools.v119.network import RequestWillBeSent
# Selenium 4 支持网络监听
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
driver = webdriver.Chrome(options=options)
# 使用 CDP 监听请求
driver.execute_cdp_cmd("Network.enable", {})
# 模拟慢网速
driver.execute_cdp_cmd("Network.emulateNetworkConditions", {
"offline": False,
"latency": 300,
"downloadThroughput": 100 * 1024,
"uploadThroughput": 50 * 1024
})
2. 性能监控
# 页面加载性能
import time
start = time.time()
driver.get("https://example.com")
load_time = time.time() - start
print(f"页面加载: {load_time:.2f} 秒")
# JS 执行时间
nav_start = driver.execute_script("return performance.timing.navigationStart")
load_end = driver.execute_script("return performance.timing.loadEventEnd")
print(f"性能指标: {(load_end - nav_start) / 1000:.2f} 秒")
3. 页面对象模式(POM)
# 页面对象:封装页面元素与操作,提高可维护性
class LoginPage:
def __init__(self, driver):
self.driver = driver
self.username = (By.ID, "username")
self.password = (By.ID, "password")
self.submit = (By.ID, "submit")
def open(self):
self.driver.get("https://example.com/login")
return self
def login(self, user, pwd):
self.driver.find_element(*self.username).send_keys(user)
self.driver.find_element(*self.password).send_keys(pwd)
self.driver.find_element(*self.submit).click()
return self
# 使用
page = LoginPage(driver).open().login("admin", "123456")
4. 保存登录态复用
import json, pickle
# 保存 cookies
def save_cookies(driver, path="cookies.pkl"):
with open(path, "wb") as f:
pickle.dump(driver.get_cookies(), f)
# 恢复 cookies
def load_cookies(driver, path="cookies.pkl"):
with open(path, "rb") as f:
cookies = pickle.load(f)
for c in cookies:
driver.add_cookie(c)
# 使用:登录一次,之后直接恢复
driver.get("https://example.com")
load_cookies(driver)
driver.refresh()
# 已保持登录状态
5. 数据采集最佳实践
# 控制频率与请求头
options = Options()
options.add_argument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64)")
driver = webdriver.Chrome(options=options)
# 设置代理
options.add_argument("--proxy-server=http://127.0.0.1:7890")
# 等待与重试
from tenacity import retry, stop_after_attempt
@retry(stop=stop_after_attempt(3))
def safe_find(driver, by, value):
return driver.find_element(by, value)
Selenium 无头浏览器模式
Selenium 无头浏览器模式
1. 什么是无头模式
无头(headless)模式指浏览器在后台运行、不显示界面,适合服务器环境、CI/CD 管道和批量任务,资源占用小、速度快。
2. Chrome 无头模式
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--headless=new") # 新版无头模式
options.add_argument("--no-sandbox") # Linux root 环境
options.add_argument("--disable-dev-shm-usage") # 容器环境
options.add_argument("--disable-gpu")
options.add_argument("--window-size=1920,1080")
driver = webdriver.Chrome(options=options)
driver.get("https://example.com")
print("标题:", driver.title)
driver.save_screenshot("headless.png")
driver.quit()
3. Firefox 无头模式
from selenium.webdriver.firefox.options import Options
options = Options()
options.add_argument("-headless")
driver = webdriver.Firefox(options=options)
driver.get("https://example.com")
driver.quit()
4. 无头模式注意点
- 无头模式下部分网站会检测并限制
- JS 渲染仍需等待机制
- 截图前确保窗口尺寸正确
- Linux 服务器需安装浏览器依赖库
5. 反检测配置(尽量避免滥用)
options = Options()
options.add_argument("--headless=new")
options.add_argument("--disable-blink-features=AutomationControlled")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
driver = webdriver.Chrome(options=options)
# 移除 webdriver 标记
driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")
6. CI/CD 集成示例
# GitHub Actions 中的无头测试
# .github/workflows/test.yml
name: UI Test
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: pip install selenium pytest
- run: |
# 安装 Chrome
wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
sudo sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list'
sudo apt-get update && sudo apt-get install -y google-chrome-stable
- run: pytest tests/ -m ui
Selenium 实战项目
Selenium 实战项目
1. 项目:登录-搜索-验证全流程
import pytest
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
class SearchFlowTest:
@pytest.fixture
def driver(self):
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
d = webdriver.Chrome(options=options)
d.implicitly_wait(10)
yield d
d.quit()
def test_search_flow(self, driver):
# 1. 打开首页
driver.get("https://www.baidu.com")
assert "百度" in driver.title
# 2. 输入关键词搜索
box = driver.find_element(By.ID, "kw")
box.send_keys("Selenium 教程")
driver.find_element(By.ID, "su").click()
# 3. 等待结果出现
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CLASS_NAME, "result"))
)
# 4. 验证结果
results = driver.find_elements(By.CLASS_NAME, "result")
assert len(results) > 0
first_text = results[0].text
assert "Selenium" in first_text or "selenium" in first_text.lower()
# 5. 截图
driver.save_screenshot("search_result.png")
2. 项目:表单自动化填写
def test_form_fill(driver):
driver.get("https://example.com/register")
fields = {
"username": "testuser",
"email": "test@example.com",
"phone": "13800001111"
}
for field_id, value in fields.items():
driver.find_element(By.ID, field_id).send_keys(value)
# 下拉选择
from selenium.webdriver.support.ui import Select
Select(driver.find_element(By.ID, "city")).select_by_visible_text("北京")
# 勾选协议
checkbox = driver.find_element(By.ID, "agree")
if not checkbox.is_selected():
checkbox.click()
# 提交
driver.find_element(By.ID, "submit").click()
# 验证成功
success = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.CLASS_NAME, "success-msg"))
)
assert "注册成功" in success.text
3. 项目:数据采集
def test_scrape_list(driver):
driver.get("https://example.com/products")
# 滚动加载全部
last_height = driver.execute_script("return document.body.scrollHeight")
while True:
driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
time.sleep(1)
new_height = driver.execute_script("return document.body.scrollHeight")
if new_height == last_height:
break
last_height = new_height
# 采集数据
items = driver.find_elements(By.CLASS_NAME, "product-item")
data = []
for item in items:
data.append({
"name": item.find_element(By.CLASS_NAME, "name").text,
"price": item.find_element(By.CLASS_NAME, "price").text,
})
# 保存 CSV
import csv
with open("products.csv", "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=["name", "price"])
writer.writeheader()
writer.writerows(data)
print(f"采集 {len(data)} 条数据")
4. 项目:监控与告警
def test_monitor(driver):
driver.get("https://example.com/status")
status = driver.find_element(By.ID, "service-status").text
if "异常" in status:
# 发送告警(邮件/钉钉/企业微信 webhook)
import requests
requests.post("https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx",
json={"msgtype": "text", "text": {"content": f"服务异常: {status}"}})
driver.save_screenshot("alert.png")
assert False, f"服务状态异常: {status}"
print("服务正常:", status)
5. 项目最佳实践总结
- 使用页面对象模式(POM)组织代码
- 显式等待代替固定 sleep
- 测试数据与用例分离(CSV/JSON/YAML)
- 失败自动截图并保留日志
- 接入 CI 无头模式执行
- 定期运行回归,关注元素变更
第 1 / 15 节