1. 项目概述:为什么Selenium依然是自动化测试的基石

如果你是一名测试工程师、开发人员,或者正在学习软件测试,那么“Selenium”这个名字你一定不陌生。它几乎是Web自动化测试的代名词,尽管近年来涌现了像Playwright、Cypress这样的新秀,但Selenium凭借其开源、跨浏览器、支持多语言的特性,依然牢牢占据着企业级自动化测试的核心地位。我接触Selenium超过八年,从最初的录制回放,到后来搭建复杂的分布式测试框架,它始终是项目中最可靠的那一环。今天,我们不谈那些高深莫测的框架设计,就从一个实战者的角度,拆解Selenium最核心、最基础的使用方法。无论你是想快速上手写几个脚本,还是为面试做准备,理解这些基础,远比盲目追求新工具更重要。

简单来说,Selenium是一个用于Web应用程序自动化测试的工具集。它的核心价值在于模拟真实用户的操作,比如点击按钮、输入文本、下拉选择等,从而解放人力,实现回归测试的自动化。它支持Java、Python、C#、JavaScript等多种主流编程语言,这意味着你的开发团队可以用最熟悉的语言来编写测试脚本。更重要的是,它通过WebDriver协议与各种浏览器(Chrome、Firefox、Edge、Safari等)直接通信,实现了对浏览器最底层的控制,这使得它的模拟行为非常真实和可靠。接下来,我将带你从环境搭建开始,一步步深入到元素定位、常见操作、等待机制等核心环节,并分享我这些年积累的实操心得和避坑指南。

2. 环境搭建与核心组件解析

2.1 语言与驱动选择:找到你的最佳拍档

开始使用Selenium的第一步,不是急着写代码,而是搭建一个稳定可用的环境。这里主要有三个核心组件需要准备:编程语言及Selenium库、浏览器、以及对应的浏览器驱动(WebDriver)。

编程语言选择 :Selenium支持多语言,但Python和Java是社区最活跃、资源最丰富的两种。对于测试入门和快速开发,我强烈推荐Python。它的语法简洁,库生态丰富(如 pytest 用于测试组织),能让你更专注于自动化逻辑本身,而非语言细节。对于大型企业级项目,特别是与Java技术栈深度集成的环境,Java则是更稳妥的选择。本文将以Python为例进行演示,因为其上手速度最快,也最能体现Selenium的易用性。

安装Selenium库 :使用Python的包管理工具pip,一行命令即可完成安装。这里有一个关键细节:建议指定一个相对稳定的版本,而不是永远使用最新的 selenium 。因为最新版可能会引入未预见的兼容性问题。

pip install selenium==4.15.0

浏览器与WebDriver :Selenium通过WebDriver来控制浏览器。你需要为你要测试的浏览器下载对应的驱动程序。

  • Chrome/Edge :下载ChromeDriver。这里有个非常重要的技巧: 驱动版本必须与你的浏览器大版本号一致 。你可以在Chrome浏览器的“设置”->“关于Chrome”中查看版本号,然后去ChromeDriver官网或国内镜像站下载对应版本。
  • Firefox :下载geckodriver。
  • 下载后,将驱动文件(如 chromedriver.exe )放在一个目录下,并将该目录路径添加到系统的环境变量 PATH 中。这是最规范的做法。当然,你也可以在代码中指定驱动的绝对路径,但这不利于脚本的跨环境移植。

注意:很多新手遇到的第一个“坑”就是驱动版本不匹配导致的报错,错误信息通常是“This version of ChromeDriver only supports Chrome version XXX”。务必确保版本对应。

2.2 你的第一个自动化脚本:从“Hello World”开始

环境就绪后,我们来写一个最简单的脚本,打开百度首页并搜索一个关键词。这个脚本将串联起Selenium最基础的几个操作。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time

# 1. 创建WebDriver实例,启动Chrome浏览器
# 如果chromedriver已在PATH中,可以直接这样写
driver = webdriver.Chrome()
# 如果未在PATH中,则需要指定路径
# driver = webdriver.Chrome(executable_path=r‘C:\path\to\chromedriver.exe’)

