BrowserContext 不能跨进程共享,必须每个子进程独立启动浏览器并创建上下文;错误传递会导致序列化失败或连接中断,正确做法是在子进程中用 sync_playwright() 初始化并复用 browser 实例。

BrowserContext 不能跨进程共享
Playwright 的 BrowserContext 实例本质上绑定到创建它的浏览器进程和 Python 进程的事件循环,**无法被 fork 或传递到其他子进程**。尝试用 multiprocessing.Queue、Manager 或 pickle 传递 context 对象,会直接报 TypeError: cannot pickle '_thread.RLock' object 或更隐蔽的连接中断错误。
常见误操作包括:在主进程创建 context → 传给 Process(target=..., args=(context,)) → 子进程调用 context.new_page() → 立即卡死或抛出 TimeoutError(底层 WebSocket 连接已失效)。
正确做法:每个子进程独立启动 browser + context
多进程下必须让每个子进程自己调用 playwright.chromium.launch()(或 firefox / webkit),再调用 browser.new_context()。这不是浪费——Playwright 的 browser 启动极快(尤其启用 headless=True 和 chromium_sandbox=False 时),且 context 创建是轻量级的。
关键实操建议:
立即学习“Python免费学习笔记(深入)”;
- 务必在子进程函数内部初始化 Playwright 和 browser,不要复用主进程的
Playwright实例 - 显式关闭资源:
context.close()→browser.close()→playwright.stop(),否则子进程退出后可能残留渲染进程 - 避免使用全局
playwright变量;每次用sync_playwright().start()获取新实例(它本身是轻量的上下文管理器) - 若需共享 cookies 或 localStorage,改用
context.storage_state()序列化后通过文件或 Queue 传递,再在新 context 中用storage_state=...参数加载
性能优化:复用 browser 实例(同一进程内)
虽然 context 不能跨进程,但在单个子进程中,你可以安全复用同一个 browser 实例创建多个 context —— 这比反复 launch() 快得多。典型场景是:一个子进程处理一批 URL,每个 URL 开一个独立 context 隔离状态。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
示例结构:
def worker(urls):
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
for url in urls:
context = browser.new_context()
page = context.new_page()
page.goto(url)
# ... 处理逻辑
context.close() # 关闭 context,但 browser 保持打开
browser.close() # 所有 context 完成后再关 browser
注意:不要在循环外提前 browser.close(),否则后续 new_context() 会失败;也不要漏掉 context.close(),否则内存泄漏明显。
替代方案:用 asyncio + ProcessPoolExecutor 控制并发粒度
如果你发现每个子进程只跑 1–2 个页面就退出,启动开销占比过高,可考虑换用线程池 + 异步 Playwright(async_playwright),再用 ProcessPoolExecutor 分配任务块。这样既能利用多核,又避免频繁启停 browser。
要点:
- 每个
ProcessPoolExecutor子进程内用asyncio.run()驱动多个async页面任务 - 仍需遵守「每个进程独占 browser」原则,但一个 browser 可支撑几十个并发 page(通过
context.new_page()) - 比纯 multiprocessing 更适合 I/O 密集型爬取,CPU 利用率更平稳
真正容易被忽略的是 context 的生命周期边界——它和进程强绑定,不是“可序列化的配置”,也不是“能缓存的对象”。想绕过这个限制,只会引入不可靠的 hack。

















