前两天折腾了一个挺有意思的事情。
不是让 Playwright 自己启动一个全新的浏览器,而是直接连接到我本地已经打开、已经登录的 Edge 浏览器,然后继续操作里面的网页。
这个需求其实非常真实。
很多时候我们并不是在做自动化测试,而是在处理一些日常工作:
- 使用已经登录的论坛账号发帖、回复
- 打开真实页面后让脚本继续操作
- 避免重新登录和验证码
- 在当前浏览器环境中完成自动化任务
这次我最终跑通了整个流程:
Playwright → 连接本地 Edge → 复用登录态 → 打开 NodeLoc → 进入帖子 → 自动发表评论。
过程中踩了几个坑,顺手记录一下。
为什么不用 Playwright 自己启动浏览器?
平时大家写 Playwright,大多都是这样开始:
const { chromium } = require('playwright');
const browser = await chromium.launch();
这样当然没问题。
但有一个明显缺点:
这是一个全新的浏览器环境。
意味着:
- 没有登录状态
- 没有 Cookie
- 没有 LocalStorage
- 没有当前正在使用的网页
如果只是自动化测试,这很合适。
但如果你想操作一个真实网站、真实账号、真实页面,就会比较麻烦。
例如:
- 论坛发帖
- 后台管理系统
- 工单系统
- 社区运营页面
往往都需要重新登录。
而我的浏览器里其实早就已经登录好了。
既然如此,为什么不直接让 Playwright 接管当前浏览器呢?
于是思路就变成了:
浏览器开启远程调试
↓
获取 CDP 地址
↓
Playwright 连接已有浏览器
↓
复用登录态继续操作
第一个坑:PowerShell 启动命令报错
一开始我是这样启动 Chrome 的:
"C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222
结果直接报错。
后来才反应过来:
这是 PowerShell,不是 CMD。
PowerShell 中执行带路径的程序,需要使用调用运算符:
&
正确写法:
& "C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222
如果是 Edge:
& "C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe" --remote-debugging-port=9222
虽然是个小坑,但第一次碰到确实容易卡住。
第二个坑:9222 端口打不开
启动后访问:
http://127.0.0.1:9222/json/version
结果提示:
127.0.0.1 拒绝了连接请求
最开始我还怀疑:
- 防火墙
- 浏览器版本
- 系统代理
后来发现问题其实更简单:
浏览器原本就在后台运行。
新的启动参数根本没有生效。
也就是说:
你以为启动了一个带远程调试端口的浏览器。
实际上系统只是复用了原来的浏览器进程。
解决办法
方案一:关闭所有浏览器进程
彻底退出 Chrome 或 Edge 后重新启动。
方案二:单独创建调试配置
我后来更推荐这种方式:
& "C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe" `
--remote-debugging-port=9222 `
--user-data-dir="$env:TEMP\edge-cdp"
这样会启动一个独立的浏览器配置目录。
成功率更高。
获取 CDP 地址
远程调试启动成功后,访问:
http://127.0.0.1:9222/json/version
会返回类似:
{
"webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/browser/xxxxxxxx"
}
其中最重要的就是:
webSocketDebuggerUrl
Playwright 连接本地浏览器
代码非常简单:
const { chromium } = require('playwright');
async function main() {
const browser = await chromium.connectOverCDP(
'ws://127.0.0.1:9222/devtools/browser/xxxx'
);
const context = browser.contexts()[0];
const page = await context.newPage();
await page.goto('https://www.nodeloc.com/', {
waitUntil: 'domcontentloaded',
timeout: 60000,
});
console.log(await page.title());
await browser.close();
}
main();
实际上也可以直接写:
const browser = await chromium.connectOverCDP(
'http://127.0.0.1:9222'
);
Playwright 会自动获取 WebSocket 地址。
真正让我觉得成功的瞬间
代码跑完并不稀奇。
真正让我觉得这事成了,是看到网页打开之后:
登录状态还在。
论坛账号还是登录状态。
Cookie 还在。
LocalStorage 也还在。
这时候的感觉完全不一样。
因为:
它已经不是在模拟一个新的浏览器访问网站。
而是在接管我正在使用的浏览器会话。
复用登录态到底有多省事?
如果使用传统 Playwright:
启动浏览器
↓
打开登录页
↓
输入账号密码
↓
处理验证码
↓
保持会话
↓
执行任务
而接管本地浏览器以后:
连接浏览器
↓
直接开始操作
中间所有登录相关步骤全部省掉。
我这次实际完成了什么?
整个流程如下:
连接本地 Edge
↓
复用论坛登录状态
↓
打开目标帖子
↓
定位回复框
↓
输入评论内容
↓
点击发送
↓
回复成功
提交完成后页面 URL 发生变化。
说明回复确实已经提交到真实网站。
不是模拟成功。
是真成功。
这种方案适合哪些场景?
1. 已登录网站操作
例如:
- 论坛
- 管理后台
- CRM
- 工单系统
最怕重新登录。
这种方式特别适合。
2. 半自动化流程
例如:
人工登录
↓
人工过验证码
↓
脚本接管
↓
自动执行后续任务
这是现实中最常见的场景。
3. 运营类工作
例如:
- 发帖
- 回复
- 页面整理
- 内容收集
- 批量操作
都很方便。
4. 真实环境调试
有些问题:
新浏览器复现不了
真实浏览器能复现
这时候 CDP 接管会非常好用。
几个必须注意的问题
1. 不要暴露调试端口
远程调试权限非常高。
能连接的人基本就能控制你的浏览器。
因此建议:
只监听本机
不要映射公网
不要随便共享地址
2. 真实环境意味着真实后果
这不是测试环境。
你点点赞按钮:
真的会点赞。
你发评论:
真的会发出去。
因此最好先检查状态:
await expect(
page.getByRole('button', { name: '发送' })
).toBeVisible();
确认无误再执行点击。
3. 不要把密码写进脚本
既然已经能复用登录态:
就没必要再保存账号密码。
安全性反而更高。
4. 优先使用语义化定位
相比这种:
#root > div > div:nth-child(3) > button
我更推荐:
getByRole()
getByText()
getByLabel()
代码更稳定。
页面改版也更不容易失效。
一点感受
以前我一直把 Playwright 当成测试工具。
但这次折腾完之后,我更愿意把它理解成:
一个能够接入真实浏览器会话的自动化控制器。
尤其是在需要:
- 复用登录态
- 操作真实页面
- 连接人工流程和自动化流程
的时候,体验非常顺畅。
很多自动化问题,本质上并不是操作网页难。
而是登录、会话、验证码太麻烦。
而 CDP 接管浏览器,恰好绕开了这些问题。
总结
如果你需要操作一个已经登录的网站,又不想重新实现整套登录流程,可以试试下面这套方案:
浏览器开启远程调试端口
↓
获取 CDP 地址
↓
Playwright connectOverCDP()
↓
连接已有浏览器会话
↓
新开标签页执行自动化任务
这套流程一旦跑通,你会发现很多原本麻烦的事情突然变简单了。
至少对我来说,它比每次从一个全新的浏览器开始要顺手得多。
评论
使用 GitHub 账号登录后即可评论