try:
    # 2. 打开目标网址
    driver.get(“https://www.baidu.com”)
    # 等待一下,确保页面加载完成(初级做法,后面会讲更好的方式)
    time.sleep(2)

    # 3. 定位搜索框,并输入内容
    # 通过元素的‘id’属性定位,这是最高效的方式之一
    search_box = driver.find_element(By.ID, “kw”)
    search_box.send_keys(“Selenium自动化测试”) # 输入文本
    search_box.send_keys(Keys.RETURN) # 模拟键盘回车键进行搜索

    # 4. 等待搜索结果加载
    time.sleep(3)

    # 5. 验证结果(简单示例:打印页面标题)
    print(“当前页面标题是:”, driver.title)
    # 可以进一步定位搜索结果元素进行断言,这里先省略

finally:
    # 6. 关闭浏览器
    # 使用quit()而不是close(),quit会关闭整个浏览器进程和驱动,更干净
    driver.quit()

运行这段代码,你会看到Chrome浏览器自动打开,访问百度,输入关键词并搜索,最后在控制台打印出标题后关闭。这就是自动化测试的魔力开端。这里有几个关键点需要理解:

  1. webdriver.Chrome() :这个调用会启动一个全新的、干净的浏览器实例(通常是无头模式,但默认是有界面的)。它不同于你手动打开的浏览器。
  2. find_element(By.ID, “kw”) :这是Selenium最核心的功能之一—— 元素定位 。我们必须先告诉Selenium要操作页面上的哪个元素。 By.ID 表示使用HTML元素的 id 属性来定位, “kw” 就是百度搜索框的id值。
  3. send_keys() :用于向输入框等元素模拟键盘输入。
  4. driver.quit() : 务必在脚本最后调用 ,以释放浏览器进程和驱动资源。我习惯用 try...finally 结构确保即使脚本中途出错,浏览器也能被正确关闭,避免残留进程占用内存。

3. 核心技能一:八种元素定位策略详解

元素定位是Selenium自动化脚本的基石,找不到元素,一切操作都无从谈起。Selenium提供了8种主要的定位策略,我将它们分为 高优先级 和 备用方案 两类。

3.1 高优先级定位策略(首选)

1. ID定位 ( By.ID ) :通过HTML元素的 id 属性定位。 id 在理想情况下应该是页面内唯一的,因此定位速度最快、最准确。 如果元素有稳定且唯一的id,请无条件首选它。

element = driver.find_element(By.ID, “username”)

2. CSS选择器定位 ( By.CSS_SELECTOR ) :这是功能最强大、最灵活的定位方式,也是我个人的最爱。它可以通过元素标签、类、属性、层级关系等进行组合定位。

  • 通过类名: find_element(By.CSS_SELECTOR, “.submit-btn”) (注意类名前的点)
  • 通过属性: find_element(By.CSS_SELECTOR, “input[name=‘email’]”)
  • 通过层级: find_element(By.CSS_SELECTOR, “#form1 .input-group > input”) CSS选择器语法丰富,能应对绝大多数复杂场景,且在现代浏览器中解析速度非常快。

3. XPath定位 ( By.XPATH ) :与CSS选择器类似强大,通过XML路径语言来定位元素。它在处理没有明显特征(如id、class)的元素时非常有用,可以通过文本内容、索引位置等定位。

  • 绝对路径(不推荐,易变): /html/body/div[1]/form/input[2]
  • 相对路径+属性: //input[@placeholder=‘请输入手机号’]
  • 包含文本: //button[contains(text(), ‘登录’)]

注意:XPath引擎遍历节点,在极端复杂的页面上可能比CSS选择器稍慢,且写出的表达式可能较长。但在处理动态ID或复杂结构时,它往往是“救命稻草”。

3.2 备用定位策略(酌情使用)

4. Name定位 ( By.NAME ) :通过 name 属性定位。在表单元素中比较常见,但并非唯一。 5. Class Name定位 ( By.CLASS_NAME ) :通过 class 属性定位。但一个元素的 class 往往有多个值(如 “btn btn-primary” ),且同类元素在页面中很多,容易定位到多个元素。 6. Tag Name定位 ( By.TAG_NAME ) :通过标签名定位,如 “input” , “a” 。通常一个页面有大量相同标签,所以一般用于查找元素集合,或与其他定位方式结合。 7. 链接文本 ( By.LINK_TEXT ) / 部分链接文本 ( By.PARTIAL_LINK_TEXT ) :专门用于定位超链接( <a> 标签)。 LINK_TEXT 需要完全匹配链接文本, PARTIAL_LINK_TEXT 可以匹配部分文本。

# 精确匹配
driver.find_element(By.LINK_TEXT, “忘记密码?”)
# 模糊匹配
driver.find_element(By.PARTIAL_LINK_TEXT, “密码”)

8. 定位多个元素 ( find_elements ) :以上所有方法都有一个对应的复数形式 find_elements (注意末尾的 s )。它返回一个匹配到的元素列表(WebElement列表),如果没找到则返回空列表,而不会抛出异常。这在处理表格、列表项或不确定数量的元素时非常有用。

all_buttons = driver.find_elements(By.CSS_SELECTOR, “button.btn”)
print(f“找到了 {len(all_buttons)} 个按钮”)
for btn in all_buttons:
    print(btn.text)

实操心得 :

  • 定位策略优先级 :ID > CSS Selector > XPath > 其他。优先使用有语义化的ID和CSS选择器。
  • 如何获取定位表达式 :不要手写!利用浏览器的开发者工具(F12)。在元素上右键 -> “检查”,然后在元素代码上右键,可以选择“Copy” -> “Copy selector”(CSS)或“Copy XPath”。但 不要完全依赖复制的值 ,它们可能生成过长或脆弱的表达式(尤其是绝对路径的XPath)。你应该基于复制的表达式进行简化优化。
  • 验证定位器 :在开发者工具的Console面板中,可以使用 $$(“你的CSS选择器”) 或 $x(“你的XPath”) 来快速测试定位表达式是否能找到元素。

4. 核心技能二:浏览器操作与元素交互

找到元素后,下一步就是与之交互。Selenium提供了丰富的API来模拟用户的所有操作。

4.1 基础元素操作

点击与输入 :这是最常用的操作。

# 点击元素
login_button = driver.find_element(By.ID, “login-btn”)
login_button.click()

# 清空输入框后输入
search_input = driver.find_element(By.NAME, “q”)
search_input.clear() # 先清空已有内容
search_input.send_keys(“新的搜索词”)

# 模拟特殊键,如回车、Tab、Ctrl+A等
from selenium.webdriver.common.keys import Keys
search_input.send_keys(Keys.ENTER)
# 组合键,如全选 (Ctrl+A)
search_input.send_keys(Keys.CONTROL, ‘a’)

获取元素状态与信息 :在自动化测试中,我们经常需要获取元素的信息来进行断言(验证)。

element = driver.find_element(By.ID, “some-element”)
# 获取文本内容(对于可见文本)
text = element.text
print(text)

# 获取属性值
attr_value = element.get_attribute(“href”) # 获取链接地址
attr_value = element.get_attribute(“value”) # 获取输入框的值
attr_value = element.get_attribute(“class”) # 获取类名

# 判断元素是否可见、是否可用、是否被选中(针对复选框/单选框)
is_displayed = element.is_displayed()
is_enabled = element.is_enabled()
is_selected = element.is_selected() # 用于 checkbox 或 radio

# 获取元素的CSS属性值
color = element.value_of_css_property(“color”)

4.2 浏览器窗口与导航操作

自动化脚本需要控制浏览器本身。

# 导航
driver.get(“https://www.example.com”) # 打开新页面
driver.back() # 后退
driver.forward() # 前进
driver.refresh() # 刷新

# 窗口操作
current_window = driver.current_window_handle # 获取当前窗口句柄
all_windows = driver.window_handles # 获取所有窗口句柄
driver.switch_to.window(all_windows[-1]) # 切换到最新打开的窗口

# 弹窗处理 (Alert/Confirm/Prompt)
alert = driver.switch_to.alert # 切换到弹窗
print(alert.text) # 获取弹窗文本
alert.accept() # 点击“确定”
# alert.dismiss() # 点击“取消”
# alert.send_keys(“输入内容”) # 向Prompt弹窗输入

# 框架/iframe切换
driver.switch_to.frame(“frame_name_or_id”) # 通过name或id切换
driver.switch_to.frame(driver.find_element(By.TAG_NAME, “iframe”)) # 通过元素切换
driver.switch_to.default_content() # 切回主文档
driver.switch_to.parent_frame() # 切回父框架

4.3 鼠标与键盘高级操作

对于更复杂的交互,如悬停、拖拽、右键菜单等,需要用到 ActionChains 类。

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

element = driver.find_element(By.ID, “menu”)
target = driver.find_element(By.ID, “target”)

actions = ActionChains(driver)

# 鼠标悬停
actions.move_to_element(element).perform()

# 拖放元素
actions.drag_and_drop(element, target).perform()

# 右键点击
actions.context_click(element).perform()

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

# 复杂的键盘鼠标组合操作(例如,按住Shift键点击)
actions.key_down(Keys.SHIFT).click(element).key_up(Keys.SHIFT).perform()

ActionChains 的原理是将一系列操作存储在一个队列中,调用 perform() 时才会按顺序执行。这允许你构建非常复杂的用户交互序列。

5. 核心技能三:三种等待机制的艺术

自动化测试中最常见也最令人头疼的问题之一就是“元素找不到”或“元素状态不对”。这十有八九是因为 页面尚未加载完成或元素尚未变得可交互 。Selenium提供了三种等待机制来解决这个问题,理解并正确使用它们是编写稳定脚本的关键。

5.1 强制等待 ( time.sleep ):不得已而为之

这是最原始的方法,让脚本无条件暂停指定的秒数。

import time
time.sleep(5) # 强制等待5秒

缺点 :效率极低。无论页面是否已就绪,都必须等待固定时间。如果网络慢,5秒可能不够;如果网络快,又会白白浪费时间。 仅在调试脚本或处理极特殊、无法用其他等待方式解决的场景时使用。

5.2 隐式等待 ( implicitly_wait ):设置全局超时

隐式等待告诉WebDriver:在查找任何一个元素时,如果元素没有立即出现,就等待一段固定的时间,直到元素出现或超时。

driver.implicitly_wait(10) # 单位为秒

设置后,对后续所有的 find_element 和 find_elements 调用都生效。如果在10秒内找到元素,则立即返回;如果超时仍未找到,则抛出 NoSuchElementException 。 优点 :设置一次,全局生效,代码简洁。 缺点 :

  1. 它只对“查找元素”这一操作有效,对元素的“可点击”、“可见”等状态无效。
  2. 它是全局设置,可能会在某些不需要等待的地方产生不必要的延迟。
  3. 与显式等待混用时可能导致意想不到的超时行为(总等待时间可能是两者之和)。

5.3 显式等待 ( WebDriverWait ):推荐的最佳实践

显式等待是针对某个特定条件进行等待,条件满足则立即继续,超时则抛出异常。它提供了最大的灵活性和精确控制。这是我最推荐的方式。

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

# 创建一个WebDriverWait对象,设置最大等待时间10秒,轮询间隔0.5秒(默认)
wait = WebDriverWait(driver, 10)

# 等待元素出现在DOM中并且可见、可点击
element = wait.until(EC.element_to_be_clickable((By.ID, “submit-button”)))
element.click()

# 等待元素可见
element = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, “.result”)))

