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)}")

完整链接文本 #

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