使用前先看:快速上手
这份手册可以按两种方式使用:新手建议从第 1 章顺着读一遍;已经在做流程时,可以直接搜索报错现象、元素类型或关键词,例如 iframe、输入不生效、Shadow DOM。
先看定位原则和 XPath / CSS 选择方法,避免一开始就复制不稳定的绝对路径。
去看定位方式先判断是不是层级、时机、元素选错,再进入故障排查章节逐项验证。
去看故障排查登录、表单、弹窗、iframe、联想词、表格行按钮都有可改造的流程骨架。
去看模板库开始前的 5 个确认
- 目标元素是否在 iframe 或 Shadow DOM 里。
- 选择器在浏览器 Console 中是否能稳定找到,而且最好只找到 1 个。
- 操作的是实际可交互元素,例如真正的 input、textarea、button。
- 是否使用 Wait for element 等待关键元素,而不是只靠固定 Delay。
- 页面发生跳转、翻页、弹窗关闭或列表刷新后,是否重新定位了元素。
第 1 章:为什么 Automa 经常失败
很多人刚开始用 Automa,最容易遇到的问题就是:找不到元素、明明看见元素但选不到、能选到输入框但输入失败、能选到按钮但点击没反应、同一个流程有时成功有时失败。
这些问题通常不是 Automa 本身坏了,而是因为真实网页不是静态页面。一个网页里可能同时存在 iframe、异步加载、动态 class、遮罩层、React / Vue 事件接管、Shadow DOM、页面重绘等情况。
- 层级对:你得先确认自己是在主页面、iframe 还是多层 iframe。
- 元素对:你得确认选到的是真正可交互的 input、button,而不是外层 div。
- 时机对:你得等页面真的加载完,而不是猜 1 秒、2 秒。
第 2 章:先理解网页元素定位是什么
可以把网页理解成一棵树。所谓元素定位,本质就是告诉浏览器:我要找到这棵树里的哪一个节点。
<div class="login-box">
<label>用户名</label>
<input type="text" name="username">
<button>登录</button>
</div>
在 Automa 中最常见的三类定位方式是:CSS Selector、XPath、JavaScript。它们不是互相替代,而是互相补充。
返回顶部第 3 章:三种核心定位方式
CSS Selector
语法短、速度快、日常最好用。
适合按 id、class、name、placeholder、属性定位。
XPath
功能强,可以按文字找元素,也能处理复杂父子兄弟关系。
适合按文字找按钮、按标签找输入框、找某一行内的按钮。
JavaScript
最灵活,是最终兜底方案。
适合输入不生效、点击没反应、Shadow DOM、复杂交互页面。
3.1 选择器优先级
| 优先级 | 推荐依据 | 示例 | 稳定性 |
|---|---|---|---|
| 1 | 唯一且稳定的 id / name / data-* 属性 | input[name="username"] | 高 |
| 2 | 业务含义明确的 placeholder / aria-label | input[placeholder="请输入手机号"] | 较高 |
| 3 | 稳定 class 或局部区域内的 class | .login-form .submit-btn | 中 |
| 4 | 按钮文字、标签文字、表格行文字 | //button[contains(., "提交")] | 中 |
| 5 | 层级序号、nth-child、绝对 XPath | /html/body/div[3]/button | 低 |
第 4 章:XPath 完整入门
4.1 XPath 是什么
XPath 是一种通过路径规则查找网页元素的方法。
//input[@name="username"]
4.2 核心语法
//button
//input[@type="text"]
//button[text()="登录"]
//div[contains(@class,"price")]
4.3 最常见写法
//input[@id="username"]
//input[@name="mobile"]
//div[contains(@class,"price")]
//button[contains(text(),"提交")]
//label[text()="手机号"]/following-sibling::input
//span[text()="价格"]/parent::div
//input[@type="text" and contains(@class,"input")]
(//div[contains(@class,"item")])[2]
4.4 常见坑
不要优先使用绝对 XPath。
/html/body/div[3]/div[2]/div[1]/form/input
//form//input[@name="username"]
//button[contains(normalize-space(.),"提交")]
4.5 实战案例
//input[@name="username"]
//button[contains(text(),"登录")]
//label[text()="姓名"]/following-sibling::input
//tr[td[contains(text(),"王五")]]//button[contains(text(),"编辑")]
//div[contains(@class,"price-value-red")]/span[2]
返回顶部
第 5 章:CSS Selector 完整入门
CSS Selector 原本是给样式使用的,但浏览器也可以用它来定位元素。
#username
.price
button
input[type="text"]
input.input-box[type="text"]
.form-box input
.form-box > input
div[class*="price"]
div:nth-child(2)
实战案例
input[name="username"]
.search-input
input[placeholder="请输入关键字"]
.market-detail-item-price-value-red
.dialog .confirm
第 6 章:XPath 和 CSS 怎么选
- 优先找稳定属性:id、name、placeholder、data-*。
- 稳定 class:优先 CSS。
- 按文字定位:优先 XPath。
- 复杂父子兄弟关系:优先 XPath。
- 普通办法都不稳:上 JavaScript。
第 7 章:iframe 核心操作
iframe 就是页面里嵌了另一个页面。你肉眼能看到元素,不代表 Automa 当前就在那个层里。
<iframe id="loginFrame" src="login.html"></iframe>
默认情况下,你是在主页面中查找元素,但目标元素其实在 iframe 内部,所以必须先切进去。
- 打开页面
- Wait for iframe 出现
- Switch Frame 到该 iframe
- 再操作 iframe 内部元素
#loginFrame
input[name="username"]
input[name="password"]
//button[contains(text(),"登录")]
7.1 iframe 的边界提醒
- 先定位 iframe 本身,再切换进去,切进去后才写 iframe 内部元素的选择器。
- 如果 iframe 是跨域页面,浏览器 Console 里直接访问 contentWindow.document 可能会被安全策略拦截;这时更应该依赖 Automa 的 frame 操作能力,而不是在主页面脚本里硬取内部 DOM。
- 切入 iframe 后,如果后续还要操作主页面元素,需要切回主页面或重新安排流程层级。
第 8 章:多层 iframe 的处理方法
主页面
└ iframeA
└ iframeB
└ input
这种情况下,概念上确实是“一层一层进入”:先找到外层 iframe,再找内层 iframe,最后才找目标元素。但在 Automa 里,处理多层 iframe 时更推荐使用官方的自定义选择器语法 |>,把 iframe 路径和最终元素写在同一个表达式里。
iframe-selector |> element-selector
如果是多层 iframe,就继续向后串:
iframe#outerFrame |> iframe#innerFrame |> input[name="username"]
也可以用属性写法,让选择器更贴近真实页面:
iframe[src*="login"] |> iframe[name="content"] |> button.primary-btn
8.1 为什么 |> 更稳定
可以把 |> 理解成“进入这个 iframe 后,再继续在里面查找右边的选择器”。整条表达式一次性描述了从主页面到目标元素的完整路径。
- 路径完整:Automa 知道目标元素在外层 iframe 里的内层 iframe 里,而不是只知道当前要切某一层。
- 上下文更清楚:多个 iframe 组件分开写时,后一个组件可能没有沿用你以为的上一层上下文,尤其是页面刷新、等待、重绘或 block 执行顺序变化后更容易失效。
- 更方便排查:一条表达式从左到右读,就能看出卡在外层 iframe、内层 iframe,还是最终元素。
- 更不容易选错:页面有多个 iframe 时,完整链路可以减少“切到了另一个相似 iframe”的概率。
8.2 推荐写法和不推荐写法
推荐
iframe#outerFrame |> iframe#innerFrame |> input[name="keyword"]
iframe[src*="workbench"] |> iframe[src*="form"] |> //button[contains(., "提交")]
容易出问题
1. Switch iframe#outerFrame
2. Switch iframe#innerFrame
3. 再单独定位 input[name="keyword"]
这种写法在某些页面也可能成功,但遇到动态加载、多 iframe、页面重绘时,更容易出现“看起来切过了,但后面还是找不到元素”的问题。
8.3 写多层 iframe 选择器的顺序
- 先在开发者工具里确认目标元素到底在哪一层 iframe 里。
- 从最外层 iframe 开始写,逐层向里,不要跳层。
- 每一层 iframe 尽量用稳定特征,例如 id、name、src*。
- 最后一段才写目标元素,例如输入框、按钮、列表项。
外层 iframe |> 内层 iframe |> 最终元素
第 9 章:输入框无法输入、按钮无法点击怎么办
输入失败常见原因
- 你选中的不是实际 input。
- 页面只认 input/change 事件。
- 页面是 React/Vue,直接改 value 不够。
- 输入时机太早。
- 元素在 iframe 里。
const input = document.querySelector('input');
input.focus();
input.value = '测试';
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
function simulateInput(el, text) {
el.focus();
el.value = '';
for (const char of text) {
el.value += char;
el.dispatchEvent(new Event('input', { bubbles: true }));
}
el.dispatchEvent(new Event('change', { bubbles: true }));
}
点击失败常见原因
- 点到了外层容器,而不是真正按钮。
- 页面上有遮罩层。
- 按钮在 iframe 中。
- 需要 JS click。
const btn = document.querySelector('button');
btn.click();
const btn = document.querySelector('button');
btn.dispatchEvent(new MouseEvent('click', {
bubbles: true,
cancelable: true
}));
返回顶部
第 10 章:动态页面、延迟加载与 DOM 重绘
现代网页大多数都是异步渲染。页面先显示壳子,数据回来后元素才真正挂出来。
- 页面刚打开时找不到元素。
- 同一流程偶尔成功偶尔失败。
- 翻页、切 tab、关闭弹窗后旧元素失效。
<div class="el-input__inner a1b2c3-x9"></div>
//div[contains(@class,"el-input__inner")]
div[class*="el-input__inner"]
返回顶部
第 11 章:Shadow DOM 是什么,怎么处理
有些元素在 Shadow DOM 中,而不是普通 DOM 中。开发者工具里会看到 #shadow-root (open)。
const host = document.querySelector('my-component');
const shadow = host.shadowRoot;
const input = shadow.querySelector('input');
input.value = '测试';
input.dispatchEvent(new Event('input', { bubbles: true }));
const root1 = document.querySelector('comp-a').shadowRoot;
const root2 = root1.querySelector('comp-b').shadowRoot;
const btn = root2.querySelector('button');
btn.click();
11.1 Shadow DOM 的限制
- #shadow-root (open) 通常可以用 JavaScript 继续向里查询。
- #shadow-root (closed) 通常无法从外部脚本直接访问,需要换业务入口、调用页面已有事件,或寻找组件暴露出的普通 DOM 控件。
- 如果元素在多层 Shadow DOM 中,要一层一层进入,和多层 iframe 的思路类似。
第 12 章:浏览器中如何调试定位
不要每次都直接把选择器丢给 Automa 试错。先在浏览器里验证能不能找到元素。
$x('//button[contains(text(),"登录")]')
$x('//button[contains(text(),"登录")]').length
document.querySelectorAll('.login-btn')
document.querySelectorAll('.login-btn').length
const frame = document.querySelector('iframe');
frame.contentWindow.document.querySelector('input');
$0
$0.contentWindow.document
返回顶部
第 13 章:Automa 中如何调试流程
- 多插入 Delay,看问题发生在哪一步。
- 打印日志,确认变量和中间结果。
- 截图确认页面状态。
- 一个动作一个动作测,不要一上来就整套跑完。
13.1 建议记录的调试信息
- 失败发生在哪一个 block,而不是只记录“流程失败”。
- 失败时页面 URL、页面标题、截图、关键变量值。
- 选择器命中了几个元素,命中的第一个元素是不是预期目标。
- 失败是一直失败,还是偶发失败;偶发失败通常优先检查等待时机和页面重绘。
第 14 章:元素定位实战案例
input[name="username"]
//button[contains(text(),"登录")]
//label[text()="手机号"]/following-sibling::input
//div[contains(@class,"price-value-red")]/span[2]
//tr[td[contains(text(),"张三")]]//button[contains(text(),"编辑")]
//div[contains(@class,"dialog")]//button[contains(text(),"确定")]
//ul[contains(@class,"suggest-list")]//li[contains(text(),"北京站")]
这几类选择器基本覆盖了大多数后台、表单、弹窗、列表和搜索建议场景。
返回顶部第 15 章:Automa 实战案例手册
15.1 登录页自动输入用户名密码
- Wait 用户名输入框
- 输入用户名
- 输入密码
- Wait 登录按钮
- 点击登录
input[name="username"]
input[name="password"]
//button[contains(text(),"登录")]
15.2 按按钮文字点击
//button[contains(text(),"立即提交")]
15.3 根据标签填写表单
//label[text()="姓名"]/following-sibling::input
//label[text()="手机号"]/following-sibling::input
15.4 iframe 内登录框操作
- Wait #loginFrame
- Switch Frame #loginFrame
- 输入用户名密码
- 点击登录
15.5 多层 iframe 输入内容
多层 iframe 推荐把外层、内层和最终元素用 |> 串成一条完整路径。
iframe#outerFrame |> iframe#innerFrame |> input[name="keyword"]
- Wait 外层 iframe 出现
- 在 iframe 组件或元素选择器中填写完整 |> 路径
- 直接操作最后一层 iframe 内的目标元素
15.6 联想词处理
- 输入关键词
- Wait 联想列表出现
- 点击目标项
(//ul[contains(@class,"suggest-list")]//li)[1]
//ul[contains(@class,"suggest-list")]//li[contains(text(),"北京站")]
15.7 价格抓取
.market-detail-item-price-value-red
//div[contains(@class,"price-value-red")]/span[2]
15.8 表格中找某一行按钮
//tr[td[contains(text(),"王五")]]//button[contains(text(),"编辑")]
返回顶部
第 16 章:Automa 完整流程模板库
普通登录流程
1. Open website
2. Wait for element → input[name="username"]
3. Input 用户名
4. Input 密码
5. Wait for 登录按钮
6. Click 登录按钮
7. Wait 登录成功页面元素
iframe 内登录流程
1. Open website
2. Wait for element → #loginFrame
3. Switch frame → #loginFrame
4. Wait for element → input[name="username"]
5. Input 用户名
6. Input 密码
7. Click 登录
多层 iframe 流程
1. Wait 外层 iframe 出现
2. 使用完整路径定位目标元素
iframe#outerFrame |> iframe#innerFrame |> input[name="keyword"]
3. 对最后定位到的 input 执行输入或点击
普通表单填写
1. Wait 姓名输入框
2. Input 姓名
3. Input 手机号
4. Input 身份证号
5. Input 备注
6. Click 提交
联想词选择
1. Wait 搜索框
2. 输入关键词
3. Wait 联想列表出现
4. Click 指定联想项
弹窗确认
1. 触发弹窗
2. Wait 弹窗出现
3. Click 弹窗内确定按钮
4. Wait 弹窗消失
const btn = Array.from(document.querySelectorAll('button'))
.find(el => el.innerText.includes('提交'));
if (btn) btn.click();
const input = document.querySelector('input[name="username"]');
if (input) {
input.focus();
input.value = 'myuser';
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
}
Click → Wait 新页面关键元素 → 重新定位 → 再执行下一步
返回顶部
第 17 章:Automa 故障排查手册
17.1 找不到元素
常见原因:没切 iframe、页面没加载完、选择器写错、元素动态加载、在 Shadow DOM 里。
document.querySelectorAll('你的CSS')
$x('你的XPath')
17.2 明明看得见,但选不到
常见原因:在 iframe、在 Shadow DOM、选到的是外层容器、弹层还没真正挂到 DOM。
17.3 输入不生效
const input = document.querySelector('input');
input.focus();
input.value = '测试内容';
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
17.4 点击没反应
const btn = document.querySelector('button');
if (btn) btn.click();
17.5 同一流程偶尔成功偶尔失败
17.6 翻页后抓不到
点击下一页后要等待新页面首项出现,再重新定位列表。
17.7 联想词点不到
输入后要触发 input/change,Wait 联想项出现,再按文字点击指定项。
17.8 表格里总点错行
//tr[td[contains(text(),"王五")]]//button[contains(text(),"编辑")]
17.9 动态 class 导致失效
div[class*="input-box"]
//div[contains(@class,"input-box")]
17.10 绝对 XPath 用几天就失效
/html/body/div[3]/div[2]/div[1]/button
//button[contains(text(),"提交")]
17.11 Shadow DOM 元素找不到
const host = document.querySelector('my-component');
const shadow = host.shadowRoot;
const input = shadow.querySelector('input');
input.value = '测试';
input.dispatchEvent(new Event('input', { bubbles: true }));
返回顶部
第 18 章:给小白的稳定写法建议
- id
- name
- placeholder
- data-* 属性
- 稳定 class
- 按文字
- 按结构关系
- JS
推荐
input[name="username"]
//button[contains(text(),"提交")]
//div[contains(@class,"dialog")]//button[contains(text(),"确定")]
不推荐
body > div:nth-child(4) > div > input
/html/body/div[5]/div[2]/div[3]/button
第 19 章:通用排查流程
- 先看是不是 iframe。
- 在浏览器 Console 验证选择器。
- 确认选择器是否唯一。
- 确认页面是否加载完成。
- 确认你选到的是不是实际可交互元素。
- 普通 block 不行就换 JS。
- 页面变化后重新定位。
document.querySelectorAll('你的CSS')
$x('你的XPath')
const input = document.querySelector('input');
input.focus();
input.value = '内容';
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
document.querySelector('button').click();
第 20 章:最常用模板速查表
//button[contains(text(),"登录")]
input[name="username"]
//label[text()="手机号"]/following-sibling::input
#loginFrame
//tr[td[contains(text(),"王五")]]//button[contains(text(),"编辑")]
JS 输入模板
const input = document.querySelector('input');
input.focus();
input.value = '测试';
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
JS 点击模板
document.querySelector('button').click();
iframe 调试模板
const frame = document.querySelector('iframe');
frame.contentWindow.document.querySelector('input');
多层 iframe 调试模板
const doc1 = document.querySelector('#outerFrame').contentWindow.document;
const doc2 = doc1.querySelector('#innerFrame').contentWindow.document;
doc2.querySelector('input');
Automa 多层 iframe 选择器模板
iframe#outerFrame |> iframe#innerFrame |> input[name="username"]
iframe[src*="login"] |> iframe[name="content"] |> button.primary-btn
按文字找按钮并点击
const btn = Array.from(document.querySelectorAll('button'))
.find(el => el.innerText.includes('提交'));
if (btn) btn.click();
返回顶部
第 21 章:结语
- 我现在是在主页面,还是 iframe 里?
- 我的定位是不是唯一、稳定、真正可交互?
- 这个动作该普通执行,还是该用 JS 兜底?