ARM64 服务器上的日常自动化:判据、回执与等待的边界
背景
过去半年我在一台 ARM64 Linux 服务器上维护一套日常自动化脚本,目标是每天自动完成一些固定动作:签到、看文档、提交表单。脚本本身不复杂,真正消耗时间的不是业务逻辑,而是“为什么昨天能跑、今天就被拦住”。反复排查之后,我把这个问题拆成了三层:环境指纹、行为序列、以及判定逻辑。这篇笔记记录后两层的经验,因为它们和具体站点无关,换一个站点照样适用。
一、先分清“失败”和“被判定为失败”
最常见的坑是把自动化自己的判定错误当成站点风控。举例:脚本判断文章发布成功的方式是“页面里没有出现『请填写』这类提示”,而编辑器页顶部常驻一句提示用户阅读使用指南的文案,正好命中正则。于是文章已经真实提交、页面正在跳转,脚本却报失败。第二天换一个写法再跑,又报失败,几次之后就会误以为账号被限流。
后来我给所有提交类动作定了统一判据,按可信度排序:
- 提交接口的响应体里出现实体 ID(例如 blogId)或 msg:success;
- URL 跳到目标路由,例如 processcomplete 这类状态页;
- 页面出现结构性 DOM 标志,例如页头的用户名节点;
- 页面文案只用于解释原因,绝不用于判定成败。
这条规则落地之后,误报率立刻降到接近零。同理,判断“是否已登录”也不能用“页面里没有出现登录按钮”这种反向条件,因为登录页本身也可能不渲染出那个按钮。
二、异步动作的回执不能当判决
浏览器自动化工具在点击后会返回一段回执,常常写着“没有可归因于这次操作的变化”。一开始我把它当成失败,于是重点一次,结果变成了重复提交。真实情况是:点击已经触发,但效果是异步的——请求发出、服务端处理、页面整页跳转,整个链路几百毫秒到几秒,工具只看了即时焦点和尺寸变化,自然什么都没发现。
正确的做法是点击之后主动去读状态,而不是读回执:读 location.href、读提交接口的响应、读目标元素是否已从 DOM 消失。还有一个反向的经验:如果点击后工具报 frame removed 之类的错误,往往说明页面正在跳转,是成功信号而不是崩溃,此时千万不要重试。
三、等待与轮询的边界
另一个反复出现的问题是超时归因。元素不好点、脚本报超时,第一反应往往是“页面慢,加长等待”,但实测有相当比例的超时与页面快慢无关:
- 页面被静默重定向到了登录页,目标元素当然永远等不到;
- 元素存在但宽度为 0,因为它在未激活的 tab 面板里,父级 display:none;
- 整个浏览器窗口被最小化,视口尺寸归零,任何按坐标的计算都失效。
这三种情况的共同点是:等待再久也不会变好。所以排查顺序应该是先读 location.hostname、再读元素的 getBoundingClientRect()、最后读 innerWidth 和 visibilityState,确认环境是否完整,再决定要不要加重试。
四、把调试成本降下来
还有一条与代码无关但很重要的经验:调试期间不要反复用真实账号登录。每次登录都是一次真实的风险评分累加,且不可回滚。我最初的调试方式是改一行代码就跑一遍全流程,一天之内登录几十次,最后连原本能正常登录的账号也开始出现验证码。
后来改成三个习惯:一是把探针拆小,只测出问题的那一步,比如只走到密码框出现就停;二是每步都打印原始证据——URL、接口响应体、元素尺寸,而不是只打印一个通过或失败;三是同一个结论只验证一次,确认之后写进文档,不再反复做 A/B。这三条把调试时间压下去一大半,也把账号风险降到了可控范围。
五、小结
自动化脚本的稳定性,大部分不取决于选择器写得多巧妙,而取决于三件事:判据是否正向、回执是否被当成事实、以及失败时能不能拿到原始证据。把这三件事做对之后,脚本仍然会失败,但每次失败都能定位到具体环节,而不是陷在“是不是被风控了”的猜测里。
- 点赞
- 收藏
- 关注作者
评论(0)