浏览器、profile 与登录态
本文说明当前工程「用哪个浏览器、用哪份 profile、登录态存在哪」,以及换浏览器带来的实际后果。前文 08 讲的是请求与响应的形状,本文讲的是浏览器本身。
一、一个浏览器,多个任务
浏览器是全进程共享的:一个浏览器进程、一份 profile,任务之间靠页签隔离。
| 行为 | 说明 |
|---|---|
第一次 start | 把浏览器拉起来 |
之后每次 start | 只认领或新开自己的页签 |
close | 只关掉该任务自己的页签 |
最后一个任务 close | 浏览器跟着退出 |
shutdown | 关掉全部任务与共享浏览器,服务进程不退出 |
为什么不能一个任务一个浏览器。 用户数据目录天生是单例:同一个 User Data 目录同时只允许一个浏览器进程,第二个进程会把命令行交给已有实例然后自己退出。所以「共用一份 profile、登录态只养一次」和「一个任务一个浏览器」只能二选一 —— 这里选了前者。
代价是上下文级设置是所有任务共享的(Cookie、地理位置、离线、额外请求头、权限),因为它们本来就挂在同一个浏览器上下文上;get_browser_state 只列任务自己的页签,switch_tab 的索引也只在任务自己的页签里数。
二、start 的 browser 参数
{"method":"start","params":{"browser":"edge","headless":false}}
| 取值 | 浏览器 | 引擎 | profile | 什么时候用 |
|---|---|---|---|---|
不传 / auto | 本机 Google Chrome,没装则退回内置 Chromium | Chromium | 托管 profile | 默认(推荐) |
chrome | 只用本机 Google Chrome | Chromium | 托管 profile;打开 browser.chrome.useUserProfile 时是用户自己的 User Data | 需要 Google 登录之类的站点,或要确认「本机 Chrome 下的表现」 |
chromium | 只用内置的那份(发行包内嵌),完全不碰本机 Chrome | Chromium | 托管 profile | 怀疑本机 Chrome 的扩展或登录态干扰时做对照 |
edge | 只用本机 Microsoft Edge | Chromium | Edge 专用托管 profile | 站点在 Chrome 下用不了,或要对照两个浏览器的差异 |
firefox | Playwright 自带的那份 Firefox | Firefox | 托管 profile | 站点在 Chromium 下用不了,见第五节 |
别名也认(msedge / google-chrome / bundled / ff 等);写了不认识的值会直接失败并列出可选值。
auto 与显式取值的区别是「能不能悄悄换一个」:不传 browser 时服务按老规矩退让(本机没有 Chrome 就用内置 Chromium),并把原因写进 data.browser.note;显式写了 chrome / edge 就是「我就要这个」,没装会直接报错并说清怎么改 —— 否则「明明要了 Edge,结果用 Chrome 跑出另一种页面」这类问题很难查。
一次只能有一个浏览器。 类型可以在任务之间切换(把在跑的任务 close 掉再 start),但不能在任务运行中切换:那会连累别的任务的页签,服务会明确报错。想同时用两个浏览器(例如一边 Chrome 一边 Edge),再起一个服务进程,给它们配不同的端口与 profile 目录。
回执里的「我要的」与「实际用的」
{"data":{"id":"1001","requestedBrowser":"firefox","effectiveBrowser":"firefox","engineHonored":true,
"browser":{"type":"firefox","engine":"firefox","mode":"managed","headless":false,
"profileDir":"~/.config/browseruse/profiles/shared-10049"}}}
| 字段 | 含义 |
|---|---|
requestedBrowser | 这次请求里写的那个值 |
effectiveBrowser | 实际落到的那个(auto 不会出现在这里) |
engineHonored | 服务实例有没有按 browser 参数切浏览器 |
mode | managed(持久化上下文)或 cdp(自己拉进程 + CDP) |
profileDir | 这次真正用的 profile 目录 |
profileSeenBefore | 这份 profile 在这次启动之前就已经有内容了吗 |
note | 没能用上用户 profile 时,退回托管 profile 的原因 |
profileNote | 对「这份 profile 里有没有登录态」的说明,例如「之前用过(上次是 browser=chrome),里面已有的登录态应该还在」 |
previousEngine / previousBrowser | 上次用这份 profile 的引擎与浏览器类型 |
engineHonored:false 表示这个服务实例没有按 browser 参数切浏览器(老版本发行包只认 headless,会静默忽略 browser,回执照样 ok:true),此时 data.engineWarning 会说明原因。要不要用 get_config 看服务端认的 engine / configuredType 也行。
profileSeenBefore 回答的是「要不要重新登录」:它只说明这份 profile 里有内容,不保证某个具体站点的登录态仍然有效。
三、profile 目录:默认按端口分开
托管 profile 默认在 ~/.config/browseruse/profiles/ 下,默认按服务端口派生目录名:
| 配置 | 解析结果 | 效果 |
|---|---|---|
| 不配(默认) | shared-<端口>,例如 shared-10049 | 多个实例各一份 profile,不抢锁 |
browser.profileDir.perPort=false | shared | 回到所有实例共用一份的老行为 |
browser.profileDir=<路径> | 指定值 | 优先级最高;要用同一份已登录的会话就指到同一个目录 |
当前解析到哪个目录用 get_config 或 list_tasks 看,start 回执里的 data.browser.profileDir 也是同一个值。
换端口等于换一套登录态,想复用旧会话就显式配
browser.profileDir。
profile 目录是跟着「浏览器产品」走的:内置 Chromium 与本机 Chrome 共用 browser.profileDir(同一 Chromium 家族);Edge 单独一份(browser.edge.profileDir);Firefox 沿用 browser.profileDir。所以换浏览器等于换一套登录态,需要登录的站点要重新登一次。
Edge 单独一份的原因:Edge 打开 Chrome 的 User Data 会把它当外来 profile 处理,而且两家 Cookie 的 App-Bound 加密密钥不同,混用只会得到一份读不出登录态的目录。
四、用用户自己那份 Chrome profile(CDP 模式)
browser.chrome.useUserProfile=true 时会走自己拉 Chrome + 远程调试端口 + connectOverCDP 这条路,data.browser.mode 是 cdp。
为什么需要这个开关:Chrome 从 136 起不允许在默认用户数据目录上开启远程调试(DevTools remote debugging requires a non-default data directory),管道与端口两种方式都被拒绝。所以默认走可预期性更好的托管 profile;要让服务直接用上现成的登录态,需要这台机器放开这个限制(给 HKLM\SOFTWARE\Policies\Google\Chrome 加 DWORD RemoteDebuggingAllowed=1,需管理员、机器级生效),或者使用 Chrome 136 之前的版本。
把 profile 复制到别处不行:Chrome 的 App-Bound 加密会让 Cookie 解不开。
用户 profile 用不上时(Chrome 正在运行、启动失败等),start 会退回托管 profile,并把原因放进 data.browser.note;browser.chrome.profileFallback=false 可以让它直接报错而不是悄悄换一份。
CDP 这条路的两个代价,用之前先知道:
| 代价 | 说明 |
|---|---|
set_credentials 用不了 | HTTP 基本认证的凭据只能在创建上下文时设置。失败信息里会提示改用 browser=chrome / chromium |
| 页面触发的下载 | 落到浏览器自己的下载目录,不是 ~/Downloads/。pdf 命令不受影响(路径由服务自己算) |
CDP 这条路的页面视口一直跟着窗口走,不套视口模拟,所以 browser.viewport 对它没有影响(见 10)。
五、换成 Firefox
browser.engine=firefox 或 browser=firefox 时改走 Playwright 自带的 Firefox,配同一份托管 profile。
| 差异 | 说明 |
|---|---|
pdf 命令 | 只有 Chromium 支持,Firefox 下返回明确的中文失败原因 |
| 用户自己的 Chrome profile | browser.chrome.useUserProfile 对 Firefox 无效(那条路要 CDP,Firefox 没有) |
| 启动参数 | 没有 --no-sandbox / chromiumSandbox / --profile-directory,Firefox 下不传 |
| 本机装的 Firefox | 用不上:Playwright 的 Firefox 是打过补丁的构建(juggler 协议),不要配 browser.firefox.path 指到本机那份 |
| UA | 不覆写成 Chrome,UA 就是 Firefox 自己的 |
为什么要这个开关:有的站点在 Chromium 下用不了。中国商标网统一身份认证(sso.cnipa.gov.cn)的 SPA 会做开发者工具检测,Chromium 会走到空白页或 HTTP 400,同一流程在 Firefox 下能正常渲染出登录表单。
start 的返回里会多一个 data.browser.engine,用它确认这次到底跑的是哪个引擎。换引擎等于换一套 profile 格式,所以要重新登录一次。
六、关于 Chromium 沙箱
默认策略:Windows / macOS 这类普通桌面环境开启,Linux 关闭(服务多数跑在容器里、以 root 运行,而 Chrome 以 root 启动会直接报错退出)。
| 平台 / 浏览器 | 默认 | 原因 |
|---|---|---|
Windows / macOS 上的 chrome、chromium、auto | 开启 | 普通桌面环境沙箱可用;关掉换不来任何东西,只换来更差的隔离 |
Windows / macOS 上的 edge | 开启 | Edge 走 CDP(端口)那条路,沙箱不影响启动 |
| Linux 上的任何浏览器 | 关闭 | 以 root 运行时必须传 --no-sandbox |
browser.chromium.sandbox 三档取值:true 始终开启 / false 始终关闭 / 不配按平台默认。两条启动路径(Playwright 的持久化上下文与 CDP 那条自己拉进程的路)看同一个开关,不会一半开一半关。
两点容易误解的地方:
--no-sandbox是 Playwright 自己加的,不是本服务传的:Playwright 的chromiumSandbox默认就是关的,只要不显式开启沙箱,这条标志就一直在。- Edge 的问题是「沙箱 + 管道」这个组合:实测开沙箱时,Playwright 的
--remote-debugging-pipe会让 Edge 启动即退出(Target page, context or browser has been closed),换成--remote-debugging-port就一切正常。所以解决办法是换启动方式,而不是关沙箱。
不要为了「藏掉自动化特征」去加 --disable-blink-features=AutomationControlled:那个参数会触发浏览器顶部的「不受支持的命令行标志」提示。需要补充启动参数用 browser.chrome.extraArgs / browser.edge.extraArgs / browser.firefox.extraArgs,改完要重启服务并重新创建浏览器实例,旧窗口不会自动应用新参数。
