【PYTHON】使用Selenium + ChromeDriver以及XPATH语法
-
-
- 查看chrome版本
- 手动下载驱动
- 代码
- 如何配置监听地址0.0.0.0无效
- XPATH语法
-
- [一、 基础路径选择](#一、 基础路径选择)
- [二、 属性与文本过滤(谓语)](#二、 属性与文本过滤(谓语))
-
- [1. 属性过滤(使用 `@` 符号)](#1. 属性过滤(使用
@符号)) - [2. 文本内容过滤](#2. 文本内容过滤)
- [1. 属性过滤(使用 `@` 符号)](#1. 属性过滤(使用
- [三、 模糊匹配与高级函数](#三、 模糊匹配与高级函数)
- [四、 节点关系与轴(Axes)](#四、 节点关系与轴(Axes))
- [五、 按位置/次序选择](#五、 按位置/次序选择)
- [六、 Python/Selenium 实战代码示例](#六、 Python/Selenium 实战代码示例)
- [🛠️ XPath 调试技巧](#🛠️ XPath 调试技巧)
- 特例
- [1. `//divcontains(@class,'form')//labelnormalize-space(text())='密码'`](#1.
//div[contains(@class,'form')]//label[normalize-space(text())='密码']) - [2. `//div@class='form'.//label\[text()='密码']`](#2.
//div[@class='form'][.//label[text()='密码']]) - [3. `//div@class='form'./label\[text()='密码']`](#3.
//div[@class='form'][./label[text()='密码']]) - [📊 核心区别总结](#📊 核心区别总结)
-
查看chrome版本
reg query "HKEY_CURRENT_USER\Software\Google\Chrome\BLBeacon" /v version
手动下载驱动
120.0.6099.109需要换成自己的
https://storage.googleapis.com/chrome-for-testing-public/120.0.6099.109/win64/chromedriver-win64.zip
代码
python
import subprocess
import time
from selenium import webdriver
# Step 1: 以远程调试模式启动 Chrome(命令行执行)
# Windows: & "C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-address=0.0.0.0 --remote-debugging-port=9222 --user-data-dir="C:\chrome_debug_profile"
# macOS: /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 --user-data-dir="/tmp/chrome_debug"
# Step 2: Python 连接到已有实例
options = webdriver.ChromeOptions()
options.add_experimental_option("debuggerAddress", "127.0.0.1:9222")
driver = webdriver.Chrome(options=options)
print(f"已连接到现有浏览器,当前页面: {driver.title}")
# 此时所有 Cookie、登录状态均保留
python
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
# 1. 配置 Chrome 选项
options = webdriver.ChromeOptions()
# ✅ 关键配置:脚本结束后保持浏览器打开(不自动关闭)
options.add_experimental_option("detach", True)
options.add_argument("--start-maximized") # 最大化窗口
options.add_argument("--disable-blink-features=AutomationControlled") # 降低被检测概率
# 2. 初始化驱动(自动下载/匹配 ChromeDriver)
service = Service(executable_path=r"D:\pythonauto\chromeDriver\driver\chromedriver.exe")
driver = webdriver.Chrome(service=service, options=options)
# 常用配置
try:
# 3. 打开百度
driver.get("https://www.baidu.com")
print("✅ 已打开百度")
# 4. 等待搜索框加载并输入"手机"
search_box = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "chat-textarea"))
)
search_box.clear()
search_box.send_keys("手机")
print("✅ 已输入关键词:手机")
# 5. 点击"百度一下"按钮
submit_btn = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.ID, "chat-submit-button"))
)
submit_btn.click()
print("✅ 已点击搜索,请在浏览器中查看结果")
# 6. ✅ 阻塞等待用户手动输入 q 退出
print("\n" + "=" * 40)
while True:
user_input = input("💡 按 q 并回车退出浏览器: ").strip().lower()
if user_input == "q":
break
print("❌ 输入无效,请输入 q 退出")
finally:
# 7. 用户确认后关闭浏览器并释放资源
print("🔒 正在关闭浏览器...")
driver.quit()
print("✅ 浏览器已安全关闭")
如何配置监听地址0.0.0.0无效
windows shell执行
# 让 Chrome 正常监听 127.0.0.1:9223(不需要改任何参数)
# 然后添加系统级端口转发:外部访问 0.0.0.0:9223 → 127.0.0.1:9223
netsh interface portproxy add v4tov4 listenport=9223 listenaddress=0.0.0.0 connectport=9223 connectaddress=127.0.0.1
# 验证转发规则已生效
netsh interface portproxy show all
# 验证外部可访问
netstat -ano | findstr :9223
# 应同时看到 0.0.0.0:9223 LISTENING 和 127.0.0.1:9223 LISTENING
#删除规则
#netsh interface portproxy delete v4tov4 listenport=9223 listenaddress=0.0.0.0
XPATH语法
一、 基础路径选择
路径表达式用于描述目标节点与文档根节点的关系,分为绝对路径和相对路径。
| 语法 | 说明 | 示例 |
|---|---|---|
/ |
绝对路径:从根节点开始,一层层往下找。 | /html/body/div/form/input |
// |
相对路径:从任意节点开始,忽略前面的层级,查找所有匹配的后代元素。 | //input 或 //div//input |
. |
选取当前节点。 | . |
.. |
选取当前节点的父节点。 | //input/.. 选取 input 的父节点 |
💡 最佳实践 :在自动化测试中,强烈建议使用相对路径 (
//)。绝对路径一旦前端页面结构发生微调(如多嵌套一层 div),定位就会立刻失效。
二、 属性与文本过滤(谓语)
使用方括号 [] 作为谓语(Predicate),可以根据属性值或文本内容精确筛选节点。
1. 属性过滤(使用 @ 符号)
| 语法 | 说明 | 示例 |
|---|---|---|
[@属性名='值'] |
精确匹配属性值 | //input[@id='username'] |
[@属性名] |
仅判断是否拥有该属性 | //*[@multiple] |
[@attr1 and @attr2] |
同时满足多个属性 | //input[@type='text' and @name='email'] |
2. 文本内容过滤
| 语法 | 说明 | 示例 |
|---|---|---|
text()='文本' |
精确匹配文本内容 | //span[text()='下一步'] |
normalize-space(.)='文本' |
去除首尾空白、合并中间空格后精确匹配(最稳健)。 | //button[normalize-space(.)='确认'] |
三、 模糊匹配与高级函数
当属性值或文本是动态生成、包含空格或只有部分固定时,使用以下函数。
| 函数 | 说明 | 示例 |
|---|---|---|
contains() |
包含匹配(最常用) | //label[contains(@class,'btn')]``//span[contains(text(),'下一步')] |
starts-with() |
前缀匹配 | //label[starts-with(@class,'btn')] |
ends-with() |
后缀匹配(XPath 2.0,部分浏览器支持) | //input[ends-with(@id,'_input')] |
string-length() |
字符串长度判断 | //input[string-length(@value)>5] |
四、 节点关系与轴(Axes)
用于处理复杂的 DOM 结构,如父子、兄弟节点定位。
| 轴名称 | 说明 | 示例 |
|---|---|---|
parent:: |
选取父节点 | //input[@id='child']/parent::div |
child:: |
选取直接子元素 | //ul/child::li (等同于 //ul/li) |
descendant:: |
选取所有后代元素 | //ul/descendant::input (等同于 //ul//input) |
ancestor:: |
选取所有先辈节点(父、祖父等) | //input/ancestor::form |
following-sibling:: |
选取当前节点之后的所有同级兄弟节点 | //label[text()='标题']/following-sibling::input |
preceding-sibling:: |
选取当前节点之前的所有同级兄弟节点 | //input[@type='submit']/preceding-sibling::button |
五、 按位置/次序选择
当页面有多个相同结构的元素时,可以通过索引定位。
⚠️ 注意 :XPath 的索引是从 1 开始的,不是从 0 开始!
| 语法 | 说明 | 示例 |
|---|---|---|
[n] |
选取某类型的第 n 个子元素 | //ul/li 选取第二个 li |
[last()] |
选取最后一个元素 | //p[last()] |
[last()-n] |
选取倒数第 n+1 个元素 | //div/p[last()-2] |
[position()<=n] |
选取前 n 个元素 | //option[position()<=3] |
六、 Python/Selenium 实战代码示例
结合 Selenium 的 find_element 方法,以下是常见场景的代码实现:
python
from selenium.webdriver.common.by import By
# 1. 基础属性定位
driver.find_element(By.XPATH, "//input[@id='username']")
# 2. 多属性组合定位 (AND 逻辑)
driver.find_element(By.XPATH, "//input[@type='text' and @name='email']")
# 3. 模糊匹配 class (防止 class 动态拼接导致失效)
driver.find_element(By.XPATH, "//button[contains(@class, 'aui-button--primary')]")
# 4. 通过文本定位并处理空白字符
driver.find_element(By.XPATH, "//button[normalize-space(text())='确认']")
# 5. 相对位置定位:找到 label 后的第一个 input 兄弟节点
driver.find_element(By.XPATH, "//label[text()='标题']/following-sibling::input")[[source_group_web_16]]
# 6. 向上查找祖先节点
driver.find_element(By.XPATH, "//input[@id='child']/ancestor::div[@class='form-item']")
# 7. 获取多个元素 (返回列表)
elements = driver.find_elements(By.XPATH, "//ul/li[@class='item']")
🛠️ XPath 调试技巧
在编写代码前,强烈建议先在浏览器中验证表达式:
- 按
F12打开浏览器开发者工具。 - 切换到 Elements 面板,按
Ctrl + F(或Cmd + F) 调出搜索框。 - 在搜索框中直接输入你的 XPath 表达式,浏览器会高亮显示匹配到的元素及数量。
- 也可以在 Console 面板输入
$x("//你的xpath表达式")进行验证。
特例
//divcontains(@class,'form')//labelnormalize-space(text())='密码'
//div@class='form'.//label\[text()='密码']
//div@class='form'./label\[text()='密码']
三者的区别
我们假设页面上有如下 HTML 结构:
html
<!-- 结构 A:嵌套了一层 div -->
<div class="form is-active">
<div class="form-item">
<label> 密码 </label>
</div>
</div>
<!-- 结构 B:label 直接在 form 下 -->
<div class="form">
<label>密码</label>
</div>
下面为你逐一拆解它们的区别:
1. //div[contains(@class,'form')]//label[normalize-space(text())='密码']
- 最终返回的是 :
<label>元素 - class 匹配 :使用
contains(),能 匹配到结构 A 中class="form is-active"的 div。 - 层级匹配 :使用了
//label(双斜杠),能 穿透中间的<div class="form-item">,找到任意层级的 label。 - 文本匹配 :使用了
normalize-space(),能 自动去除<label> 密码 </label>中的前后空格。 - 总结 :这是最健壮、最推荐的写法。它容错率最高,能应对动态 class、深层嵌套和格式化空白。
2. //div[@class='form'][.//label[text()='密码']]
- 最终返回的是 :
<div class="form">容器元素 - class 匹配 :使用
=精确匹配,不能 匹配结构 A(因为多了is-active),只能匹配结构 B。 - 层级匹配 :谓词内使用了
.//label(带点的//),能穿透中间层级,只要在这个 div 内部任意位置有"密码" label 即可。 - 文本匹配 :使用了
text()='密码',如果 label 带有空格(如<label> 密码 </label>),会匹配失败。 - 总结:这是一个**"通过内部特征来锁定外部容器"**的写法。它的致命弱点是 class 精确匹配和文本精确匹配,在实际自动化中极易因为前端微调而失效。
3. //div[@class='form'][./label[text()='密码']]
- 最终返回的是 :
<div class="form">容器元素 - class 匹配 :同样是精确匹配,只能匹配结构 B。
- 层级匹配 :谓词内使用了
./label(单斜杠/),只能 查找 div 的直接子元素 。如果 label 被包裹在<div class="form-item">里(如结构 A),会直接匹配失败。 - 文本匹配:同样是精确匹配,遇到空格会失败。
- 总结 :这是最脆弱的写法。它要求 div 和 label 必须是严格的父子关系,且 class 和文本不能有任何多余字符。
📊 核心区别总结
| 特性 | 表达式 1 (contains + // + normalize) |
表达式 2 (= + .// + text) |
表达式 3 (= + ./ + text) |
|---|---|---|---|
| 返回目标 | <label> 元素 |
<div> 容器 |
<div> 容器 |
| 兼容动态 class | ✅ 兼容 | ❌ 不兼容 | ❌ 不兼容 |
| 兼容深层嵌套 | ✅ 兼容 | ✅ 兼容 | ❌ 仅限直接子元素 |
| 兼容文本含空格 | ✅ 兼容 | ❌ 不兼容 | ❌ 不兼容 |
| 实战推荐度 | ⭐⭐⭐⭐⭐ (极高) | ⭐⭐ (极低) | ⭐ (极低) |