# 等待元素包含特定文本
element = wait.until(EC.text_to_be_present_in_element((By.ID, “status”), “成功”))

# 等待页面标题包含特定文字
wait.until(EC.title_contains(“订单详情”))

# 等待旧元素消失(例如,等待加载动画消失)
wait.until(EC.invisibility_of_element_located((By.ID, “loading-spinner”)))

expected_conditions (EC) 模块提供了大量预定义的条件,如 presence_of_element_located (元素存在于DOM)、 visibility_of_element_located (元素可见)、 element_to_be_clickable (元素可点击)等。 优点 :

  • 精准 :只为必要的条件等待。
  • 灵活 :可以自定义等待条件。
  • 清晰 :代码明确表达了在等待什么。

我的等待策略建议 :

  1. 完全避免使用强制等待 在生产脚本中。
  2. 可以设置一个较短的全局隐式等待 (如3-5秒),作为基础保障。
  3. 对于所有关键操作 ,特别是点击、输入前的元素状态确认, 优先使用显式等待 。这是编写健壮、稳定自动化脚本的核心技巧。

6. 实战进阶:处理常见复杂场景

掌握了基础操作和等待机制,你已经可以应对大部分简单页面的自动化了。但在实际项目中,你会遇到各种“拦路虎”。下面分享几个高频复杂场景的处理方案。

