ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Playwright Python 实战:一套代码跑通 Chromium、Firefox 和 WebKit 的完整手册

Playwright Python 实战:一套代码跑通 Chromium、Firefox 和 WebKit 的完整手册 Playwright Python 实战一套代码跑通 Chromium、Firefox 和 WebKit 的完整手册【免费下载链接】playwright-pythonPython version of the Playwright testing and automation library.项目地址: https://gitcode.com/GitHub_Trending/pl/playwright-python你刚把 PR 合进 mainCI 日志里 Chromium 全绿Firefox 却在同一条断言上挂掉报 element is not visible。手动打开页面点一遍什么异常都看不到。这种只在某个引擎复现的问题靠肉眼排查基本无解。Playwright Python 解决的就是这类事一套 Python API 同时驱动 Chromium、Firefox、WebKit 三个浏览器引擎同一份测试脚本原样跑遍三端行为差异直接暴露在日志里。看懂支撑三引擎一致的三处机制中间隔着一个 Node.js driver 进程Python 进程并不直接控制浏览器。调用async_playwright()或sync_playwright()时会先拉起一个随包分发的 Node.js 驱动子进程两边通过 stdin/stdout 管道交换带长度前缀的 JSON 消息这套逻辑在playwright/_impl/_transport.py的 PipeTransport 里可以逐行读到。浏览器协议本身的差异——Chromium 的 CDP、Firefox 和 WebKit 各自的私有通道——全部被 driver 消化掉所以你在三个引擎上调用的page.click()是同一条代码路径。顺带的好处是少了一层 WebDriver 式的 HTTP 往返延迟更低引擎差异也被收敛到一个地方修复不用你在测试里逐个打补丁。每个动作都带 actionability 检查locator.click()、fill()这类动作执行前Playwright 会逐项确认元素在 DOM 里、可见、几何位置稳定、能接收事件、没被禁用。任何一项不满足就内部重试直到你给的 timeout 才抛错。直接效果是大部分手写 sleep 可以删掉偶发失败的数量肉眼可见地变少。同步 API 是异步底层的换皮sync 版并不是另一套实现它内部开了一个 greenlet 去跑 asyncio 事件循环你的阻塞调用触发时把控制权交还给循环去收 driver 的消息。这就是为什么同步写法可以安心放在线程和 pytest 里却不能放进已经运行的事件循环——这个限制踩坑部分还会再碰到一次。这是仓库为每个引擎单独存放的基线截图之一同一张网格测试页在 WebKit 下渲染出来的样子后续做截图比较时拿它当参照。从装环境到三引擎参数化三条命令装好 Playwright Pythonpip install playwright playwright install chromium playwright install firefox playwright install webkit第一条装 Python 包里面已含 Node driver后三条把浏览器二进制拉进本地缓存只装你实际要跑的引擎就能省时间。CI 里不想每台机器折腾系统依赖的话utils/docker/下按 Ubuntu 版本备好了 Dockerfile拿来即用。最小脚本确认三引擎都能 launchfrom playwright.sync_api import sync_playwright with sync_playwright() as p: for engine in (p.chromium, p.firefox, p.webkit): browser engine.launch() page browser.new_page() print(page.evaluate(() navigator.userAgent)) browser.close()with块退出时自动停掉 driver 子进程不需要手动收尾。这段只干一件事确认三个引擎都能启动、都能执行 JS。把 user agent 打出来还能顺带核对每个引擎的指纹。拦截网络请求验证 API 调用page context.new_page() with page.expect_response(glob:**/api/orders) as resp_info: page.click(#checkout) resp resp_info.value assert resp.status 200 assert resp.json()[total] 42expect_response是上下文管理器进入时开始监听匹配的请求你在块里触发点击退出时直接拿到那个请求的 Response。断言接口行为不用轮询、不怕慢请求漏网比事后翻请求列表干净得多。pytest 参数化让一个用例跑三遍ENGINES [chromium, firefox, webkit] pytest.fixture def page(request): with sync_playwright() as p: browser getattr(p, request.param).launch() yield browser.new_page() browser.close() pytest.mark.parametrize(page, ENGINES, indirectTrue) def test_checkout(page): page.goto(BASE /cart)indirectTrue把参数字符串当 fixture 参数传进去一个测试函数就被复制成三个引擎各跑一遍。仓库自己的tests/async/conftest.py也是这个思路只是把 browser 提升到 session 级复用避免每条用例重新 launch。同一张网格页在 Chromium 下的基线图。三个引擎的基线分目录存放逐字节比较时各比各的引擎间的渲染微差不会互相误伤。测试开始抖时先查这三处别把同步 API 塞进 asyncio 循环我一开始也踩过在 async main 里 new 了一个 sync Playwright第一次调用就抛错提示换 Async API——同步入口里有现成的运行中循环检查。正确姿势很简单入口是异步的全项目统一用 async_api入口是同步脚本统一用 sync_api。两套混着用事件循环会先乱套。截图比较别指望跨引擎逐字节相同字体光栅化、抗锯齿在不同引擎间存在细微差别加上页面里总有时间戳这类动态内容截图回归最容易出假阳性。两个动作基线按引擎分目录仓库就是这么存的捕获前把会变的区域 mask 掉shot page.screenshot( full_pageTrue, mask[page.locator(.timestamp)], )这张基线图里多出的品红色格子就是 mask 的落点被遮蔽区域不参与比较时间戳跳到下一秒也不会误报。删掉固定 sleep改等具体信号page.wait_for_timeout(1500)在慢机器上不够、快机器上纯浪费。换成等真实信号接口回来了、元素状态变了await page.wait_for_response(glob:**/api/orders) await page.locator(.cart).wait_for(statevisible)wait_for_load_state(networkidle)能用但偏保守只适合对网络空闲要求不严格的页面。核心收获三引擎共用一套 API靠的是中间那层 Node driver 把协议差异吞掉actionability 自动检查替代手写 sleep是测试不抖的第一道保障截图基线按引擎分目录、动态区 mask视觉比较才稳pytest 里把引擎变成 parametrize 参数CI 矩阵直接开跑更多场景直接读仓库里的完整测试套件tests/async/和tests/sync/。【免费下载链接】playwright-pythonPython version of the Playwright testing and automation library.项目地址: https://gitcode.com/GitHub_Trending/pl/playwright-python创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进