Selenium 元素定位 #
定位策略概览 #
八大定位方式 #
text
┌─────────────────────────────────────────────────────────────┐
│ Selenium 八大定位策略 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ ID │ │ Name │ │
│ │ 最快最稳定 │ │ 表单常用 │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Class Name │ │ Tag Name │ │
│ │ 样式类定位 │ │ 标签定位 │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Link Text │ │ Partial Link │ │
│ │ 完整链接文本 │ │ Text │ │
│ └─────────────────┘ │ 部分链接文本 │ │
│ └─────────────────┘ │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ CSS Selector │ │
│ │ 最灵活强大的定位方式 │ │
│ └─────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ XPath │ │
│ │ 功能最全面的定位方式 │ │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
定位策略选择 #
| 策略 | 优先级 | 适用场景 | 稳定性 |
|---|---|---|---|
| ID | ⭐⭐⭐⭐⭐ | 唯一标识元素 | 最高 |
| Name | ⭐⭐⭐⭐ | 表单元素 | 高 |
| CSS Selector | ⭐⭐⭐⭐ | 复杂选择 | 高 |
| XPath | ⭐⭐⭐ | 复杂定位 | 中 |
| Class Name | ⭐⭐⭐ | 样式类 | 中 |
| Link Text | ⭐⭐⭐ | 链接元素 | 中 |
| Tag Name | ⭐⭐ | 批量元素 | 低 |
| Partial Link Text | ⭐⭐ | 模糊链接 | 低 |
ID 定位 #
基本用法 #
ID 是最快速、最稳定的定位方式:
python
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com")
# ID 定位
element = driver.find_element(By.ID, "username")
# HTML 示例
# <input type="text" id="username" name="user">
注意事项 #
python
# ID 应该是唯一的,但有时页面会有重复 ID
elements = driver.find_elements(By.ID, "duplicate-id")
print(f"找到 {len(elements)} 个元素")
# 如果 ID 是动态生成的,避免使用
# <input id="input_12345"> ← 不推荐
# <input id="username"> ← 推荐
Name 定位 #
基本用法 #
Name 定位常用于表单元素:
python
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com")
# Name 定位
username = driver.find_element(By.NAME, "username")
password = driver.find_element(By.NAME, "password")
# HTML 示例
# <input type="text" name="username">
# <input type="password" name="password">
表单场景 #
python
# 表单中的 name 通常是唯一的
form_elements = {
'username': driver.find_element(By.NAME, "username"),
'password': driver.find_element(By.NAME, "password"),
'email': driver.find_element(By.NAME, "email"),
'phone': driver.find_element(By.NAME, "phone"),
}
for name, element in form_elements.items():
print(f"{name}: {element.get_attribute('type')}")
Class Name 定位 #
基本用法 #
通过 class 属性定位元素:
python
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com")
# Class Name 定位(单个类名)
element = driver.find_element(By.CLASS_NAME, "btn-primary")
# HTML 示例
# <button class="btn btn-primary">Submit</button>
多个元素定位 #
python
# 查找所有相同 class 的元素
buttons = driver.find_elements(By.CLASS_NAME, "btn")
for button in buttons:
print(button.text)
注意事项 #
python
# Class Name 只能使用单个类名
# 错误:driver.find_element(By.CLASS_NAME, "btn btn-primary")
# 如果需要多个类名,使用 CSS Selector
element = driver.find_element(By.CSS_SELECTOR, ".btn.btn-primary")
Tag Name 定位 #
基本用法 #
通过 HTML 标签名定位:
python
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com")
# Tag Name 定位
title = driver.find_element(By.TAG_NAME, "h1")
links = driver.find_elements(By.TAG_NAME, "a")
inputs = driver.find_elements(By.TAG_NAME, "input")
print(f"标题: {title.text}")
print(f"链接数: {len(links)}")
print(f"输入框数: {len(inputs)}")
实际应用 #
python
# 获取所有图片
images = driver.find_elements(By.TAG_NAME, "img")
for img in images:
src = img.get_attribute("src")
alt = img.get_attribute("alt")
print(f"图片: {src}, 描述: {alt}")
# 获取所有表格
tables = driver.find_elements(By.TAG_NAME, "table")
for table in tables:
rows = table.find_elements(By.TAG_NAME, "tr")
print(f"表格行数: {len(rows)}")
Link Text 定位 #
完整链接文本 #
python
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com")
# Link Text 定位(完整匹配)
login_link = driver.find_element(By.LINK_TEXT, "登录")
about_link = driver.find_element(By.LINK_TEXT, "关于我们")
# HTML 示例
# <a href="/login">登录</a>
# <a href="/about">关于我们</a>
login_link.click()
部分链接文本 #
python
# Partial Link Text 定位(部分匹配)
link = driver.find_element(By.PARTIAL_LINK_TEXT, "联系")
# 可以匹配:
# <a href="/contact">联系我们</a>
# <a href="/contact">联系方式</a>
# <a href="/contact">联系电话</a>
实际应用 #
python
# 查找所有包含"更多"的链接
more_links = driver.find_elements(By.PARTIAL_LINK_TEXT, "更多")
for link in more_links:
print(f"链接文本: {link.text}")
print(f"链接地址: {link.get_attribute('href')}")
CSS Selector 定位 #
基本选择器 #
python
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com")
# ID 选择器
element = driver.find_element(By.CSS_SELECTOR, "#username")
# Class 选择器
element = driver.find_element(By.CSS_SELECTOR, ".btn-primary")
# 标签选择器
element = driver.find_element(By.CSS_SELECTOR, "input")
# 组合选择器
element = driver.find_element(By.CSS_SELECTOR, "input#username")
element = driver.find_element(By.CSS_SELECTOR, "button.btn-primary")
属性选择器 #
python
# 属性存在
element = driver.find_element(By.CSS_SELECTOR, "[data-testid]")
# 属性等于
element = driver.find_element(By.CSS_SELECTOR, "[type='submit']")
element = driver.find_element(By.CSS_SELECTOR, "[name='username']")
# 属性包含
element = driver.find_element(By.CSS_SELECTOR, "[class*='btn']")
element = driver.find_element(By.CSS_SELECTOR, "[href*='login']")
# 属性开头
element = driver.find_element(By.CSS_SELECTOR, "[class^='btn']")
element = driver.find_element(By.CSS_SELECTOR, "[href^='https']")
# 属性结尾
element = driver.find_element(By.CSS_SELECTOR, "[class$='primary']")
element = driver.find_element(By.CSS_SELECTOR, "[href$='.pdf']")
# 多属性组合
element = driver.find_element(By.CSS_SELECTOR, "input[type='text'][name='username']")
层级选择器 #
python
# 后代选择器(所有后代)
element = driver.find_element(By.CSS_SELECTOR, "form input")
# 子选择器(直接子元素)
element = driver.find_element(By.CSS_SELECTOR, "ul > li")
# 相邻兄弟选择器
element = driver.find_element(By.CSS_SELECTOR, "h1 + p")
# 通用兄弟选择器
element = driver.find_element(By.CSS_SELECTOR, "h1 ~ p")
伪类选择器 #
python
# 第一个子元素
element = driver.find_element(By.CSS_SELECTOR, "li:first-child")
# 最后一个子元素
element = driver.find_element(By.CSS_SELECTOR, "li:last-child")
# 第 n 个子元素
element = driver.find_element(By.CSS_SELECTOR, "li:nth-child(2)")
element = driver.find_element(By.CSS_SELECTOR, "li:nth-child(odd)")
element = driver.find_element(By.CSS_SELECTOR, "li:nth-child(even)")
element = driver.find_element(By.CSS_SELECTOR, "li:nth-child(3n+1)")
# 倒数第 n 个
element = driver.find_element(By.CSS_SELECTOR, "li:nth-last-child(2)")
# 唯一子元素
element = driver.find_element(By.CSS_SELECTOR, "li:only-child")
# 类型选择
element = driver.find_element(By.CSS_SELECTOR, "p:first-of-type")
element = driver.find_element(By.CSS_SELECTOR, "p:last-of-type")
element = driver.find_element(By.CSS_SELECTOR, "p:nth-of-type(2)")
# 空元素
element = driver.find_element(By.CSS_SELECTOR, "div:empty")
# 否定选择器
element = driver.find_element(By.CSS_SELECTOR, "input:not([disabled])")
element = driver.find_element(By.CSS_SELECTOR, "li:not(:first-child)")
状态伪类 #
python
# 启用/禁用
element = driver.find_element(By.CSS_SELECTOR, "input:enabled")
element = driver.find_element(By.CSS_SELECTOR, "input:disabled")
# 选中状态
element = driver.find_element(By.CSS_SELECTOR, "input:checked")
element = driver.find_element(By.CSS_SELECTOR, "option:selected")
# 获得焦点
element = driver.find_element(By.CSS_SELECTOR, "input:focus")
复杂组合示例 #
python
# 表格中第二行第三列
cell = driver.find_element(By.CSS_SELECTOR, "table tr:nth-child(2) td:nth-child(3)")
# 表单中必填的输入框
required_inputs = driver.find_elements(By.CSS_SELECTOR, "input[required]")
# 导航菜单中的活动项
active_item = driver.find_element(By.CSS_SELECTOR, "nav .menu-item.active")
# 带有特定 data 属性的按钮
button = driver.find_element(By.CSS_SELECTOR, "button[data-action='submit']")
# 父元素下的第一个按钮
button = driver.find_element(By.CSS_SELECTOR, ".modal button:first-child")
XPath 定位 #
基本语法 #
python
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com")
# 绝对路径(不推荐)
element = driver.find_element(By.XPATH, "/html/body/div/form/input")
# 相对路径(推荐)
element = driver.find_element(By.XPATH, "//input")
# 带 ID 的相对路径
element = driver.find_element(By.XPATH, "//input[@id='username']")
属性定位 #
python
# 单属性
element = driver.find_element(By.XPATH, "//input[@id='username']")
element = driver.find_element(By.XPATH, "//input[@name='password']")
element = driver.find_element(By.XPATH, "//input[@type='submit']")
# 多属性
element = driver.find_element(By.XPATH, "//input[@type='text' and @name='username']")
element = driver.find_element(By.XPATH, "//input[@type='text' or @type='email']")
# 属性包含
element = driver.find_element(By.XPATH, "//div[contains(@class, 'btn')]")
element = driver.find_element(By.XPATH, "//a[contains(@href, 'login')]")
# 属性开头
element = driver.find_element(By.XPATH, "//a[starts-with(@href, 'https')]")
# 属性结尾(需要使用 substring)
element = driver.find_element(By.XPATH, "//a[substring(@href, string-length(@href) - 3) = '.pdf']")
文本定位 #
python
# 精确文本匹配
element = driver.find_element(By.XPATH, "//button[text()='登录']")
element = driver.find_element(By.XPATH, "//a[text()='关于我们']")
# 包含文本
element = driver.find_element(By.XPATH, "//button[contains(text(), '登录')]")
element = driver.find_element(By.XPATH, "//p[contains(text(), '欢迎')]")
# 以文本开头
element = driver.find_element(By.XPATH, "//button[starts-with(text(), '确认')]")
# 文本为空
element = driver.find_element(By.XPATH, "//button[not(text())]")
# 组合文本和属性
element = driver.find_element(By.XPATH, "//button[@class='btn' and text()='提交']")
层级定位 #
python
# 父元素
element = driver.find_element(By.XPATH, "//input[@id='username']/..")
# 子元素
element = driver.find_element(By.XPATH, "//form/input")
# 所有后代
element = driver.find_element(By.XPATH, "//form//input")
# 兄弟元素(后面)
element = driver.find_element(By.XPATH, "//label[text()='用户名']/following-sibling::input")
# 兄弟元素(前面)
element = driver.find_element(By.XPATH, "//input[@id='password']/preceding-sibling::label")
# 祖先元素
element = driver.find_element(By.XPATH, "//input[@id='username']/ancestor::form")
# 后代元素
element = driver.find_element(By.XPATH, "//form/descendant::input")
位置定位 #
python
# 第一个
element = driver.find_element(By.XPATH, "//li[1]")
element = driver.find_element(By.XPATH, "(//input)[1]")
# 最后一个
element = driver.find_element(By.XPATH, "//li[last()]")
element = driver.find_element(By.XPATH, "(//input)[last()]")
# 倒数第二个
element = driver.find_element(By.XPATH, "//li[last()-1]")
# 第 n 个
element = driver.find_element(By.XPATH, "//li[3]")
# 范围选择
elements = driver.find_elements(By.XPATH, "//li[position() >= 2 and position() <= 5]")
# 奇数/偶数
elements = driver.find_elements(By.XPATH, "//li[position() mod 2 = 1]") # 奇数
elements = driver.find_elements(By.XPATH, "//li[position() mod 2 = 0]") # 偶数
高级函数 #
python
# count() - 计数
count = driver.find_element(By.XPATH, "count(//li)")
# string-length() - 字符串长度
element = driver.find_element(By.XPATH, "//input[string-length(@value) > 5]")
# normalize-space() - 去除空白
element = driver.find_element(By.XPATH, "//button[normalize-space(text())='登录']")
# translate() - 大小写转换
element = driver.find_element(By.XPATH, "//button[translate(text(), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')='LOGIN']")
# concat() - 字符串连接
element = driver.find_element(By.XPATH, "//title[text()=concat('Hello', ' ', 'World')]")
# not() - 取反
element = driver.find_element(By.XPATH, "//input[not(@disabled)]")
element = driver.find_element(By.XPATH, "//div[not(contains(@class, 'hidden'))]")
轴定位 #
python
# ancestor - 所有祖先
element = driver.find_element(By.XPATH, "//input/ancestor::form")
# ancestor-or-self - 所有祖先和自身
element = driver.find_element(By.XPATH, "//div/ancestor-or-self::div")
# descendant - 所有后代
elements = driver.find_elements(By.XPATH, "//form/descendant::input")
# descendant-or-self - 所有后代和自身
elements = driver.find_elements(By.XPATH, "//div/descendant-or-self::*")
# following - 当前节点之后的所有节点
elements = driver.find_elements(By.XPATH, "//h1/following::*")
# following-sibling - 当前节点之后的所有兄弟节点
elements = driver.find_elements(By.XPATH, "//h1/following-sibling::*")
# preceding - 当前节点之前的所有节点
elements = driver.find_elements(By.XPATH, "//footer/preceding::*")
# preceding-sibling - 当前节点之前的所有兄弟节点
elements = driver.find_elements(By.XPATH, "//footer/preceding-sibling::*")
# parent - 父节点
element = driver.find_element(By.XPATH, "//input/parent::div")
# child - 子节点
elements = driver.find_elements(By.XPATH, "//ul/child::li")
定位策略对比 #
CSS Selector vs XPath #
python
# 相同功能的两种写法对比
# ID 定位
css = "#username"
xpath = "//input[@id='username']"
# Class 定位
css = ".btn-primary"
xpath = "//button[contains(@class, 'btn-primary')]"
# 属性定位
css = "[data-testid='submit']"
xpath = "//*[@data-testid='submit']"
# 文本定位(XPath 独有)
xpath = "//button[text()='登录']"
# CSS 无法直接通过文本定位
# 父元素定位(XPath 独有)
xpath = "//input[@id='username']/.."
# CSS 无法定位父元素
# 兄弟元素定位
css = "h1 + p" # 相邻兄弟
xpath = "//h1/following-sibling::p[1]" # 所有兄弟
选择建议 #
text
┌─────────────────────────────────────────────────────────────┐
│ 定位策略选择指南 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 优先使用 ID(如果有唯一 ID) │
│ driver.find_element(By.ID, "username") │
│ │
│ 2. 其次使用 Name(表单元素) │
│ driver.find_element(By.NAME, "password") │
│ │
│ 3. 复杂选择使用 CSS Selector │
│ driver.find_element(By.CSS_SELECTOR, ".form input") │
│ │
│ 4. 需要文本定位时使用 XPath │
│ driver.find_element(By.XPATH, "//button[text()='登录']") │
│ │
│ 5. 需要层级定位时使用 XPath │
│ driver.find_element(By.XPATH, "//input/..") │
│ │
└─────────────────────────────────────────────────────────────┘
实战技巧 #
处理动态属性 #
python
# 动态 ID(部分固定)
# <input id="username_12345">
element = driver.find_element(By.CSS_SELECTOR, "[id^='username_']")
element = driver.find_element(By.XPATH, "//input[starts-with(@id, 'username_')]")
# 动态 Class
# <div class="item-12345 active">
element = driver.find_element(By.CSS_SELECTOR, "[class*='item-']")
element = driver.find_element(By.XPATH, "//div[contains(@class, 'item-')]")
处理表格数据 #
python
# 定位表格中的特定单元格
def get_table_cell(driver, row, col):
xpath = f"//table//tr[{row}]/td[{col}]"
return driver.find_element(By.XPATH, xpath)
# 获取表格所有数据
def get_table_data(driver):
table = driver.find_element(By.TAG_NAME, "table")
rows = table.find_elements(By.TAG_NAME, "tr")
data = []
for row in rows:
cells = row.find_elements(By.TAG_NAME, "td")
row_data = [cell.text for cell in cells]
data.append(row_data)
return data
处理下拉菜单 #
python
# 悬停显示的菜单
from selenium.webdriver.common.action_chains import ActionChains
menu = driver.find_element(By.CSS_SELECTOR, ".dropdown")
ActionChains(driver).move_to_element(menu).perform()
# 等待菜单显示后定位
submenu = driver.find_element(By.CSS_SELECTOR, ".dropdown-menu .menu-item")
submenu.click()
相对定位器(Selenium 4+) #
python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.relative_locator import locate_with
driver = webdriver.Chrome()
driver.get("https://example.com")
# 找到标签,然后定位其下方的输入框
label = driver.find_element(By.XPATH, "//label[text()='用户名']")
input_field = driver.find_element(locate_with(By.TAG_NAME, "input").below(label))
# 找到按钮,然后定位其左侧的输入框
button = driver.find_element(By.ID, "submit")
input_field = driver.find_element(locate_with(By.TAG_NAME, "input").to_left_of(button))
# 组合使用
password_field = driver.find_element(
locate_with(By.TAG_NAME, "input")
.below(driver.find_element(By.ID, "username"))
.to_right_of(driver.find_element(By.XPATH, "//label[text()='密码']"))
)
调试技巧 #
验证定位器 #
python
# 在浏览器控制台中验证 CSS Selector
# document.querySelectorAll("#username")
# document.querySelectorAll(".btn-primary")
# 在浏览器控制台中验证 XPath
# $x("//input[@id='username']")
# $x("//button[text()='登录']")
定位器测试函数 #
python
def test_locator(driver, by, value):
"""测试定位器是否有效"""
try:
element = driver.find_element(by, value)
print(f"✓ 定位成功: {by} = '{value}'")
print(f" 元素: {element.tag_name}")
print(f" 文本: {element.text[:50] if element.text else '(空)'}")
return True
except Exception as e:
print(f"✗ 定位失败: {by} = '{value}'")
print(f" 错误: {e}")
return False
# 使用示例
test_locator(driver, By.ID, "username")
test_locator(driver, By.CSS_SELECTOR, ".btn-primary")
test_locator(driver, By.XPATH, "//button[text()='登录']")
下一步 #
掌握了元素定位后,接下来学习 用户交互 了解更多交互操作!
最后更新:2026-03-28