3、Selenium基础入门:元素定位八大方法、浏览器操作API、等待机制详解
好,咱们进入第三章。说实话,Selenium 这块内容,是自动化测试的基石。你想想看,如果连页面上的元素都抓不到,后面的操作全是空谈。我个人习惯把这一章叫做「三板斧」——定位、操作、等待。这三样玩熟了,OA 系统的自动化就迈出了最关键的一步。
3.1 元素定位八大方法
定位元素,说白了就是告诉 Selenium:「嘿,去点那个按钮!」。但怎么描述「那个按钮」?Selenium 给了我们八种方式。我刚开始学的时候也觉得多,但用久了你会发现,其实常用的就那么三四种。
先看个总览表格,心里有个谱:
| 定位方法 | 语法示例 | 适用场景 |
|---|---|---|
| id | find_element(By.ID, "username") | 元素有唯一 id 时,首选 |
| name | find_element(By.NAME, "password") | 表单元素常用 |
| class_name | find_element(By.CLASS_NAME, "btn-primary") | 样式类定位 |
| tag_name | find_element(By.TAG_NAME, "input") | 标签名定位,不常用 |
| link_text | find_element(By.LINK_TEXT, "登录") | 精确匹配超链接文本 |
| partial_link_text | find_element(By.PARTIAL_LINK_TEXT, "登") | 模糊匹配超链接 |
| xpath | find_element(By.XPATH, "//input[@id='username']") | 万能定位,但性能稍差 |
| css_selector | find_element(By.CSS_SELECTOR, "#username") | 速度快,推荐使用 |
3.1.1 id 定位——最靠谱的方式
如果开发同学给元素加了 id,那恭喜你,这是最省心的方式。id 在页面里是唯一的,就像人的身份证号。我在项目中遇到过,有些老系统的 OA 页面 id 是动态生成的,每次刷新都不一样。嗯,这时候就别死磕 id 了。
# 定位用户名输入框
username_input = driver.find_element(By.ID, "username")
username_input.send_keys("admin")
3.1.2 name 定位——表单的标配
name 属性在表单里很常见。但要注意,name 可以不唯一。我曾经踩过一个坑:页面上两个输入框的 name 都叫「keyword」,结果定位到了第一个,死活点不了第二个。
# 定位密码输入框
password_input = driver.find_element(By.NAME, "password")
password_input.send_keys("123456")
3.1.3 class_name 定位——样式即定位
class 通常用来控制样式,但如果某个 class 在页面上只出现一次,也可以用来定位。不过说实话,我很少单独用 class_name,因为 class 重复的概率太高了。
3.1.4 tag_name 定位——最不推荐
用标签名定位?你想想看,页面上有多少个 <div>、<input>?除非你确定整个页面只有一个该标签,否则别用。我一般只在遍历所有同类元素时才用它。
3.1.5 link_text 与 partial_link_text——链接专用
这两个专门对付超链接。前者要完全匹配,后者只要包含就行。我个人习惯用 partial_link_text,因为有时候链接文本前面带个空格或者特殊字符,完全匹配反而容易失败。
# 点击「退出登录」链接
driver.find_element(By.LINK_TEXT, "退出登录").click()
# 或者模糊匹配
driver.find_element(By.PARTIAL_LINK_TEXT, "退出").click()
3.1.6 XPath——万能但慢
XPath 是 XML 路径语言,能定位到页面上的任何元素。但它的缺点是解析慢,而且写长了容易出错。我建议:能用 id 或 css 的时候,尽量别用 XPath。
# 绝对路径(不推荐,太脆弱)
driver.find_element(By.XPATH, "/html/body/div[1]/form/input[1]")
# 相对路径(推荐)
driver.find_element(By.XPATH, "//input[@id='username']")
# 包含文本匹配
driver.find_element(By.XPATH, "//button[contains(text(), '提交')]")
3.1.7 CSS Selector——我的最爱
CSS Selector 定位速度快,语法简洁。说白了,它就是通过 CSS 选择器来定位元素。如果你懂前端,上手毫无压力。
# id 选择器
driver.find_element(By.CSS_SELECTOR, "#username")
# class 选择器
driver.find_element(By.CSS_SELECTOR, ".btn-primary")
# 属性选择器
driver.find_element(By.CSS_SELECTOR, "input[name='username']")
# 层级选择器
driver.find_element(By.CSS_SELECTOR, "form > input")
3.2 浏览器操作API
定位到元素之后,就要操作浏览器了。Selenium 提供了一套完整的浏览器操作 API。我把它分成三类:窗口操作、页面导航、元素交互。
3.2.1 窗口操作
做 OA 系统测试时,经常要处理多窗口、多标签页的情况。比如点击一个「新建流程」按钮,弹出一个新窗口。
# 获取当前窗口句柄
current_handle = driver.current_window_handle
# 获取所有窗口句柄
all_handles = driver.window_handles
# 切换到新窗口
driver.switch_to.window(all_handles[-1])
# 关闭当前窗口
driver.close()
# 切换回原窗口
driver.switch_to.window(current_handle)
3.2.2 页面导航
浏览器的前进、后退、刷新,这些在自动化里也常用。
# 打开网址
driver.get("http://oa.example.com")
# 后退
driver.back()
# 前进
driver.forward()
# 刷新
driver.refresh()
# 获取当前 URL
current_url = driver.current_url
# 获取页面标题
title = driver.title
3.2.3 元素交互操作
这是最常用的部分。点击、输入、清空、获取文本,每天都要用。
# 点击
driver.find_element(By.ID, "submit").click()
# 输入文本
driver.find_element(By.ID, "username").send_keys("admin")
# 清空输入框
driver.find_element(By.ID, "username").clear()
# 获取元素文本
text = driver.find_element(By.CLASS_NAME, "message").text
# 获取元素属性
value = driver.find_element(By.ID, "username").get_attribute("value")
get_attribute("value") 获取的是输入框当前的值,而 .text 获取的是元素显示的文本。这两个容易搞混,我刚开始也犯过这个错。
3.3 等待机制详解
为什么要等待?因为页面加载需要时间。你想想看,如果页面还没加载完,你就去点按钮,那肯定报错。Selenium 提供了三种等待方式。
3.3.1 强制等待——简单粗暴
time.sleep(),让程序强制停几秒。这方法最简单,但也最不推荐。为什么?因为如果页面 1 秒就加载完了,你等 5 秒就是浪费时间。如果页面 10 秒才加载完,你等 5 秒又不够。
import time
time.sleep(3) # 强制等待3秒
3.3.2 隐式等待——全局设置
隐式等待是设置一个全局的超时时间。只要在这个时间内元素出现了,就继续执行。如果超时了还没出现,就报错。
# 设置隐式等待,最多等10秒
driver.implicitly_wait(10)
# 之后的所有 find_element 都会等待最多10秒
element = driver.find_element(By.ID, "username")
隐式等待的优点是设置一次,全局生效。但缺点是它只对 find_element 有效,对 find_elements 无效。而且如果某个元素需要更长的等待时间,它也没办法单独设置。
3.3.3 显式等待——精准控制
显式等待是我最推荐的方式。它可以针对某个元素设置特定的等待条件和超时时间。说白了,就是「等到某个条件满足再执行」。
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 等待元素可见
element = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.ID, "username"))
)
# 等待元素可点击
button = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.ID, "submit"))
)
# 等待元素存在
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "username"))
)
常用的等待条件有哪些?我列个表:
| 条件 | 说明 |
|---|---|
| visibility_of_element_located | 元素可见(在 DOM 中且显示) |
| element_to_be_clickable | 元素可见且可点击 |
| presence_of_element_located | 元素存在于 DOM 中 |
| text_to_be_present_in_element | 元素中包含指定文本 |
| alert_is_present | 弹窗出现 |
3.4 实战:OA登录页面的自动化
好了,理论讲完了,咱们来点实战。下面是一个 OA 系统登录页面的自动化脚本,综合运用了今天学的所有知识。
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
import time
# 启动浏览器
driver = webdriver.Chrome()
driver.get("http://oa.example.com/login")
# 设置隐式等待
driver.implicitly_wait(5)
try:
# 显式等待用户名输入框
username_input = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.ID, "username"))
)
username_input.send_keys("admin")
# 定位密码框
password_input = driver.find_element(By.NAME, "password")
password_input.send_keys("123456")
# 点击登录按钮
login_button = driver.find_element(By.CSS_SELECTOR, "#loginForm .btn-primary")
login_button.click()
# 等待页面跳转,检查是否登录成功
WebDriverWait(driver, 10).until(
EC.text_to_be_present_in_element((By.CLASS_NAME, "welcome"), "欢迎")
)
print("登录成功!")
except Exception as e:
print(f"登录失败:{e}")
finally:
time.sleep(2)
driver.quit()
这个脚本里,我用了显式等待来等用户名输入框,用了 CSS Selector 来定位登录按钮,用了隐式等待作为兜底。你想想看,如果不用等待,脚本很可能在页面还没加载完就去操作,那结果就是一片红。
嗯,这一章的内容就到这里。元素定位、浏览器操作、等待机制,这三样东西你练熟了,OA 系统里 80% 的页面你都能搞定。下一章咱们聊聊更高级的——如何处理那些烦人的弹窗和下拉框。