无头服务器上的浏览器自动化:Xvfb + 常驻 Chromium + CDP 实战
在无图形界面的 Linux 服务器上做浏览器自动化,最难受的往往不是写代码,而是两件事:登录态没法复用,出了故障只能靠日志猜。这篇文章记录一套实测可用的组合:Xvfb 提供虚拟显示,常驻 Chromium 暴露 CDP 调试端口,noVNC 给人工兜底,Playwright 通过 connect_over_cdp 直接接管同一个浏览器实例。
一、为什么不让每次任务都新建浏览器
大多数脚本的默认写法是 browser = pw.chromium.launch(),每次跑完就销毁。这在无人值守场景里有个致命问题:需要短信验证码的站点,每次都得有人重新登录。
换一个思路:让浏览器长期活着,登录由人做一次,后续所有脚本都连上去复用同一个 profile。Cookies、localStorage、IndexedDB 全部留在磁盘上的 --user-data-dir 里,进程重启也不丢。
二、三块拼图
1. 虚拟显示。 没有物理屏也要有 DISPLAY,否则有头浏览器起不来:
Xvfb :1 -screen 0 1600x900x24 &
export DISPLAY=:1
2. 常驻 Chromium。 关键是 --remote-debugging-port 和固定的 --user-data-dir:
chromium \
--user-data-dir=/root/.remote-chrome \
--remote-debugging-port=9222 \
--remote-debugging-address=127.0.0.1 \
--no-first-run --no-default-browser-check \
--window-size=1600,900
用 systemd 托管,Restart=always,崩了自动拉起来,不需要人守着。
3. 人看的入口。 x11vnc 抓 Xvfb 的帧缓冲,websockify 把 VNC 转成 WebSocket,前面套一层 Basic 认证,就能在浏览器里点鼠标了。这一层是"故障兜底":脚本卡住时,人可以直接看屏幕、手动点一下。
三、Playwright 侧的写法
from playwright.sync_api import sync_playwright
with sync_playwright() as pw:
browser = pw.chromium.connect_over_cdp("http://127.0.0.1:9222")
ctx = browser.contexts[0] # 复用已登录的上下文
page = ctx.pages[0] # 复用已有标签页
connect_over_cdp 和 launch 的区别在于:它不创建新 profile,拿到的是正在跑的那个实例。所以登录态、扩展、甚至当前打开的页面都是现成的。注意 browser.close() 在 CDP 连接下只断开连接,不会杀掉浏览器进程,这一点很符合我们的需求。
四、四个踩过的坑
坑 1:CDP 端口别暴露到公网。 CDP 的权限等价于"控制整个浏览器",能读所有 Cookie、能执行任意 JS。必须显式写 --remote-debugging-address=127.0.0.1。要看界面,走 VNC 那条独立通道,别为了省事把 9222 开到 0.0.0.0。
坑 2:"element is not visible"不一定是真的看不见。 很多 Vue/React 页面会同时渲染桌面端和移动端两份 DOM,其中一份 display:none。Playwright 的自动等待会挑到隐藏的那一份然后报错。改法是在页面里用 JS 自己选:
page.evaluate("""() => {
const els = [...document.querySelectorAll('.status-btn__text')];
const el = els.find(e => e.offsetParent !== null);
el.closest('button').click();
}""")
坑 3:判断登录态要挑权威页面。 首页/列表页的头部经常渲染缓存数据,看起来还登录着,实际会话早就过期。找一个"未登录必然重定向"的页面(比如控制台首页),看 URL 有没有被踢到登录页,比读页面文字可靠得多。
坑 4:内容型站点优先用原生输入。 CodeMirror、contenteditable 这类编辑器直接 fill() 有时不触发内部状态更新,标题能进、正文却是空的。稳妥做法是先点进编辑区拿到焦点,再用 keyboard.insert_text() 逐字写入,让它走完整的 beforeinput 事件链。
五、成本与收益
一次性成本大概是一个小时:两个 systemd unit、一个 VNC 反代配置。换来的收益是——登录态可以跨任务复用,定时任务不再因为一次验证码全军覆没;出错时能直接看屏幕,不用盲猜选择器;同一套环境既能给人看,又能给脚本用。
如果你的自动化里也有"必须人工登录一次"的环节,这套结构的投入产出比相当高。
- 点赞
- 收藏
- 关注作者
评论(0)