6.1 处理动态加载与Ajax内容

现代网页大量使用Ajax技术异步加载内容。一个典型的例子是:点击“加载更多”按钮,新的内容项会动态添加到页面底部,而页面不会刷新。 挑战 :你无法直接定位尚未出现在DOM中的元素。 解决方案 :结合显式等待和查找多个元素。

# 假设每次点击“加载更多”会加载10条新评论
load_more_button = driver.find_element(By.ID, “load-more”)
initial_comments = driver.find_elements(By.CLASS_NAME, “comment-item”)

print(f“初始有 {len(initial_comments)} 条评论”)

# 点击加载更多
load_more_button.click()

# 等待新的评论元素出现。我们等待评论数量增加。
def comments_increased(driver):
    current_comments = driver.find_elements(By.CLASS_NAME, “comment-item”)
    # 如果当前评论数大于之前的数量,条件满足
    return len(current_comments) > len(initial_comments)

wait = WebDriverWait(driver, 10)
wait.until(comments_increased)

final_comments = driver.find_elements(By.CLASS_NAME, “comment-item”)
print(f“加载后共有 {len(final_comments)} 条评论”)

这里的关键是自定义了一个等待条件 comments_increased ,它检查元素列表的长度是否发生了变化。这是一种非常实用的模式。

