Playwright with sync_playwright() as p 上下文管理器

前言

刚开始写 Playwright 自动化脚本,你一定见过这种写法:

复制代码
with sync_playwright() as p:
    browser = p.chromium.launch()
    ...

很多教程一笔带过,让新手很懵:这个 with 是干嘛的?p 又是啥?

这篇就用人话,用最简单的比喻,帮你彻底搞明白。


一、先记住一个比喻:去网吧上网

你去网吧,前台给你一张临时上机卡 (就是那个 p)。

在网吧里,你可以用这张卡开机、打开浏览器、玩游戏......

不管你是正常下机,还是突然被老妈拎回家,前台最后都一定会把卡收回,电脑自动关闭。

对应到代码:

复制代码
with sync_playwright() as p:
    # 你拿了卡 p,在网吧里随便玩
    browser = p.chromium.launch()
# 离开 with,卡被回收,浏览器全部关掉

就这么简单。


二、为什么要这样写?

核心好处就是两个字:省心

如果你不用 with,就得自己开机和关机:

复制代码
p = sync_playwright().start()   # 手动开机
# ... 操作浏览器 ...
p.stop()   # 必须手动关机

问题是:万一中间代码报错了,p.stop() 没执行,浏览器进程就会赖在后台不走,资源越占越多。

with 不管你中间怎么崩,最后保证一定会关机。这就是 Python 上下文管理器的保护机制。


三、p 到底是什么?

p 就是你进入 Playwright 世界的入口

有了它,你才能启动浏览器:p.chromium.launch()

有了它,你才能用火狐:p.firefox.launch()

可以把 p 理解成一张"万能遥控器",没有它,你什么都点不了。


四、异步版本一样道理

异步写法就是把 with 换成 async with,其他逻辑一模一样:

复制代码
async with async_playwright() as p:
    browser = await p.chromium.launch()

同样是进去给卡,出来收卡,只不过这个网吧支持"同时招呼好多人"。


五、结合pytest,示例:

用 pytest 写也是完全一样的思路:with sync_playwright() 当成自动帮你"开机 + 关机"的管家

只不过 pytest 里大家一般不会在每个测试函数里都写一遍 with,那样每跑一个用例都会重新启动和关闭整个 Playwright 服务,很慢。

所以实际测试中,更常见的做法是用 fixture 共享浏览器,让 Playwright 在整个测试会话里只启动一次:

复制代码
import pytest
from playwright.sync_api import sync_playwright

@pytest.fixture(scope="session")
def browser():
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        yield browser        # 测试在这期间使用浏览器
        # with 结束自动清理

def test_baidu(browser):
    page = browser.new_page()
    page.goto("https://www.baidu.com")
    assert page.title() != ""
    page.close()

但核心逻辑没变:with 照样在进场时帮你启动,出场时帮你关闭,只是我们把它提到 fixture 里,让所有测试共用一个浏览器,省时间。

简单讲就是:思路一样,只是挪到了 fixture 里复用,避免重复开机。

六、使用 pytest-playwright 插件

使用 pytest-playwright 插件时,你通常不需要再写 with sync_playwright() as p:。插件的 Fixture 体系已经接管了整个生命周期管理,能帮你自动启动和关闭浏览器,代码也更简洁。

✅ 优先推荐:使用内置 Fixture,告别手动 with

这是插件官方推荐的方式,也是我之前提到的建议。它的核心优势在于,无需手动管理 with 语句,测试代码会非常干净、专注。

复制代码
from playwright.sync_api import Page

def test_baidu(page: Page):  # 👈 直接注入 page fixture
    page.goto("http://127.0.0.1:8000/practice")
    page.get_by_role("textbox", name="姓名:").fill("小罗")
    # ... 其他操作
    page.pause()

这是最理想、被官方推荐的方式,因为它的代码最简洁,并且能无缝支持 --headed--tracing 等所有命令行参数。

🔧 如果非要手动管理:可以用 with,但不与内置 Fixture 混用

如果你必须完全手动控制,可以彻底不用插件,纯手工编写测试:

python

复制代码
from playwright.sync_api import sync_playwright

def test_baidu():
    with sync_playwright() as p:       # 👈 with 管理 playwright 对象
        browser = p.chromium.launch()
        page = browser.new_page()
        page.goto("http://127.0.0.1:8000/practice")
        page.get_by_role("textbox", name="姓名:").fill("小罗")
        # ...
        page.pause()
        browser.close()

这种方式能保证资源的正确释放,但无法使用插件的 Fixture ,意味着 --tracing 等所有便捷特性都将失效。

🚦 with 与 Fixture:两种模式的对比与选择

下面这个表格可以帮助你快速理清两种方式的适用场景:

官方内置 Fixture ✅ 推荐 手动管理 with ⚠️ 特殊场景
玩法 直接开玩 ,只需安装 pytest-playwright,测试里用 page 这个现成的 Fixture 就行。 DIY 模式 ,需要自己在代码里写 with sync_playwright() as p
代码量 极简,不关心浏览器怎么造、怎么关。 较啰嗦,with 和关闭步骤都得自己写。
清理 插件自动清理,不用担心资源泄露。 依赖 with 代码块,离开后自动关闭。
支持--tracing命令行 完全支持 不支持
适用场景 绝大多数自动化测试场景。 对启动细节有极致控制需求的少数场景

with sync_playwright() as p 是 Playwright 原始 API 的生命周期管理方式。但在 pytest-playwright 这个优秀的插件生态下,它已经为你做好了自动化的生命周期管理。你应该把精力集中在编写测试逻辑上,而不是重复处理浏览器的启停。

另外,page.pause()pytest-playwright 插件中也能正常工作。你只需要用 --headed 命令开启有头模式,它就会启动 Playwright Inspector,帮助你逐步调试。

所以,结论是:pytest-playwright 完全可以使用上下文管理,只是你不需要再自己写了

七、一句话总结

with sync_playwright() as p: 就是一个自动开机、自动关机的"网吧模式",帮你免去一切手动清理的烦恼。

记住这个比喻,以后写 Playwright 再也不用纠结这一行是干啥的了。

相关推荐
balance_rui9 小时前
FreeRTOS
笔记·stm32
uncle_ll10 小时前
LangChain基础学习笔记
笔记·学习·langchain·llm·rag
三品吉他手会点灯10 小时前
C语言学习笔记 - 14.C编程预备计算机专业知识 - 本讲内容概述
c语言·笔记·学习
陈皮糖..11 小时前
27 届运维实习笔记|第三、四周:从流程熟练到故障排查,企业运维实战深化
运维·笔记·sql·nginx·ci/cd·云计算·jenkins
三水不滴11 小时前
SpringAI + SpringDoc + Knife4j 构建企业级智能问卷系统
经验分享·spring boot·笔记·后端·spring
三品吉他手会点灯11 小时前
C语言学习笔记 - 15.C编程预备计算机专业知识 - CPU 内存条 硬盘 显卡 主板 显示器 之间的关系
c语言·笔记·学习
三品吉他手会点灯11 小时前
C语言学习笔记 - 11.C语言简介 - VSCode(C/C++)环境安装与配置
c语言·笔记·学习
The Chosen One98513 小时前
计算机知识点的理解开悟后的分享(一)
笔记
独孤九剑打醒他14 小时前
#原创声明 #拒绝白嫖 #技术立场 #创作者保护
笔记