使用前先看:快速上手

这份手册可以按两种方式使用:新手建议从第 1 章顺着读一遍;已经在做流程时,可以直接搜索报错现象、元素类型或关键词,例如 iframe输入不生效Shadow DOM

刚开始搭流程

先看定位原则和 XPath / CSS 选择方法,避免一开始就复制不稳定的绝对路径。

去看定位方式
流程已经失败

先判断是不是层级、时机、元素选错,再进入故障排查章节逐项验证。

去看故障排查
想直接套模板

登录、表单、弹窗、iframe、联想词、表格行按钮都有可改造的流程骨架。

去看模板库

开始前的 5 个确认

  • 目标元素是否在 iframe 或 Shadow DOM 里。
  • 选择器在浏览器 Console 中是否能稳定找到,而且最好只找到 1 个。
  • 操作的是实际可交互元素,例如真正的 inputtextareabutton
  • 是否使用 Wait for element 等待关键元素,而不是只靠固定 Delay。
  • 页面发生跳转、翻页、弹窗关闭或列表刷新后,是否重新定位了元素。
版本提醒:Automa 的界面名称和 block 配置项可能随版本变化。本文重点讲的是通用排查思路:先确认层级,再确认选择器,再确认等待时机,最后选择普通动作或 JavaScript 兜底。
返回顶部

第 1 章:为什么 Automa 经常失败

很多人刚开始用 Automa,最容易遇到的问题就是:找不到元素、明明看见元素但选不到、能选到输入框但输入失败、能选到按钮但点击没反应、同一个流程有时成功有时失败。

这些问题通常不是 Automa 本身坏了,而是因为真实网页不是静态页面。一个网页里可能同时存在 iframe、异步加载、动态 class、遮罩层、React / Vue 事件接管、Shadow DOM、页面重绘等情况。

真正稳定的自动化,核心不是“复制一个 XPath”,而是:找对元素、等对时机、用对动作。
  • 层级对:你得先确认自己是在主页面、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、复杂交互页面。

一般建议是:常规定位优先 CSS,按文字和复杂结构优先 XPath,实在不行用 JavaScript。

3.1 选择器优先级

优先级推荐依据示例稳定性
1唯一且稳定的 id / name / data-* 属性input[name="username"]
2业务含义明确的 placeholder / aria-labelinput[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
对于大多数常规输入框、按钮、列表项,CSS Selector 往往比 XPath 更简洁。
返回顶部

第 6 章:XPath 和 CSS 怎么选

  • 优先找稳定属性:id、name、placeholder、data-*。
  • 稳定 class:优先 CSS。
  • 按文字定位:优先 XPath。
  • 复杂父子兄弟关系:优先 XPath。
  • 普通办法都不稳:上 JavaScript。
一句话总结:常规定位优先 CSS,按文字和复杂关系优先 XPath,实在不行用 JS。
返回顶部

第 7 章:iframe 核心操作

iframe 就是页面里嵌了另一个页面。你肉眼能看到元素,不代表 Automa 当前就在那个层里。

<iframe id="loginFrame" src="login.html"></iframe>

默认情况下,你是在主页面中查找元素,但目标元素其实在 iframe 内部,所以必须先切进去。

  1. 打开页面
  2. Wait for iframe 出现
  3. Switch Frame 到该 iframe
  4. 再操作 iframe 内部元素
#loginFrame
input[name="username"]
input[name="password"]
//button[contains(text(),"登录")]
很多人会跳过 Switch Frame,然后后面所有定位都失败。

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
重点:遇到多层 iframe 时,优先用一个 iframe 组件或一个支持 iframe 选择器的位置,把完整路径用 |> 串到最后一个 iframe 内的目标元素。

8.1 为什么 |> 更稳定

可以把 |> 理解成“进入这个 iframe 后,再继续在里面查找右边的选择器”。整条表达式一次性描述了从主页面到目标元素的完整路径。

  • 路径完整:Automa 知道目标元素在外层 iframe 里的内层 iframe 里,而不是只知道当前要切某一层。
  • 上下文更清楚:多个 iframe 组件分开写时,后一个组件可能没有沿用你以为的上一层上下文,尤其是页面刷新、等待、重绘或 block 执行顺序变化后更容易失效。
  • 更方便排查:一条表达式从左到右读,就能看出卡在外层 iframe、内层 iframe,还是最终元素。
  • 更不容易选错:页面有多个 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 选择器的顺序

  1. 先在开发者工具里确认目标元素到底在哪一层 iframe 里。
  2. 从最外层 iframe 开始写,逐层向里,不要跳层。
  3. 每一层 iframe 尽量用稳定特征,例如 idnamesrc*
  4. 最后一段才写目标元素,例如输入框、按钮、列表项。
外层 iframe |> 内层 iframe |> 最终元素
不要只写 iframe |> iframe |> input。页面上只要多一个 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、关闭弹窗后旧元素失效。
优先使用 Wait for element,不要只靠固定 Delay 猜页面速度。
<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();
如果你遇到“明明看得见却永远定位不到”,就要怀疑是不是 Shadow DOM。

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 登录页自动输入用户名密码

  1. Wait 用户名输入框
  2. 输入用户名
  3. 输入密码
  4. Wait 登录按钮
  5. 点击登录
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 内登录框操作

  1. Wait #loginFrame
  2. Switch Frame #loginFrame
  3. 输入用户名密码
  4. 点击登录

15.5 多层 iframe 输入内容

多层 iframe 推荐把外层、内层和最终元素用 |> 串成一条完整路径。

iframe#outerFrame |> iframe#innerFrame |> input[name="keyword"]
  1. Wait 外层 iframe 出现
  2. 在 iframe 组件或元素选择器中填写完整 |> 路径
  3. 直接操作最后一层 iframe 内的目标元素

15.6 联想词处理

  1. 输入关键词
  2. Wait 联想列表出现
  3. 点击目标项
(//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 同一流程偶尔成功偶尔失败

修复思路:用 Wait 替代短 Delay,让选择器更唯一,页面变化后重新定位。

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 章:给小白的稳定写法建议

  1. id
  2. name
  3. placeholder
  4. data-* 属性
  5. 稳定 class
  6. 按文字
  7. 按结构关系
  8. 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 章:通用排查流程

  1. 先看是不是 iframe。
  2. 在浏览器 Console 验证选择器。
  3. 确认选择器是否唯一。
  4. 确认页面是否加载完成。
  5. 确认你选到的是不是实际可交互元素。
  6. 普通 block 不行就换 JS。
  7. 页面变化后重新定位。
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();
以后遇到任何问题,都先按这个顺序排查,不要第一反应一直换 XPath。
返回顶部

第 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 章:结语

先确认层级,再确认元素唯一,再确认页面时机,最后执行动作。
  1. 我现在是在主页面,还是 iframe 里?
  2. 我的定位是不是唯一、稳定、真正可交互?
  3. 这个动作该普通执行,还是该用 JS 兜底?
层级对,元素对,时机对。
返回顶部