用 Playwright + CDP 复用已登录 Chrome:多账号会话隔离的 5 个坑

举报
yd_234133654 发表于 2026/10/29 19:04:26 2026/10/29
【摘要】 自动化脚本每次 launch 新浏览器都要重新登录?本文记录用 CDP attach 复用已登录 Chrome 的实现方式,以及多账号场景下踩到的 5 个坑:profile 隔离、Local State 覆盖、单实例锁、登录态落盘位置,以及人机验证该不该自动化的边界。

为什么不用 launch,而用 CDP attach

很多自动化脚本习惯 browser.launch(),每次运行都开一个全新的临时 profile:没有登录态、没有已保存的密码,每次都要重新登录一遍。对需要长期维持多个账号登录态的场景(每日例行任务、运营后台巡检、多租户验证),更省事的做法是先让用户手动登录一次,之后用 CDP attach 到同一个 Chrome 实例,直接复用 profile 里的 Cookie。

from playwright.async_api import async_playwright

async def attach():
    async with async_playwright() as pw:
        browser = await pw.chromium.connect_over_cdp("http://127.0.0.1:9222")
        for ctx in browser.contexts:
            for page in ctx.pages:
                print(page.url, await page.title())

前置条件是 Chrome 带调试端口启动:

google-chrome \
  --user-data-dir=/path/to/profiles \
  --profile-directory="Profile 1" \
  --remote-debugging-port=9222 \
  --no-first-run --no-default-browser-check

这里有个容易混淆的点:--user-data-dir 指向的是浏览器实例级目录,--profile-directory 才是实例内部的账号位。两者组合起来,才唯一确定一个登录身份。

坑 1:靠标题区分账号,一定会串号

connect_over_cdp 连上的是整个浏览器,contexts 通常只有一个,所有 profile 的窗口都平铺在同一个 context 的 pages 里。

于是靠 title 区分账号几乎必然失败——同一个站点的两个账号,标签页标题往往一字不差。可靠的做法是从 URL 里找一个每会话唯一、又跟账号绑定的参数:

def pick(pages, marker):
    for p in pages:
        if marker in p.url:
            return p
    return None

华为云 SSO 回跳会在 URL 上带 ticket=ST-...,这个 ticket 就是天然的账号指纹。开新 tab 前先读一次 ticket,确认落在哪个账号上,再决定要不要操作。

坑 2:手工造 profile 目录,Chrome 会静默回落到 Default

手动 cp -a Default "Profile 1" 造目录是可行的,但必须同步改 Local State 里的 profile.info_cache,否则 Chrome 不认这个名字,仍然落到 Default:

import json
p = "/path/to/profiles/Local State"
d = json.load(open(p))
d["profile"]["info_cache"]["Profile 1"] = {
    "name": "account2",
    "is_using_default_name": False,
}
json.dump(d, open(p, "w"), ensure_ascii=False, indent=2)

改完必须完全退出 Chrome(包括后台残留进程)再启动,否则 Local State 会被内存里的旧值覆盖回去。我第一次就是改完直接开窗口,结果两个窗口登的都是同一个账号。

坑 3:同一个 user-data-dir 起不了两个进程

如果想让两个账号"各占一个浏览器进程",会直接失败:Chrome 对 user-data-dir 加了单实例锁,第二个进程只是把 URL 转发给第一个进程然后退出。

所以"两个账号"只能是同一实例的两个窗口,不能是"两个独立的 Chrome"。这一点决定了自动化的写法:收尾不能调 browser.close(),那会把用户正在用的窗口一起关掉;只关自己新开的 tab。

坑 4:登录态落在 profile,不在浏览器

Cookies、Login Data、Local Storage 全部位于 ~/.config/google-chrome/<Profile>/ 下。这意味着三件事:

  • 想在 A 账号下做事,就必须在 A 的 profile 窗口里操作;
  • 只要 profile 目录还在,重启 Chrome 登录态就还在;
  • 反过来,删掉 profile 目录就等于登出,不需要别的清理动作。

顺带记一个反直觉的细节:密码末尾的标点算在密码里。少一个句点,站点返回的是"账号或密码错误",而不是"格式错误",很容易误判成密码泄露或账号被锁。

坑 5:人机验证不要试图自动化

密码验证通过后,站点常会弹滑块拼图。我试过按缺口做边缘检测算出精确偏移、按 DOM 校准拖动比例、模拟人类加减速轨迹——连续九次全部被判失败,还触发了一次"失败过多"。

原因是这类验证判的不只是轨迹,还有环境指纹:无 GPU 的虚拟桌面、--remote-debugging-port 暴露的调试特征、合成事件缺少的真实输入设备属性。在自动化环境里,它本来就是"机器人环境",拖得再准也没用。

正确的处理是换通道,而不是硬刚:

  1. 优先用邮箱验证码;
  2. 没有邮箱就用短信验证码;
  3. 两者都不通,把窗口交回人工,拖一次就过。

同一套登录流程,密码登录必弹滑块,手机号加短信验证码一次就过——这不是运气,是风控策略的差异。

一张 checklist

  • --user-data-dir 与 --profile-directory 成对使用
  • 改 Local State 前先彻底退出 Chrome
  • 靠 URL 里的唯一参数识别账号,不靠标题
  • 只关自己开的 tab,不动用户的窗口
  • 验证环节换通道,不硬拼轨迹
  • 每次敏感操作前读一遍页面上的账号标识,确认没串号

小结

CDP attach 的价值在于复用真实登录态,代价是要接受浏览器是共享资源、不能用进程隔离来做账号隔离。把隔离维度从"进程"换成"profile 加 URL 指纹",这套方案在长期运行的任务里是稳的;真正必须人工介入的,只剩人机验证和二次验证码这一步。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。