znlgis 博客

GIS开发与技术分享 — GDAL · GeoServer · PostGIS · QGIS · OpenLayers · Cesium · FreeCAD · NPOI

第07章 - 动作详解(三):等待、延时与滚动

自动化流程的成败,很大程度上取决于「时机」——页面还没加载完就去点击,必然失败。本章讲解三类控制节奏的动作:wait(等待元素出现)、wait_gone(等待元素消失)、sleep(固定延时),以及 scroll(滚动)。其中 wait 系列是编写健壮工作流的关键。

7.1 为什么需要等待

桌面与 Web 应用的响应是异步的:点击一个按钮后,新页面/弹窗需要时间加载。如果工作流不加等待,紧接着就去操作还没出现的元素,就会「找不到元素」而失败。

robotgo-flow 提供了两种截然不同的等待思路:

  • 智能等待wait / wait_gone):轮询检测某个元素是否出现/消失,一旦满足条件立即继续,最长等到超时。这是推荐方式——既不会白等,也不会过早操作。
  • 固定延时sleep):无条件等待固定的秒数。简单粗暴,但要么等太久浪费时间,要么等太短仍失败。仅在无法用元素判断状态时使用

7.2 wait:等待元素出现

wait 会轮询查找某个模板,直到它出现或超时。两种写法:

# 简写:使用全局默认超时(settings.element_timeout)
- wait: "templates/loading_done.png"

# 完整写法:为这次等待单独指定超时(秒)
- wait: {template: "templates/popup.png", timeout: 30}

对应源码:wait 字段既接受字符串(模板路径,使用默认超时),也接受包含 templatetimeout 的映射(parseWait 解析)。执行时调用 eng.WaitForElement(template, timeout)

7.2.1 等待的内部机制

引擎的 WaitForElement 通过 matcher.go 中的轮询实现:每 200ms 检测一次目标模板是否能被找到,直到找到(成功返回)或达到超时(失败)。查找本身遵循「窗口内优先、全屏回退」的策略(详见第 10 章)。

7.2.2 典型用法

wait 通常紧跟在会触发页面变化的动作之后:

steps:
  - name: "打开并等待加载"
    actions:
      - open_url: "https://example.com/dashboard"
      - wait: "templates/dashboard_ready.png"    # 等仪表盘加载出来
  - name: "点击并等待弹窗"
    actions:
      - click: "templates/btn_new.png"
      - wait: {template: "templates/dialog.png", timeout: 15}  # 等弹窗,最多 15 秒
      - click: "templates/dialog_ok.png"

7.3 wait_gone:等待元素消失

wait_gonewait 相反,它等待某个元素消失

- wait_gone: "templates/loading.png"

最典型的场景是等待加载动画消失:很多页面加载时会显示一个 loading 转圈图标,加载完成后消失。用 wait_gone 等这个图标消失,就能准确判断「加载完成」。

内部通过 WaitForElementGone 实现:轮询查找模板,直到 FindElement 返回错误(即找不到该元素,意味着它已消失)或超时。

steps:
  - name: "提交并等待完成"
    actions:
      - click: "templates/btn_submit.png"
      - wait_gone: "templates/loading_spinner.png"   # 等 loading 消失
      - wait: "templates/success_msg.png"            # 再等成功提示出现

waitwait_gone 配合使用,可以精确地卡准异步操作的起止时机。

7.4 sleep:固定延时

sleep 无条件等待固定的秒数,支持小数:

- sleep: 2.5     # 等待 2.5 秒
- sleep: 0.5     # 等待半秒

对应 ActionSleep float64 字段。sleep.go 中的实现直接使用 Go 的 time.Sleep,等待 seconds * time.Second

7.4.1 什么时候用 sleep

sleep 应作为「最后手段」,仅在无法用元素状态判断时机时使用,例如:

  • 目标程序没有明显的「加载完成」视觉标志;
  • 需要等待一个纯粹的时间过程(如动画播放、倒计时);
  • 在两个快速操作之间留出极短的缓冲。

7.4.2 sleep 与 wait 的取舍

对比项 wait / wait_gone sleep
等待依据 元素出现/消失 固定时间
效率 条件满足立即继续 必须等满设定时间
健壮性 高,适应快慢网络 低,时间写死
适用场景 有明确视觉标志 无法用元素判断时

经验法则:能用 wait/wait_gone 就不要用 sleepsleep 写多了,工作流会变得又慢又脆。

7.5 scroll:滚动

scroll 用于滚动页面或列表,值是一个整数,正数向下、负数向上,数值代表滚动量:

# 向下滚动
- scroll: 500

# 向上滚动
- scroll: -300

对应 ActionScroll int 字段。scroll.go 的执行逻辑:

  • amount > 0eng.ScrollDown(amount)
  • amount < 0eng.ScrollUp(-amount)
  • amount == 0 → 什么也不做。

7.5.1 滚动的底层行为

引擎的 ScrollDown/ScrollUp

  • 普通模式:直接调用 robotgo.ScrollDir 一次性滚动;
  • 人类模式(启用滚动抖动):把滚动量拆成 1~3 单位的小步分块执行,步间有 30~120ms 间隔,并有 20% 概率在结束后轻微反向回滚 1~2 单位,模拟真实滚动的不精确性。

7.5.2 典型用法:滚动到目标再操作

模板匹配只能找到当前屏幕上可见的元素。如果目标在页面下方看不见,需要先滚动让它进入视野:

steps:
  - name: "滚动找到并点击"
    actions:
      - scroll: 800                          # 向下滚动
      - wait: "templates/target_button.png"  # 等目标进入视野
      - click: "templates/target_button.png"

对于很长的页面,可能需要多次滚动逐步查找。

7.6 综合示例:处理异步加载列表

结合本章的动作,处理一个「加载列表 → 等待完成 → 滚动浏览 → 点击项」的流程:

steps:
  - name: "打开列表页"
    actions:
      - open_url: "https://example.com/items"
      - wait_gone: "templates/loading.png"      # 等加载动画消失
      - wait: "templates/list_container.png"     # 等列表容器出现
  - name: "滚动并选择"
    actions:
      - scroll: 600                              # 向下滚动
      - sleep: 0.5                               # 给渲染留点时间
      - wait: "templates/target_row.png"         # 等目标行出现
      - click: "templates/target_row.png"        # 点击目标行

7.7 常见问题

问题 可能原因 解决方法
频繁「等待超时」 超时设得太短、模板不准 增大 timeoutelement_timeout;检查模板
明明加载完了还在等 wait_gone 的模板选得不好 选一个确实会消失的元素(如 loading 图标)
滚动后找不到元素 滚动量不够、渲染延迟 增大滚动量或分多次滚动,配合短 sleep
工作流整体很慢 滥用 sleep wait/wait_gone 替换固定延时

7.8 小结

本章我们讲解了控制节奏的四种动作:wait 智能等待元素出现(每 200ms 轮询,可指定超时)、wait_gone 等待元素消失(适合等加载动画结束)、sleep 固定延时(最后手段)、scroll 滚动(正下负上,人类模式分块执行)。核心经验是:优先用智能等待,慎用固定延时。下一章,我们讲解一组专为浏览器场景设计的动作。