Playwright Python自动化测试与爬虫实战:从入门到精通
1. 项目概述:为什么是Playwright Python?
如果你正在寻找一个能搞定Web自动化测试、数据抓取、甚至网页监控的Python工具,并且已经厌倦了Selenium的复杂配置和偶尔的“抽风”,或者觉得Puppeteer只能绑在Node.js上不够灵活,那Playwright Python绝对值得你花时间研究。我最初接触它是因为一个跨浏览器兼容性测试的项目,当时被Selenium在不同浏览器驱动版本上的“玄学”问题折腾得够呛,直到用了Playwright,才真正体会到什么叫“开箱即用”的顺畅。
简单来说,Playwright是一个由微软开源的现代化Web自动化测试和浏览器自动化库。它的Python版本(playwright-python)让你能用Python代码直接控制Chromium、Firefox和WebKit(Safari的内核)浏览器,进行点击、输入、截图、拦截网络请求等几乎所有你能在浏览器里手动完成的操作。它的核心优势在于其架构设计:它为每个测试或自动化脚本启动一个独立的浏览器上下文(Browser Context),这相当于一个全新的浏览器会话,彼此完全隔离,避免了Cookie、本地存储的污染,也让并行执行变得异常简单和稳定。相比于Selenium基于WebDriver协议的“远程控制”模式,Playwright直接通过DevTools Protocol与浏览器内核通信,速度更快,功能也更底层、更强大。
这套教程的目标很明确:让你从一个对Playwright只有耳闻的小白,快速成长为能用它解决实际工作中复杂自动化需求的熟手。无论是测试工程师想要编写稳定可靠的E2E测试用例,还是数据分析师需要从动态加载的网页中抓取数据,或者是运维开发同学想做一个定时巡检网站健康状态的脚本,你都能在这里找到可以直接“抄作业”的解决方案。我们不会面面俱到地罗列所有API,而是聚焦于最核心的20%的功能,解决80%的实际问题,并分享那些官方文档里不会写的“踩坑”经验和性能调优技巧。
2. 环境搭建与核心概念速通
工欲善其事,必先利其器。Playwright Python的安装过程已经非常简化,但其中一些步骤背后的选择,直接影响着你后续开发的体验。
2.1 一站式安装与初始化
首先,你需要一个Python环境(3.7及以上)。我强烈建议使用虚拟环境(如venv或conda)来管理项目依赖,避免包冲突。
# 创建并激活虚拟环境(以venv为例) python -m venv playwright-env # Windows playwright-env\Scripts\activate # macOS/Linux source playwright-env/bin/activate # 安装playwright库 pip install playwright # 安装Playwright所需的浏览器内核(Chromium, Firefox, WebKit) playwright install这里有几个关键点需要注意:
pip install playwright:安装的是Playwright的Python客户端库,它提供了我们编写脚本用的API。playwright install:这个命令至关重要。它会下载Playwright自己维护的、与其API版本严格匹配的浏览器二进制文件。这些浏览器是专门为自动化优化过的,通常比你自己从官网下载的浏览器更稳定,并且默认支持无头模式。你也可以指定安装特定浏览器,如playwright install chromium或playwright install firefox。- 浏览器管理:Playwright管理的浏览器默认安装在用户目录下的缓存文件夹中(例如
~/Library/Caches/ms-playwright/on macOS)。这意味着它不会干扰你系统上已安装的Chrome或Firefox。
安装完成后,用一个最简单的脚本验证一切正常:
import asyncio from playwright.async_api import async_playwright async def main(): async with async_playwright() as p: # 启动Chromium浏览器,headless=False表示显示界面 browser = await p.chromium.launch(headless=False) # 创建一个新的浏览器上下文(隔离环境) context = await browser.new_context() # 在新上下文中打开一个页面 page = await context.new_page() # 导航到百度 await page.goto("https://www.baidu.com") # 等待3秒以便观察 await page.wait_for_timeout(3000) # 关闭浏览器 await browser.close() asyncio.run(main())运行这个脚本,你应该能看到一个Chromium浏览器窗口自动打开并访问了百度。恭喜,你的Playwright环境已经就绪。
2.2 理解核心三要素:Browser, Context, Page
这是Playwright架构中最核心、也最需要理解清楚的三个概念,它们的关系决定了脚本的稳定性和可维护性。
Browser: 可以理解为安装的浏览器程序本身(如Chromium)。通过
launch()方法启动一个浏览器实例。一个Browser实例可以创建多个独立的Context。- 实操心得: 对于大多数单任务脚本,启动一个Browser就够了。如果是并行执行大量测试用例,可以考虑启动多个Browser实例(消耗更多资源),但更常见的做法是在一个Browser下创建多个Context,因为Context是轻量级且隔离的。
Context:这是Playwright的精华所在。你可以把它想象成一个全新的、独立的浏览器会话(Profile)。每个Context拥有独立的Cookie、本地存储(LocalStorage、SessionStorage)、缓存和证书。它相当于隐身模式打开的一个新窗口,但功能更强大、可控。
- 为什么重要?在自动化中,状态隔离是关键。比如,你一个脚本要测试登录和未登录两种状态。如果没有Context,你就需要反复清理Cookie,非常麻烦且容易出错。有了Context,你只需要创建两个Context,一个用来登录,一个保持未登录,它们互不干扰。关闭Context会自动清理其所有资源。
- 常用配置: 创建Context时可以设置视口大小、User-Agent、地理位置、权限(如是否允许通知)等,模拟各种浏览器环境。
context = await browser.new_context( viewport={'width': 1920, 'height': 1080}, user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...', locale='zh-CN' # 设置语言环境为中文 )Page: 代表Context中的一个标签页。我们绝大部分的自动化操作(如点击、输入)都是在Page对象上完成的。一个Context可以拥有多个Page(即多个标签页)。
- 关系:
Browser-> 创建多个Context-> 每个Context包含多个Page。
- 关系:
注意: 官方推荐的最佳实践是,为每个独立的测试用例或自动化任务创建一个新的Browser Context,而不是复用同一个Context或Page。这能最大程度保证测试的独立性和可重复性。
2.3 同步 vs. 异步API:如何选择?
你可能注意到了,上面的示例代码使用了async/await语法。Playwright Python同时提供了同步和异步两套API。
- 异步API (
playwright.async_api): 基于asyncio。当你的操作涉及大量等待(如网络请求、元素出现)时,异步API允许你在等待一个页面加载时去处理另一个页面的操作,能极大提升脚本效率,特别是在执行并行任务时。这是Playwright的主推模式,也是性能更优的选择。 - 同步API (
playwright.sync_api): 代码写起来更直观,像传统的线性脚本。适合简单的、线性的自动化任务,或者你对异步编程不熟悉的情况。
# 同步API示例 from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch(headless=True) context = browser.new_context() page = context.new_page() page.goto("https://example.com") print(page.title()) browser.close()我的建议是:除非你的项目非常简单或已有大量同步代码基础,否则直接从异步API开始学习。现代Python异步生态已经很成熟,掌握它对于编写高效的网络相关程序有长远好处。本教程后续也将主要使用异步API进行讲解。
3. 元素定位与交互:自动化操作的基石
自动化就是模拟人的操作,而操作的前提是找到正确的目标。Playwright提供了丰富、强大且稳定的元素定位器(Locators),这是它比旧式工具好用的关键之一。
3.1 定位器最佳实践与智能等待
Playwright的定位器是延迟执行的,并且内置了自动等待机制,这解决了传统自动化中令人头疼的“元素未加载完成就进行操作”的问题。
核心定位方式:
get_by_*系列(推荐首选): 这是最语义化、最不易失效的定位方式。Playwright会优先根据可访问性属性和文本内容来定位。page.get_by_role(): 通过ARIA角色定位(如button,textbox)。page.get_by_text(): 通过文本内容定位。page.get_by_label(): 通过关联的label文本定位输入框。page.get_by_placeholder(): 通过占位符文本定位。page.get_by_alt_text(): 通过图片的alt属性定位。page.get_by_title(): 通过title属性定位。page.get_by_test_id(): 通过开发者专门为测试添加的>async def login(page): # 更推荐使用 get_by_* 系列 await page.get_by_placeholder("请输入用户名").fill("my_username") # 如果输入框有配套的<label>,用get_by_label更好 # await page.get_by_label("密码").fill("my_password") await page.locator("input[type='password']").fill("my_password") # 用CSS选择器作为补充 # 点击登录按钮,优先用role或text await page.get_by_role("button", name="登录").click() # 或者 await page.get_by_text("登录").click()智能等待:你不需要在每次操作前都写
time.sleep。Playwright的几乎所有操作(如click,fill,goto)都内置了等待。它会等待元素满足可操作状态(如可见、启用、稳定)。page.wait_for_selector(): 等待特定选择器的元素出现。page.wait_for_function(): 等待页面JavaScript执行到某个状态。page.wait_for_load_state(): 等待页面加载到特定状态('load','domcontentloaded','networkidle')。'networkidle'在单页应用(SPA)中很有用,表示网络空闲。
踩坑记录: 对于动态加载的单页应用(如Vue, React),
page.goto()的默认等待'load'事件可能不够,因为此时前端框架可能还在异步加载数据。一个更可靠的做法是结合wait_for_load_state('networkidle')和等待某个特定元素出现。await page.goto('https://spa.example.com') await page.wait_for_load_state('networkidle') # 等待主要网络活动停止 await page.wait_for_selector('.data-loaded-indicator') # 等待数据加载完成的UI指示器3.2 高级交互:键盘、鼠标、文件与拖放
除了点击和输入,Playwright还能模拟复杂的用户交互。
键盘操作:
await page.get_by_role("textbox").press("Control+A") # 全选 await page.keyboard.type("Hello World!") # 模拟键盘输入 await page.keyboard.press("Enter")鼠标操作:
await page.mouse.move(x, y) # 移动鼠标到坐标 await page.mouse.down() # 按下鼠标左键 await page.mouse.move(x + 100, y) # 拖动 await page.mouse.up() # 松开鼠标左键 # 更简单的方式:使用locator的drag_to方法 source = page.locator("#draggable") target = page.locator("#droppable") await source.drag_to(target)文件上传:这是Playwright做得特别好的地方,无需模拟点击文件选择框这种不稳定的操作。
# 直接设置文件输入框的值 file_input = page.locator("input[type='file']") await file_input.set_input_files([ '/path/to/file1.txt', '/path/to/file2.jpg' ]) # 如果要清空已选文件 await file_input.set_input_files([])处理弹窗与对话框:Playwright可以监听并响应
alert,confirm,prompt以及页面上弹出的自定义模态框。# 监听对话框并接受 page.on("dialog", lambda dialog: dialog.accept()) await page.locator("button#trigger-alert").click() # 点击触发alert的按钮 # 或者更精确的控制 def handle_dialog(dialog): print(f"对话框消息: {dialog.message}") if dialog.type == "confirm": dialog.accept() # 点击确定 else: dialog.dismiss() # 点击取消 page.once("dialog", handle_dialog) # 使用once监听一次4. 网络拦截与Mock:掌控请求与响应
这是Playwright相对于纯UI自动化工具的降维打击能力。你可以监听、修改甚至伪造浏览器发出的任何网络请求和收到的响应,这对于测试、爬虫和性能分析至关重要。
4.1 监听与修改网络流量
async def handle_request(request): # 修改所有请求的请求头 headers = request.headers headers['x-custom-header'] = 'my-value' # 可以继续请求,也可以中止 request.abort() async def handle_response(response): if "/api/data" in response.url: # 拦截特定API的响应 json_data = await response.json() print(f"拦截到数据: {json_data}") # 注意:这里无法直接修改原始响应内容,但可以记录或触发其他逻辑 # 在page或context上添加监听器 page.on("request", handle_request) page.on("response", handle_response) await page.goto("https://example.com") # 记得在不需要时移除监听器,避免内存泄漏 page.remove_listener("request", handle_request)4.2 模拟API响应(Mock)
在测试中,我们经常需要模拟后端API返回特定数据(如错误状态、空数据)来测试前端表现。Playwright的
route功能可以轻松实现。async def mock_api_response(route): # 拦截对 /api/user 的请求 if "/api/user" in route.request.url: # 伪造一个JSON响应 await route.fulfill( status=200, content_type="application/json", body=json.dumps({"name": "Mock User", "id": 123}) ) else: # 其他请求继续正常进行 await route.continue_() # 开始路由拦截 await page.route("**/api/**", mock_api_response) # ** 是通配符 await page.goto("https://your-app.com") # 此时页面中调用 /api/user 的请求将收到我们伪造的数据实操心得: Mock数据时,务必确保返回的数据结构(JSON字段、类型)与真实API完全一致,否则可能导致前端JavaScript报错,测试无法真实反映UI逻辑。最好从浏览器开发者工具的Network面板里拷贝一份真实的响应作为模板。
4.3 性能分析与资源管理
通过监听请求和响应,你可以轻松地收集页面性能数据。
import time start_time = time.time() all_responses = [] async def log_response(response): all_responses.append({ 'url': response.url, 'status': response.status, 'timing': response.request.timing }) page.on("response", log_response) await page.goto("https://example.com") page.remove_listener("response", log_response) total_time = time.time() - start_time print(f"页面加载总耗时: {total_time:.2f}秒") # 分析all_responses,找出慢请求 slow_requests = [r for r in all_responses if r['timing']['responseEnd'] - r['timing']['requestStart'] > 1]5. 高级特性与实战技巧
掌握了基础操作和网络控制后,我们来探索一些能大幅提升脚本能力和稳定性的高级特性。
5.1 处理iframe、多标签页与弹窗
iframe: 需要先定位到iframe元素,然后获取其内部的
frame对象才能操作。# 通过iframe的name属性或选择器定位 iframe_element = page.frame_locator("iframe[name='content']") # 或者通过URL匹配 frame = page.frame(url=r".*login.*") # 然后在frame对象上操作,就像操作page一样 await frame.locator("input#username").fill("user")多标签页: Playwright通过
context.pages来管理一个上下文中的所有页面。# 点击一个会打开新标签页的链接(通常需要监听popup事件) async with page.expect_popup() as popup_info: await page.get_by_text("Open New Tab").click() new_page = await popup_info.value await new_page.bring_to_front() # 切换到新标签页 # 操作new_page... # 遍历所有标签页 for single_page in context.pages: print(single_page.title())弹窗(非对话框): 对于由
window.open()或target='_blank'打开的新窗口,使用expect_popup是最佳实践。5.2 执行JavaScript与获取页面状态
有时需要通过注入JS来获取复杂数据或执行特殊操作。
# 获取页面标题 title = await page.title() # 获取页面URL url = page.url # 执行JS并返回值 element_count = await page.evaluate("() => document.querySelectorAll('div').length") # 在页面环境中执行JS,可以传入参数 dimensions = await page.evaluate("""([width, height]) => { return { innerWidth: width, innerHeight: height }; }""", [800, 600]) # 将JS函数应用到某个元素上 bounding_box = await page.locator("button").evaluate("(element) => element.getBoundingClientRect()")注意:
page.evaluate()中的代码是在浏览器页面环境中执行的,与你的Python脚本环境隔离。传递参数和返回值会被自动序列化/反序列化。对于复杂的DOM操作,优先使用Playwright内置的定位器和操作方法,它们更稳定且提供了自动等待。evaluate通常用于获取Playwright API无法直接获取的信息(如复杂的CSS计算值)或执行特定JS库的功能。5.3 截图、录屏与PDF生成
截图:
await page.screenshot(path="screenshot.png", full_page=True) # 全屏截图 await page.locator(".chart").screenshot(path="chart.png") # 对特定元素截图录屏: 需要在启动Context时开启录屏选项。
context = await browser.new_context(record_video_dir="./videos/") page = await context.new_page() # ... 你的操作 ... await context.close() # 关闭context后视频文件才会被保存 # 视频路径可以通过 page.video.path() 获取生成PDF:
await page.pdf(path="document.pdf", format="A4")生成PDF功能对页面的CSS打印样式支持较好,适合生成报告。
5.4 设备模拟与移动端测试
Playwright内置了多种主流移动设备(如iPhone, Pixel)的配置,可以轻松模拟移动端浏览器环境。
from playwright.async_api import async_playwright, Devices async def main(): async with async_playwright() as p: # 使用预定义的设备描述符 iphone = Devices["iPhone 13 Pro"] browser = await p.chromium.launch() # 在new_context时传入设备参数 context = await browser.new_context(**iphone) page = await context.new_page() await page.goto("https://mobile.example.com") # 此时页面视图、User-Agent、触摸事件等都已模拟为iPhone 13 Pro你也可以自定义设备参数,如屏幕尺寸、像素比、是否支持触摸等。
6. 测试集成与最佳实践
Playwright虽然是一个通用的浏览器自动化库,但其在测试领域的应用最为广泛。它提供了专门的测试运行器(Playwright Test),但用纯Python脚本组织测试也同样高效。
6.1 组织可维护的测试脚本
即使是使用纯Python,遵循一些模式也能让测试代码更清晰。
# test_login.py import pytest import asyncio from playwright.async_api import async_playwright, Page @pytest.fixture(scope="session") def event_loop(): """为异步测试创建事件循环""" loop = asyncio.get_event_loop_policy().new_event_loop() yield loop loop.close() @pytest.fixture(scope="function") async def page(): """为每个测试用例提供一个干净的页面""" async with async_playwright() as p: browser = await p.chromium.launch(headless=True) # 无头模式运行 context = await browser.new_context() page = await context.new_page() yield page # 测试结束后清理 await context.close() await browser.close() @pytest.mark.asyncio async def test_successful_login(page: Page): """测试成功登录流程""" await page.goto("https://your-app.com/login") await page.get_by_label("用户名").fill("testuser") await page.get_by_label("密码").fill("securepass") await page.get_by_role("button", name="登录").click() # 断言:登录后应跳转到首页,并显示用户名 await page.wait_for_url("**/dashboard") welcome_text = await page.get_by_text("欢迎,testuser").text_content() assert "testuser" in welcome_text @pytest.mark.asyncio async def test_login_with_invalid_password(page: Page): """测试使用错误密码登录""" await page.goto("https://your-app.com/login") await page.get_by_label("用户名").fill("testuser") await page.get_by_label("密码").fill("wrongpass") await page.get_by_role("button", name="登录").click() # 断言:应显示错误提示信息 error_message = page.locator(".alert-error") await expect(error_message).to_be_visible() # 使用Playwright的断言 await expect(error_message).to_contain_text("密码错误")关键点:
- 使用Pytest: 它是Python生态中最主流的测试框架,与异步代码配合良好(需要
pytest-asyncio插件)。 - Fixture管理资源: 如上例中的
pagefixture,它负责创建和销毁浏览器实例及页面,确保每个测试独立运行。 - 使用正式的断言: Playwright提供了
expect断言库,它内置了智能等待,比直接用Python的assert更可靠。例如expect(locator).to_be_visible()会先等待元素可见再断言。
6.2 配置与参数化
将浏览器类型、是否无头、基础URL等配置外置,提高脚本的灵活性。
# conftest.py import pytest import os from playwright.async_api import async_playwright def pytest_addoption(parser): parser.addoption("--browser", action="store", default="chromium", help="浏览器: chromium, firefox, webkit") parser.addoption("--headless", action="store_true", default=True, help="是否无头运行") parser.addoption("--base-url", action="store", default="https://staging.example.com", help="测试环境基础URL") @pytest.fixture(scope="session") def base_url(request): return request.config.getoption("--base-url") @pytest.fixture(scope="function") async def page(request, base_url): browser_name = request.config.getoption("--browser") headless = request.config.getoption("--headless") async with async_playwright() as p: browser = await getattr(p, browser_name).launch(headless=headless) context = await browser.new_context(base_url=base_url) # 设置基础URL page = await context.new_page() yield page await context.close() await browser.close()然后可以通过命令行参数运行测试:
pytest --browser=firefox --headless=False --base-url=https://prod.example.com。6.3 稳定性提升:重试、超时与等待策略
自动化测试最大的敌人是不稳定(Flaky Tests)。Playwright提供了多种机制来对抗它。
自动重试: Pytest可以配置测试失败自动重试。
pytest --reruns 2 --reruns-delay 1 # 失败后重试2次,每次间隔1秒或者在代码中,对某些不稳定操作使用自定义重试逻辑。
import asyncio async def click_with_retry(locator, max_retries=3): for i in range(max_retries): try: await locator.click(timeout=5000) # 单次操作超时5秒 return except Exception as e: if i == max_retries - 1: raise await asyncio.sleep(1) print(f"点击失败,第{i+1}次重试...")合理设置超时: Playwright的全局超时、导航超时、操作超时都可以配置。
# 在创建context或page时设置 context = await browser.new_context( viewport=VIEWPORT, # 设置全局超时 default_timeout=30000, # 30秒 ) page = await context.new_page() # 设置页面级别的导航超时 page.set_default_navigation_timeout(60000) # 60秒 page.set_default_timeout(20000) # 20秒使用更稳定的定位器: 如前所述,优先使用
get_by_role(),get_by_text(),get_by_test_id()。避免使用可能随样式或微小布局变化而改变的XPath或复杂CSS选择器。等待策略: 不要用固定的
sleep。多用wait_for_selector,wait_for_function和wait_for_load_state('networkidle')来等待页面达到预期状态。
7. 常见问题排查与性能优化
即使遵循了最佳实践,在实际项目中还是会遇到各种问题。这里记录了一些高频问题的排查思路和优化技巧。
7.1 元素找不到或操作超时
这是最常见的问题,没有之一。
- 检查元素是否在iframe内: 这是新手最容易忽略的一点。确保你的操作对象在当前页面的主文档中,如果元素在iframe里,必须先切换到对应的frame。
- 检查页面是否加载完成: 对于SPA,确保在操作前页面数据已加载。使用
page.wait_for_load_state('networkidle')并结合等待特定元素出现。 - 检查定位器是否正确: 使用Playwright Inspector来辅助调试。通过设置环境变量
PWDEBUG=1运行脚本,它会打开一个可交互的调试工具,让你可以查看页面、生成定位器、逐步执行代码。PWDEBUG=1 python your_script.py - 检查是否有弹窗/对话框阻塞: 未处理的
alert、confirm会阻塞页面脚本执行。添加对话框监听器处理它们。 - 元素被遮挡: 有时要点击的元素可能被另一个透明或悬浮的元素覆盖。Playwright的
click默认会检查元素是否可操作(包括是否被覆盖)。如果确认要强制点击,可以使用force=True参数,但需谨慎。await locator.click(force=True)
7.2 脚本运行速度慢
- 启用无头模式: 在服务器或CI环境中运行务必使用
headless=True(默认就是True),图形渲染会消耗大量资源。 - 复用Browser实例,但创建新的Context: 启动浏览器的开销很大。对于一组相关的测试或任务,复用同一个Browser实例,但为每个独立场景创建新的Context。
# 不好的做法:每个任务都启动关闭浏览器 # 好的做法: async with async_playwright() as p: browser = await p.chromium.launch(headless=True) for task in tasks: context = await browser.new_context() # 复用Browser,新建Context page = await context.new_page() await run_task(page, task) await context.close() # 关闭Context,清理状态 await browser.close() - 避免不必要的等待: 用智能等待(
wait_for_*)替代固定的page.wait_for_timeout()。 - 并行执行: 利用异步API和
asyncio.gather()并行执行多个独立任务。async def fetch_page(url, context): page = await context.new_page() await page.goto(url) title = await page.title() await page.close() return title async with async_playwright() as p: browser = await p.chromium.launch() context = await browser.new_context() urls = ["https://example.com/1", "https://example.com/2", "https://example.com/3"] tasks = [fetch_page(url, context) for url in urls] results = await asyncio.gather(*tasks) # 并行获取 print(results) - 拦截不必要的资源: 如果测试不关心图片、样式表、字体等,可以路由拦截并中止它们,大幅提升加载速度。
async def block_assets(route): if route.request.resource_type in ["image", "stylesheet", "font"]: await route.abort() else: await route.continue_() await page.route("**/*", block_assets)
7.3 在CI/CD中运行(如GitHub Actions, Jenkins)
在无显示服务器的CI环境中运行,需要一些额外配置。
- 安装系统依赖: Playwright的浏览器需要一些系统库。可以使用Playwright CLI自动安装。
playwright install-deps # 安装所有浏览器的系统依赖 playwright install-deps chromium # 只安装Chromium的依赖 - 使用官方Docker镜像: 最简单可靠的方式。微软提供了包含所有依赖的Docker镜像。
FROM mcr.microsoft.com/playwright/python:v1.40.0-noble COPY . /app WORKDIR /app RUN pip install -r requirements.txt CMD ["pytest"] - 配置缓存: 在CI中缓存Playwright的浏览器二进制文件(
~/.cache/ms-playwright)可以显著加快流水线速度。
7.4 调试技巧
- Playwright Inspector: 如前所述,
PWDEBUG=1是最强大的交互式调试工具。 - 录制代码: 使用
playwright codegen命令可以打开一个浏览器,你手动操作,它会实时生成对应的Python代码。这是学习API和快速生成脚本原型的绝佳方式。playwright codegen https://example.com - 追踪查看器: 生成一个可视化的操作追踪文件,像视频一样回放脚本执行过程,能看清每一步发生了什么。
然后使用命令context = await browser.new_context() # 启动追踪 await context.tracing.start(screenshots=True, snapshots=True) # ... 执行你的操作 ... # 停止追踪并保存 await context.tracing.stop(path="trace.zip")playwright show-trace trace.zip打开查看器。 - 控制台输出: 将浏览器控制台日志输出到你的终端。
page.on("console", lambda msg: print(f"CONSOLE: {msg.type} - {msg.text}")) page.on("pageerror", lambda err: print(f"PAGE ERROR: {err}"))
从我自己的项目经验来看,Playwright Python真正做到了“功能强大”和“开发者友好”的平衡。它抽象了底层浏览器的复杂性,提供了稳定、直观的API。花时间熟练掌握它,不仅能提升自动化测试的效率和可靠性,更能为你打开Web自动化、爬虫、RPA等领域的一扇新大门。开始可能会觉得异步编程有点门槛,但一旦习惯,你会发现用它写出的脚本既简洁又高效。遇到问题时,多利用Inspector和Trace工具,大部分难题都能迎刃而解。