6.2 处理下拉选择框 ( <select> )

对于原生的HTML <select> 元素,Selenium提供了专门的 Select 类,比模拟点击选项更稳定。

from selenium.webdriver.support.ui import Select

# 定位到<select>元素
select_element = driver.find_element(By.ID, “country-select”)
# 创建Select对象
select = Select(select_element)

# 选择方式有三种:
select.select_by_value(“us”) # 通过option的value属性值选择
select.select_by_visible_text(“美国”) # 通过选项的可见文本选择
select.select_by_index(1) # 通过索引选择(从0开始)

# 获取所有已选选项
all_selected_options = select.all_selected_options
# 获取所有选项
all_options = select.options
# 判断是否是多选
is_multiple = select.is_multiple

对于非原生(用div/ul/li模拟的)精美下拉框,则无法使用 Select 类,需要模拟点击展开,再点击目标选项。

6.3 文件上传

文件上传通常有两种形式:

  1. Input类型为file的元素 :这是最简单的,直接使用 send_keys 传入文件路径即可。

    upload_input = driver.find_element(By.CSS_SELECTOR, “input[type=‘file’]”)
    # 传入文件的绝对路径
    upload_input.send_keys(“/Users/yourname/Desktop/test_image.jpg”)
    

    注意:路径必须是绝对路径,且脚本运行环境(可能是远程服务器)必须有该文件的访问权限。

  2. 通过点击按钮触发的系统文件选择对话框 :Selenium 无法直接操作 系统级别的对话框。这是Selenium的一个限制。常见的解决方案有:

    • 规避 :如果可能,让开发同学在测试环境提供一个直接通过 input[type=‘file’] 上传的入口。
    • 使用AutoIT、Robot类等第三方工具 (不跨平台,且复杂)。
    • 使用浏览器开发者工具 :在测试时,可以手动操作一次,观察网络请求,然后尝试在自动化脚本中直接模拟这个HTTP POST请求(使用 requests 库),绕过前端界面。这需要一定的前后端联调知识。

