用 CDP 养一个常驻 Chrome,踩过的几个坑
用 CDP 养一个常驻 Chrome,踩过的几个坑
扫码登录这种活,用完就关的浏览器不行。登录态在内存 cookie 里,进程一退就没了,第二天还得再扫。所以得养一个带固定 profile 的常驻 Chrome,任务来了 attach 上去点两下,任务走了它继续挂着。
这套东西断断续续跑了一阵子,坑基本集中在连接和等待上。
1. target 会越积越多
最开始的写法是每次任务 Page() 都开一个新标签页。用久了之后,/json/list 里能翻出一屏 about:blank。内存没爆,但每次遍历 target 都要多花几十毫秒,而且偶尔会 attach 到错的页面上。
后来改成:启动时只留一个 target,任务里先查 /json/list,有 page 就复用,没有才新建。
curl -s localhost:9222/json/version | jq -r .Browser
curl -s localhost:9222/json/list | jq -r '.[] | select(.type=="page") | .url'
判断浏览器活没活,看 /json/version 就够了,不要去 pgrep。进程在但 CDP 端口没起来的情况很常见,尤其是 headless 刚启动那两秒。
2. Page.navigate 返回了不等于页面能用
CDP 的 Page.navigate 只表示导航开始了。返回之后立刻去读 DOM,拿到的是上一个页面或者空壳。我一开始用 sleep 8 兜底,在本地挺稳,换到慢一点的网络就开始随机失败。
后来改成轮询一个业务特征,而不是等固定时间:
for _ in range(30):
if page.js("document.querySelector('.user-info') !== null"):
break
time.sleep(0.5)
等具体元素比等秒数靠谱。秒数是在猜网络,元素是在等结果。
3. cookie 拿不全
Network.getAllCookies 在 page 会话里经常报不存在。换成 Storage.getCookies(浏览器级)才能拿到 httpOnly 的那部分。这两个接口返回的字段还不完全一样,Storage 给的是 expires 浮点数,Network 有时候给 -1。
导出快照的时候两个都试一遍,谁先成功用谁。别只写一个,换台机器就翻车。
4. headless 的 UA 太诚实
HeadlessChrome/131.0.0.0 这个串挂在请求头里,部分站点直接按机器人处理,页面能打开但接口返回 403。用 Network.setUserAgentOverride 换成一个普通的 Chrome UA,问题消失。
顺带一提,改 UA 之后有些前端会走不同的渲染分支,布局会变,选择器可能失效。改 UA 不是纯好处,得跟着测一遍。
5. 连接记得关
每个 Page() 都开一条 WebSocket。任务里创建了五六个 Page 对象又忘了 close(),进程不退,句柄一直涨。跑久了连 Chrome 都开始变慢。
现在统一用 try/finally,或者干脆做成上下文管理器。
一个能用的最小配置
--headless=new
--no-sandbox
--disable-dev-shm-usage
--user-data-dir=/path/to/profile
--remote-debugging-port=9222
--proxy-server=http://127.0.0.1:8897
--disable-dev-shm-usage 在容器里几乎是必须的,/dev/shm 默认只有 64M,Chrome 渲染大页面会直接崩,报错还特别隐晦。
profile 目录要放在持久卷上。放在容器临时层里,重建一次登录态就没了,等于白养。
- 点赞
- 收藏
- 关注作者
评论(0)