无头环境自动化自愈指南:浏览器消失、依赖清理与命令挂起的四类故障恢复
前言
最近连续几天跑自动化任务,经历过浏览器"凭空消失"、pip 包被清理、命令无故挂起、二维码扫不上……把每一次故障排查沉淀下来,最后形成了一套自愈方案。这篇把最典型的四个故障场景和对应的恢复套路写出来,希望能帮到同样在无头环境里跑自动化的同学。
场景一:Playwright 浏览器"不见了"
现象:明明昨天还能用,今天报错:
BrowserType.launch: Executable doesn't exist at
/root/.cache/ms-playwright/chromium_headless_shell-1243/...
原因:容器环境的缓存目录被定期清理,浏览器二进制随之消失。
标准恢复:python3 -m playwright install chromium。但如果官方 CDN 慢(实测 148 KB/s,186MB 要 21 分钟),就需要换源:
PLAYWRIGHT_DOWNLOAD_HOST=https://cdn.npmmirror.com/binaries/playwright \
python3 -m playwright install chromium
更快的方案(实测 18-21 MB/s):直接下载 headless shell 压缩包再手动构造目录:
curl -sL "https://cdn.npmmirror.com/binaries/playwright/builds/chromium/1243/chromium-headless-shell-linux-arm64.zip" -o /tmp/hs.zip
mkdir -p ~/.cache/ms-playwright/chromium_headless_shell-1243
cd ~/.cache/ms-playwright/chromium_headless_shell-1243
unzip -qo /tmp/hs.zip
mv chrome-linux chrome-headless-shell-linux-arm64
ln -sf headless_shell chrome-headless-shell-linux-arm64/chrome-headless-shell
touch INSTALLATION_COMPLETE
三个关键点:目录名必须与 Playwright 期望的版本号一致(用报错信息里的路径),可执行文件名要软链成 chrome-headless-shell,最后创建 INSTALLATION_COMPLETE 标记文件。
场景二:pip 包"每天消失"
现象:脚本报 ModuleNotFoundError: No module named 'qrcode',但前几天明确装过。
原因:同场景一,Python 包目录被清理。
恢复:把所有依赖收敛成一行安装:
python3 -m pip install -q qrcode zxing-cpp pillow numpy playwright
预防:把"依赖自检"放进任务脚本开头,缺包就自动安装,而不是等报错。一个 20 行的 bootstrap 脚本能省掉 80% 的手动恢复时间。
场景三:命令静默挂起
现象:timeout 120 python3 long_task.py 执行后卡住,无输出无退出,最终 bash 报 “Promise never resolved”。
根因分析(踩了多次才定位):
- 执行环境的命令包装层对超过约 40-60 秒且输出稀疏的命令会挂起
- 挂起后,下一条命令的执行会连带杀掉之前的脚本进程
- 所以"脚本挂起但实际已执行"的假象经常出现
解法一:心跳输出
脚本内每 3-4 秒打印一行进度:
for i, step in enumerate(steps, 1):
print(f'HB: [{i}/{len(steps)}] {step}', flush=True)
关键在 flush=True,否则输出在缓冲区里等于没有。
解法二:后台脱离 + 日志轮询(最可靠)
(nohup python3 task.py > /workspace/task.log 2>&1 &)
# 之后用短命令轮询日志:tail -3 task.log
注意必须用子 shell 包裹 ( ... &),否则进程可能仍受父 shell 生命周期影响。
解法三:不用 sleep
sleep 60 这类"静默等待"本身就是挂起的高发命令。需要等待时用带输出的轮询循环替代。
场景四:登录/二维码扫描失败
现象 A:短信验证码登录按钮点不动
页面按钮 class 带 hwid-disabled,表单全填了、协议勾了,点击无任何反应。
排查发现三个叠加问题:
- 验证码过期会导致按钮禁用(但页面不提示,静默)
- 协议勾选代码有 bug:
checked = true后又click()等于取消勾选 - 页面有多个"登录/注册"文案元素,
get_by_text().first匹配到了非按钮节点
有效解法:用物理鼠标坐标点击:
pos = await page.evaluate("""() => {
const b = document.querySelector('.hwid-btn-primary');
const r = b.getBoundingClientRect();
return {x: r.x + r.width/2, y: r.y + r.height/2};
}""")
await page.mouse.move(pos['x'], pos['y'])
await page.mouse.click(pos['x'], pos['y'])
现象 B:终端 ASCII 二维码扫不上
把二维码转成字符画在终端显示,手机完全扫不出来。原因:终端字体对全角字符的渲染宽度不一致,二维码模块比例被破坏。
解法:二维码本质是一个 URL,直接把这个 URL 发到手机打开,效果与扫码完全等价。扫码登录页面的 scanlogin?qrcode_ticket=... 链接,手机浏览器打开后同样能确认登录。
一句话总结
无头环境自动化的三个自愈原则:
- 依赖自愈:脚本开头做依赖自检,缺什么装什么;下载走镜像
- 进程自愈:后台化 + 心跳日志 + 结果文件,不依赖命令的同步返回
- 交互自愈:优先物理坐标点击;二维码场景直接传 URL 而非字符画
把这些写进脚本模板,环境再怎么清理,恢复成本都能压到几分钟内。
- 点赞
- 收藏
- 关注作者
评论(0)