6.4 截图与日志记录

自动化脚本运行,尤其是无人值守时,截图是排查问题最直观的证据。

from datetime import datetime

# 截取整个浏览器窗口
timestamp = datetime.now().strftime(“%Y%m%d_%H%M%S”)
screenshot_path = f“./screenshot_error_{timestamp}.png”
driver.save_screenshot(screenshot_path)
print(f“截图已保存至:{screenshot_path}”)

# 截取特定元素
element = driver.find_element(By.ID, “error-message”)
element.screenshot(‘./element_screenshot.png’)

良好的日志记录习惯也至关重要。建议使用Python内置的 logging 模块,为脚本添加不同级别的日志输出(INFO, DEBUG, ERROR),便于追踪执行流程和定位问题。

7. 常见问题排查与脚本优化技巧

即使按照最佳实践编写脚本,依然会遇到各种问题。这里记录了我踩过的一些“坑”和解决方法。

7.1 元素定位失败问题排查表

问题现象 可能原因 排查步骤与解决方案
NoSuchElementException (元素不存在) 1. 定位器写错了。
2. 页面尚未加载完成。
3. 元素在iframe/frame内。
4. 元素是动态生成的(Ajax)。
5. 页面有多个匹配元素, find_element 只返回第一个但可能不是你想要的。
1. 在浏览器控制台用 $$() 或 $x() 验证定位器。
2. 添加显式等待 ,等待元素出现。
3. 使用 driver.switch_to.frame() 切换到正确的frame。
4. 等待动态内容加载完成(见6.1)。
5. 使用 find_elements 检查匹配数量,优化定位器使其更精确。
ElementNotInteractableException (元素不可交互) 1. 元素不可见(被遮挡、CSS隐藏)。
2. 元素未处于可操作状态(如disabled)。
3. 有弹窗、蒙层覆盖。
1. 使用 EC.visibility_of... 或 EC.element_to_be_clickable 等待。
2. 检查元素 disabled 属性。
3. 关闭或处理覆盖层。
StaleElementReferenceException (元素过时引用) 1. 之前找到的元素,在操作前页面已经刷新或该部分DOM被重新渲染。
2. 常见于单页应用(SPA)。
这是高级问题 。解决方案是 重新查找元素 。最好在每次操作前即时定位,或使用“Page Object模式”的懒加载方式定位元素。
脚本在本地运行成功,在服务器/CI上失败 1. 浏览器/驱动版本不一致。
2. 服务器无图形界面(headless模式)。
3. 屏幕分辨率、字体渲染差异导致元素位置偏移。
4. 网络环境差异导致加载超时。
1. 统一环境版本,使用Docker容器化。
2. 确保Headless模式配置正确(添加 --headless 参数)。
3. 设置固定的浏览器窗口大小 driver.set_window_size(1920, 1080) 。
4. 增加全局等待时间,优化网络超时设置。

7.2 提升脚本稳定性和可维护性

1. 使用Page Object模式 (PO模式) :这是UI自动化测试的经典设计模式。其核心思想是将页面封装成类,页面的元素定位和操作作为类的方法。测试脚本只调用这些方法,不直接包含定位器。这样做的好处是:

  • 可维护性高 :页面UI变动时,只需修改对应的Page类,无需修改大量测试脚本。
  • 可读性强 :业务逻辑清晰。
  • 减少重复代码 :公共操作可以封装在基类中。

一个简单的Page Object示例:

