浏览器、profile 与登录态
本文说明当前工程「用哪个浏览器、用哪份 profile、登录态存在哪」,以及换浏览器带来的实际后果。前文 04 讲的是请求与响应的形状,本文讲的是浏览器本身。
一、一个浏览器,多个任务
浏览器是全进程共享的:一个浏览器进程、一份 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 | 落到 Chrome 时走 CDP,退回内置 Chromium 时走持久化上下文 | 见第三节 | 默认(推荐) |
chrome | 只用本机 Google Chrome | Chromium | CDP(自己拉进程 + 远程调试端口) | 固定一份 shared-default;打开 browser.chrome.useUserProfile 时是用户自己的 User Data | 需要 Google 登录之类的站点,或要确认「本机 Chrome 下的表现」 |
chromium | 只用内置的那份(发行包内嵌),完全不碰本机 Chrome | Chromium | 持久化上下文 | 托管 profile(按端口派生) | 怀疑本机 Chrome 的扩展或登录态干扰时做对照 |
edge | 只用本机 Microsoft Edge | Chromium | CDP | Edge 专用托管 profile | 站点在 Chrome 下用不了,或要对照两个浏览器的差异 |
firefox | Playwright 自带的那份 Firefox | Firefox | 持久化上下文 | 托管 profile(按端口派生) | 站点在 Chromium 下用不了,见第五节 |
「启动方式」这一列是实现细节,但对结果有影响:CDP 那条路用的是托管 profile 而不是用户日常那份,所以它不要求你先关掉正在用的 Chrome;代价是创建期的某些设置要另想办法补,见 27。
别名也认(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 | 只在 mode=cdp 时出现:product(浏览器亲口报的版本)、protocolVersion,以及 notes(用协议补了哪些创建期设置,补失败的也在这里)。见 27 |
profileDir | 这次真正用的 profile 目录 |
profileSeenBefore | 这份 profile 在这次启动之前就已经有内容了吗 |
note | 这次启动方式与 profile 的说明:走 CDP 时说明用的是哪条路与哪份 profile;用户 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/ 下,但本机 Chrome 与其它浏览器用的不是同一份。
3.1 本机 Chrome(CDP):固定一份 shared-default
| 配置 | 解析结果 | 效果 |
|---|---|---|
| 不配(默认) | shared-default | 换端口不换登录态 |
browser.profileDir=<路径> | 指定值 | 显式配置永远优先;想跟旧的托管目录共用一份登录态就指过来 |
browser.chrome.cdpProfileDir=<路径> | 指定值 | 优先级最高,专管这条路 |
刻意不按端口派生。 按端口派生是为了让多个服务实例各一份 profile、不抢锁;但这条路用的是托管 profile,本来就不会和用户正在开的 Chrome 抢目录,再按端口分只会带来一个纯粹损失 —— 换端口等于换一套登录态。
从「按端口派生的托管 profile」(
shared-<端口>)切到shared-default时,登录态不会跟着走:那是两个不同的目录。要沿用旧目录,用上面两个配置之一显式指过去。
3.2 内置 Chromium / Firefox:托管 profile,默认按端口派生
| 配置 | 解析结果 | 效果 |
|---|---|---|
| 不配(默认) | shared-<端口>,例如 shared-10049 | 多个实例各一份 profile,不抢锁 |
browser.profileDir.perPort=false | shared | 回到所有实例共用一份的老行为 |
browser.profileDir=<路径> | 指定值 | 优先级最高 |
当前解析到哪个目录用 get_config 的 profileDir.resolved 看 —— 它按「这次会落到哪个浏览器类型」解析(profileDir.forType 给出那个类型),与 start 回执里的 data.browser.profileDir 是同一个值。这一点在浏览器类型为 chrome 时尤其要看:那时它给的是 shared-default 那条路,不是 shared-<端口>。
换端口等于换一套登录态(只对 3.2 这一类),想复用旧会话就显式配
browser.profileDir。
profile 目录是跟着「浏览器产品」走的:Edge 单独一份(browser.edge.profileDir),Firefox 沿用 browser.profileDir,本机 Chrome 是 shared-default。所以换浏览器等于换一套登录态,需要登录的站点要重新登一次。
Edge 单独一份的原因:Edge 打开 Chrome 的 User Data 会把它当外来 profile 处理,而且两家 Cookie 的 App-Bound 加密密钥不同,混用只会得到一份读不出登录态的目录。
四、换一份 profile:用用户自己那份 Chrome
browser.chrome.useUserProfile=true 时,本机 Chrome 改用系统默认的 User Data —— 也就是你日常在用的那份、现成的 Google 登录态。
这个开关只管「用哪份 profile」,不管「走哪条路」:本机 Chrome 无论用哪份 profile 都走 CDP(见 27)。所以它与第三节那份 shared-default 是两回事,切换过去等于换一套登录态。
为什么默认不用它: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(shared-default),并把原因放进 data.browser.note;browser.chrome.profileFallback=false 可以让它直接报错而不是悄悄换一份。
这条路用之前先知道两件事:
| 事项 | 说明 |
|---|---|
set_credentials 用不了 | HTTP 基本认证的凭据只能在创建上下文时设置,而 CDP 这条路的上下文不由服务创建。需要它时改用 browser=chromium 或 browser=firefox,失败信息里会直接给出这两个取值 |
| 页面触发的下载 | 服务已经在启动时用协议命令把下载目录设成约定目录(见 27 第五节),不设的话会落到浏览器自己的下载目录。pdf 命令不受影响(路径由服务自己算) |
本机 Chrome 这条路的页面视口一直跟着窗口走,不套视口模拟,所以 browser.viewport 对它没有影响(见 06)。
五、换成 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,改完要重启服务并重新创建浏览器实例,旧窗口不会自动应用新参数。
调试端口不要用 9222。 它不是浏览器的内置默认值,只是各类工具约定俗成的端口,跟着用会互相抢。CDP 那条路默认让浏览器自己挑空闲端口(一定在 10000 以上),需要固定端点时用 browser.chrome.debugPort 钉一个 10000 以上的值,原因与排障见 27 第八节。
