Playwright Python 入门实战指南:Library 模式的安装、同步/异步 API、REPL 调试与 PyInstaller 打包
Playwright 是一套用单一 API 驱动 Chromium、Firefox 与 WebKit 的 Web 自动化与端到端测试框架。本文基于开源仓库中的 Library 快速上手文档,聚焦"以 Python 库(Library)方式直接使用 Playwright"这一路径——即不依赖 pytest 插件、自行在 Python 脚本中启动浏览器并操控页面。读完本文,你将掌握 Python 端的完整安装流程(pip / Poetry / uv)、同步与异步双 API 的正确用法、REPL 下的交互式实验技巧、PyInstaller 单文件打包方案,以及 time.sleep、Windows 事件循环、多线程等典型坑位的规避方法。
说明:本文引用的文档位于本仓库 docs/src 目录,仓库版本为
1.64.0-next(见根目录 package.json)。仓库本身主要承载 Playwright 的核心实现(packages/playwright-core、packages/playwright)与多语言文档源,Python 客户端通过内置 driver 与这些核心服务通信,因此文中会交叉引用核心源码作为底层依据。
两种 Python 使用方式:Library 与 Pytest 插件
Python 开发者使用 Playwright 通常有两条路线,官方在 Python 入门文档 中做了区分:
- Library(库模式):直接
import playwright,在自己的 Python 脚本中显式管理浏览器、上下文与页面的创建和销毁。本文 关联文档 即以此为讲解对象,适合写脚本、做爬虫/采集、开发内部自动化工具,或希望完全掌控自动化流程的场景。 - Pytest 插件(
pytest-playwright):官方推荐的端到端测试写法,为测试用例内置隔离的page/contextfixture、多浏览器配置与 Web-First 断言,需额外pip install pytest-playwright(见 Python 入门文档的安装章节)。
两种方式共享同一套底层驱动与浏览器二进制,playwright install 安装的浏览器可被两者复用。本文只讨论 Library 模式,若你面向正式的端到端测试工程,建议另行阅读 运行测试文档 与 Pytest 集成文档。
安装 Playwright 与浏览器
用你喜欢的包管理器安装 Python 包
原文档针对 pip、Poetry、uv 三种主流工具分别给出了安装序列,本质都是三件事:升级包管理器自身、安装 playwright 包、执行 playwright install 下载浏览器。
pip(最常用)
pip install --upgrade pip
pip install playwright
playwright install
Poetry
poetry self update
poetry add playwright
playwright install
uv
uv self update
uv add playwright
playwright install
第三步 playwright install 会默认下载 Chromium、Firefox 与 WebKit 三个内核的浏览器二进制(同时包含 ffmpeg 等附随组件)。本仓库 packages/playwright-core/browsers.json 中记录了默认安装清单:chromium、chromium-headless-shell、firefox、webkit 与 ffmpeg 的 installByDefault 均为 true,可由此核实"默认安装哪些浏览器"这一行为;需要定制时参考官方 浏览器安装参数文档。
浏览器二进制下载的进阶控制
playwright install 支持更细粒度的参数与若干环境变量,常见实用场景如下(完整列表见 浏览器管理文档):
- 只安装特定内核:
playwright install chromium/playwright install webkit/playwright install firefox; - 安装系统级依赖(适合 CI/纯净 Linux 环境):
playwright install-deps,可与浏览器安装合并为playwright install --with-deps chromium; - 通过
HTTPS_PROXY走代理下载:HTTPS_PROXY=https://192.0.2.1 playwright install; - 通过
PLAYWRIGHT_DOWNLOAD_HOST从内网制品仓库下载,也支持按浏览器单独指定(PLAYWRIGHT_CHROMIUM_DOWNLOAD_HOST等)并优先于全局变量; - 指定浏览器存放位置:
PLAYWRIGHT_BROWSERS_PATH=$HOME/pw-browsers playwright install(安装与运行脚本时需使用同一变量值)。不设置时,Linux/macOS/Windows 下浏览器默认分别放入~/.cache/ms-playwright、~/Library/Caches/ms-playwright与%USERPROFILE%\AppData\Local\ms-playwright; - 跳过浏览器下载(由外部统一管理二进制):
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1; - 卸载本安装的浏览器:
playwright uninstall,跨安装全部清理加--all。
值得说明的是,浏览器管理文档 还指出:Playwright 在"浏览器安装与脚本执行"等环节依赖其捆绑的 Node.js 运行时,如需改用系统预装的 Node.js,可设置 PLAYWRIGHT_NODEJS_PATH 指向具体 node 可执行文件。这条规则同样适用于 Python 端运行 playwright install 的场景。
第一个 Python 脚本:同步 API 起步
安装完成后,在 Python 脚本中 import playwright 并启动三种浏览器中的任意一种(chromium、firefox、webkit)即可工作。同步 API 的最小示例:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://playwright.dev")
print(page.title())
browser.close()
这段代码的语义结构值得拆解:
with sync_playwright() as p:sync_playwright()是全局入口,with语句负责启动与收尾 Playwright driver 进程;p.chromium.launch():显式启动一个浏览器实例;browser.new_page():创建页面(建议正式的自动化任务改用browser.new_context()建立隔离的 BrowserContext,再new_page,参见 BrowserContext 文档);page.goto(...)与page.title():导航并读取页面标题;browser.close():结束后显式关闭浏览器。
为什么同步 API 也"看起来像异步":driver 子进程机制
Python 同步 API 并非单纯把异步方法包一层,而是基于"Playwright 把 driver 放在子进程中运行"这一架构。从已知问题一节(下文详述)可知:Python 端通过子进程与 driver 通信,driver 内部再与浏览器内核对接。这也解释了为什么在 Windows 上 asyncio 必须使用支持异步子进程的 ProactorEventLoop。仓库侧对应的服务端实现位于 packages/playwright-core/src/server,例如浏览器类型的启动入口 browserType.ts(含 headless 默认值为 true 的解析逻辑),Python/Node 各语言客户端都通过 dispatcher 层与其通信。
同步与异步双 API:asyncio 项目用 async_api
Playwright Python 同时提供同步与异步两套几乎一一对应的 API。若项目基于 asyncio(现代异步 Python 的主流选择),官方建议使用异步 API:
import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
await page.goto("https://playwright.dev")
print(await page.title())
await browser.close()
asyncio.run(main())
两套 API 的核心差异总结:
| 对比项 | sync_api |
async_api |
|---|---|---|
| 导入方式 | from playwright.sync_api import sync_playwright |
from playwright.async_api import async_playwright |
| 使用上下文 | with sync_playwright() as p: |
async with async_playwright() as p: |
| 方法调用 | 直接调用,如 page.goto(url) |
全部需要 await,如 await page.goto(url) |
| 适用场景 | 普通脚本、REPL、单元式快捷实验 | 基于 asyncio 的现代项目、与异步生态混用 |
- 代码生成器产出的示例可直接作为参照:仓库 tests/library/inspector/cli-codegen-python.spec.ts 与 cli-codegen-python-async.spec.ts 分别固化了同步与异步两类生成代码的形态。
- 混用同一套 API 的关键原则是:在同一个事件循环/上下文内保持同步或异步的一致性,不要在同步流程中误用异步 API,反之亦然。
第一个实战脚本:用 WebKit 截图
继续原文档的经典演练——用 WebKit 访问 https://playwright.dev/ 并保存页面截图:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.webkit.launch()
page = browser.new_page()
page.goto("https://playwright.dev/")
page.screenshot(path="example.png")
browser.close()
运行后将得到当前目录下的 example.png。page.screenshot(path=...) 是调试与文档化最常用的能力之一,截图时 Playwright 会自动等待页面加载、执行必要的布局,更多截图细节(整页截图 full_page、区域截图、元素截图等)见 截图指南。
headless 与 slow_mo:让浏览器"看得见、走得慢"
默认情况下 Playwright 以无头(headless)模式运行浏览器,方便 CI。若需要观察浏览器 UI(例如排查布局或验证交互效果),在 launch() 时显式关闭 headless,并用 slow_mo 放慢每一步操作的执行速度:
firefox.launch(headless=False, slow_mo=50)
参数含义与取值:
headless=False:以有头模式启动,弹出真实浏览器窗口;不传时默认True。服务端解析可印证默认值逻辑见 browserType.ts。slow_mo=50:每次操作间隔 50 毫秒(示例值,可按需调整),便于肉眼跟踪脚本执行的每个动作;该参数对所有支持的操作生效,是演示与排错利器。
更深度的调试工具(Playwright Inspector、PWDEBUG、Trace Viewer 等)属于独立专题,详见 调试指南——文档中给出 Python 端通过 PWDEBUG=1 pytest -s 进入调试模式的用法。
交互式实验:同步 REPL 与 asyncio REPL
不想写完整脚本时,可以直接在 Python REPL 中启动 Playwright 做快速验证。
普通 REPL(同步 API):直接运行 python,然后逐步执行:
from playwright.sync_api import sync_playwright
playwright = sync_playwright().start()
# Use playwright.chromium, playwright.firefox or playwright.webkit
# Pass headless=False to launch() to see the browser UI
browser = playwright.chromium.launch()
page = browser.new_page()
page.goto("https://playwright.dev/")
page.screenshot(path="example.png")
browser.close()
playwright.stop()
注意这里不再使用 with,而是显式调用 .start() 与 .stop() 管理 Playwright 的生命周期。
asyncio REPL(异步 API):运行 python -m asyncio 进入支持顶层 await 的交互环境:
from playwright.async_api import async_playwright
playwright = await async_playwright().start()
browser = await playwright.chromium.launch()
page = await browser.new_page()
await page.goto("https://playwright.dev/")
await page.screenshot(path="example.png")
await browser.close()
await playwright.stop()
python -m asyncio 模式省去了每次手写 asyncio.run(main()) 的样板代码,在临时探索选择器、验证定位策略时非常高效。
用 PyInstaller 打成独立可执行文件
Playwright 可以与 PyInstaller 配合,把脚本打包成不带 Python 环境也能运行的独立可执行程序。原文档给出的打包入口脚本如下:
# main.py
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://playwright.dev/")
page.screenshot(path="example.png")
browser.close()
不捆绑浏览器的普通打包
若目标机器已单独安装过 Playwright 浏览器(或能从共享路径读取,见上文 PLAYWRIGHT_BROWSERS_PATH),只需打包应用本身:
pyinstaller -F main.py
-F 表示生成单一可执行文件。
把浏览器一起打进可执行文件
如果要让产物"开箱即用"、不依赖目标机器上的任何 Playwright 浏览器,可以在打包前用 PLAYWRIGHT_BROWSERS_PATH=0 让浏览器二进制以密闭(hermetic)方式落入本地位置,再执行打包。官方文档给出的三个平台写法:
bash
PLAYWRIGHT_BROWSERS_PATH=0 playwright install chromium
pyinstaller -F main.py
Windows 命令提示符(batch)
set PLAYWRIGHT_BROWSERS_PATH=0
playwright install chromium
pyinstaller -F main.py
PowerShell
$env:PLAYWRIGHT_BROWSERS_PATH="0"
playwright install chromium
pyinstaller -F main.py
:::note 官方提示
把浏览器一起捆绑进可执行文件会让产物体积显著变大,因此建议只捆绑你实际用到的浏览器(如示例中只装了 chromium),而非全部三种内核。
:::
已知问题与规避方案
原文档以"已知问题"为标题罗列了四类高频坑,理解它们背后的机制比死记规则更重要。
time.sleep() 会导致状态陈旧:改用 page.wait_for_timeout()
不要手动 sleep——Playwright 在每次动作前都内置了可自动等待的 actionability 检查(见 自动等待文档:元素需可见、稳定、能接收事件、可用等)。如果确实需要等待,使用 page.wait_for_timeout(5000) 而非 time.sleep(5):
page.wait_for_timeout(5000) # 推荐:经 Playwright 内部通道调度
# time.sleep(5) # 不推荐
原因在于 Playwright 内部依赖异步操作完成页面通信与协议消息处理;time.sleep 会阻塞当前线程的事件处理,使这些内部异步任务无法被正确消化,导致读取到陈旧状态。仓库侧的参考实现可见 frameDispatcher.ts 到 frames.ts 的 waitForTimeout 通道:它以 Promise + 定时器在服务端等待,同时保持对页面关闭/Frame 分离等状态的响应,这正是与进程级 time.sleep 的本质区别。
即便要用等待,也应优先依赖自动等待或显式的条件等待(如 page.wait_for_selector、expect 轮询断言),把固定超时等待仅作为调试手段。
Windows 上与 asyncio 的 SelectorEventLoop 不兼容
Playwright 将 driver 作为子进程运行,因此要求 asyncio 使用支持异步子进程的 ProactorEventLoop,而 Windows 默认的 SelectorEventLoop 不支持异步子进程。
- 在 Windows Python 3.8+ 上,
ProactorEventLoop本就是默认事件循环,通常无需处理; - 在 Windows Python 3.7 上,Playwright 会主动把默认事件循环设置为
ProactorEventLoop(即把 3.8+ 的默认行为向前兼容到 3.7)。
若自定义了事件循环策略并遇到子进程相关错误,请检查是否将事件循环策略改成了 Selector 系。
线程安全:API 不是线程安全的
Playwright 的 API 不保证线程安全。多线程环境中,正确保用姿势是每个线程创建独立的 playwright 实例,避免共享浏览器/页面对象跨线程访问(更详细的讨论可参见官方 issue playwright-python #623)。这与 BrowserContext 的隔离设计(每个上下文对应独立的浏览器状态)一脉相承——隔离不是可选项,而是多线程并发的硬性要求。
取消 asyncio 任务会产生未定义行为
取消正在执行 Playwright 调用的 task 不被支持,会引发未定义行为。如果一个操作需要比它的调用者活得更久,应当:
- 把它放进一个独立的 task 中运行;
- 用
asyncio.shield()保护它,避免外层取消波及。
从 Library 继续深入:下一步阅读地图
以 library-python.md 为起点,可将能力半径逐步扩大到以下主题:
- 浏览器管理进阶:按内核安装/卸载、系统依赖、代理下载、浏览器缓存目录与 GC 清理,见 browsers.md;
- 定位器与动作:从"能跑通脚本"迈向"写出稳定可靠的自动化逻辑",优先阅读 定位器指南 与 自动等待机制;
- 上下文与隔离:理解
new_context()、多页面管理及 storage state,见 BrowserContext 文档 与 浏览器上下文概念; - 调试:Inspector、
PWDEBUG、Trace Viewer、慢动作演示,见 debug.md; - 走向正式测试工程:Library 适合脚本化自动化;若你的目标是从零搭建可维护的端到端测试套件,则应切换为 pytest 插件路线,从 Python 入门文档 与 测试运行文档 开始。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00