# login_page.py
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class LoginPage:
    def __init__(self, driver):
        self.driver = driver
        self.wait = WebDriverWait(driver, 10)

    # 定位器
    USERNAME_INPUT = (By.ID, “username”)
    PASSWORD_INPUT = (By.ID, “password”)
    LOGIN_BUTTON = (By.ID, “login-btn”)
    ERROR_MSG = (By.CLASS_NAME, “error-message”)

    # 页面操作方法
    def enter_username(self, username):
        element = self.wait.until(EC.visibility_of_element_located(self.USERNAME_INPUT))
        element.clear()
        element.send_keys(username)

    def enter_password(self, password):
        element = self.wait.until(EC.visibility_of_element_located(self.PASSWORD_INPUT))
        element.clear()
        element.send_keys(password)

    def click_login(self):
        element = self.wait.until(EC.element_to_be_clickable(self.LOGIN_BUTTON))
        element.click()

    def get_error_message(self):
        try:
            element = self.wait.until(EC.visibility_of_element_located(self.ERROR_MSG))
            return element.text
        except:
            return None

# 在测试脚本中使用
def test_login_failure(driver):
    login_page = LoginPage(driver)
    driver.get(“https://example.com/login”)
    login_page.enter_username(“wrong_user”)
    login_page.enter_password(“wrong_pass”)
    login_page.click_login()
    assert “用户名或密码错误” in login_page.get_error_message()

2. 配置浏览器选项,应对反爬与检测 :一些网站会检测Selenium的自动化特征(如 window.navigator.webdriver 属性)。虽然Selenium主要用于测试,但了解如何“隐藏”特征有时对测试某些功能也有必要。

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

options = Options()
# 常用选项
options.add_argument(“--disable-blink-features=AutomationControlled”) # 禁用自动化控制特征
options.add_argument(“--start-maximized”) # 启动时最大化窗口
options.add_argument(“--incognito”) # 无痕模式
# 添加实验性选项,排除收集的启用自动化开关
options.add_experimental_option(“excludeSwitches”, [“enable-automation”])
options.add_experimental_option(‘useAutomationExtension’, False)

# 创建驱动时传入选项
driver = webdriver.Chrome(options=options)
# 执行CDP命令,覆盖navigator.webdriver属性(仅Chrome)
driver.execute_cdp_cmd(“Page.addScriptToEvaluateOnNewDocument”, {
    “source”: “””
        Object.defineProperty(navigator, ‘webdriver’, {
            get: () => undefined
        });
    “””
})

请注意,这些方法可能随着浏览器更新而失效,且用于爬虫可能涉及法律和道德问题,但在测试环境中用于绕过不必要的检测是常见做法。

3. 无头模式运行 :在服务器或CI/CD流水线中,通常没有图形界面。此时需要在无头模式下运行浏览器,节省资源。

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument(“--headless=new”) # Chrome较新版本的无头模式参数
options.add_argument(“--no-sandbox”) # Linux环境下常需要
options.add_argument(“--disable-dev-shm-usage”) # 解决共享内存问题
options.add_argument(“--disable-gpu”) # 某些系统需要

driver = webdriver.Chrome(options=options)

在无头模式下,所有操作都在后台进行,看不到浏览器界面,但截图功能依然可用,这对于调试至关重要。

掌握Selenium的基本使用,就像是学会了一套强大的“浏览器操控术”。从精准的元素定位到灵活的等待策略,从处理复杂交互到编写健壮的Page Object代码,每一步都凝结了实战中的经验与教训。我个人的体会是,自动化测试脚本的稳定性,30%取决于工具本身,70%取决于编写者对页面加载逻辑、异步处理和异常场景的理解与处理。不要满足于让脚本“跑起来”,而要追求让它“在任何时候都能稳定地跑起来”。这需要你像侦探一样,仔细观察页面行为,分析网络请求,并运用合理的等待和重试机制。最后,记住自动化测试的初衷是提升效率,而不是制造维护负担。从简单的核心流程开始,逐步构建你的测试用例库,并配以良好的框架设计和日志报告,才能真正让Selenium成为你项目质量保障的得力助手。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