Playwright自动化测试与爬虫实战:从环境搭建到项目部署
2026/7/17 5:05:36 网站建设 项目流程

1. 项目概述:为什么是Playwright?

如果你正在寻找一个能横跨现代Web应用所有主流浏览器(Chromium、Firefox、WebKit)的自动化测试或爬虫工具,并且厌倦了Selenium的配置繁琐和Puppeteer的浏览器单一性,那么Playwright绝对值得你投入时间。它由微软团队开发,原生支持Python、Node.js、.NET和Java,其设计哲学从一开始就瞄准了现代Web的复杂性——单页应用、Shadow DOM、网络拦截、文件上传、地理位置模拟,这些在传统工具里需要大量胶水代码才能实现的功能,Playwright都提供了优雅的一站式解决方案。

我最初接触Playwright是为了解决一个棘手的E2E测试需求:一个重度依赖WebSocket和动态加载的React应用。用Selenium写出来的脚本既不稳定又冗长,而Playwright的wait_for_load_state('networkidle')和内置的自动等待机制,让脚本的健壮性直接上了一个台阶。更重要的是,它的Python API设计得非常符合直觉,即使你之前没有浏览器自动化的经验,也能快速上手写出可用的脚本。这篇文章,我将带你从零开始,深入Playwright的每一个核心角落,并通过几个实战项目,让你不仅能“会用”,更能“用好”。

2. 环境搭建与核心概念解析

2.1 一站式环境配置指南

Playwright的安装比它的前辈们要友好得多。它自带浏览器内核,无需单独配置WebDriver。但为了获得最佳开发体验,我建议按以下步骤搭建环境。

首先,确保你有一个干净的Python环境。我强烈推荐使用venv创建虚拟环境,避免包依赖冲突。

# 创建项目目录并进入 mkdir playwright-project && cd playwright-project # 创建虚拟环境 python -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate

接下来,安装Playwright。这里有两个选择:只安装库,或者连同常用的浏览器一起安装。对于新手,我推荐一次性装好。

# 安装playwright库 pip install playwright # 安装Playwright所需的浏览器(Chromium, Firefox, WebKit) playwright install

注意playwright install命令会下载大约几百MB的浏览器二进制文件到缓存目录。如果网络环境不佳,可以尝试使用--with-deps参数或设置镜像源。有时,公司内网可能会拦截这些下载,你需要确认是否有相应的网络策略。

安装完成后,一个简单的验证脚本能帮你检查一切是否就绪。

# test_install.py import asyncio from playwright.async_api import async_playwright async def main(): async with async_playwright() as p: # 尝试启动Chromium浏览器 browser = await p.chromium.launch(headless=False) # headless=False让我们能看到浏览器 page = await browser.new_page() await page.goto('https://example.com') print(f'页面标题: {await page.title()}') await browser.close() asyncio.run(main())

运行这个脚本,如果能看到一个浏览器窗口弹出并访问example.com,然后在控制台打印出标题,那么恭喜你,环境配置成功。

2.2 同步与异步API:如何选择?

Playwright提供了两套API:同步和异步。这是初学者最容易困惑的点之一。

  • 同步API (playwright.sync_api):代码写起来是线性的,更符合传统脚本的阅读习惯。Playwright在背后通过智能等待帮你处理了大多数异步操作,让你感觉像是在写同步代码。适合大多数自动化脚本、爬虫和简单的测试场景。它的代码看起来像这样:

    from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch() page = browser.new_page() page.goto('https://example.com') # 看起来是同步调用,实际内部会等待加载完成 print(page.title()) browser.close()
  • 异步API (playwright.async_api):显式使用async/await语法。当你的操作需要处理大量并发任务时(例如同时监控多个页面、并行执行一组测试),异步API能极大提升效率,充分利用CPU资源。适合高性能爬虫、复杂的并发测试套件。

    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://example.com') print(await page.title()) await browser.close() asyncio.run(main())

我的选择建议:如果你是新手,或者项目不涉及复杂并发,从同步API开始。它的心智负担更小,调试也更直观。当你需要同时操作多个页面或标签页,并且感受到性能瓶颈时,再考虑迁移到异步API。本文后续示例将主要使用同步API,因为其受众更广,但会在关键部分指出异步的写法。

2.3 核心对象模型:Browser, Context, Page

理解Playwright的三个核心对象是写出高效脚本的关键。它们的关系就像浏览器->用户会话->单个标签页。

  1. Browser:代表一个浏览器实例。你可以把它想象成你电脑上安装的Chrome或Firefox应用程序。通过launch()方法启动,可以配置无头模式、代理、窗口大小等。

    browser = p.chromium.launch(headless=False, slow_mo=50) # slow_mo让每个操作延迟50毫秒,方便观察
  2. Context:浏览器上下文。这是一个独立的会话,类似于一个隐身窗口。每个Context拥有独立的cookie、本地存储和缓存,但共享同一个Browser进程。这是Playwright非常强大的一个特性,你可以用它来模拟多个互不干扰的用户同时登录同一个网站。

    # 创建两个独立的上下文,模拟两个用户 context1 = browser.new_context() context2 = browser.new_context(user_agent='自定义UA')
  3. Page:页面。代表一个标签页或一个弹出窗口。我们绝大部分的交互(点击、输入、获取内容)都发生在Page对象上。

    page = context.new_page() page.goto('https://www.baidu.com')

为什么Context如此重要?假设你要测试一个购物车的并发修改。你可以创建两个Context,每个Context里登录不同的用户账号,然后同时操作同一个商品库存。它们完全隔离,不会因为Cookie串了而导致测试失败。在爬虫场景中,你可以用不同的Context来管理不同的会话状态,比如一个Context负责抓取公开信息,另一个Context负责维持已登录状态去抓取会员数据。

3. 元素定位与交互:精准操作的基石

定位不到元素,是自动化脚本失败的首要原因。Playwright提供了丰富且强大的定位器(Locator),其设计理念是“尽可能自动等待元素可用”。

3.1 定位器(Locator)最佳实践

Playwright推荐使用page.locator(selector)来创建定位器。这个定位器是“惰性”的,只有在真正执行操作(如click())时,它才会去查找元素,并且自带自动等待,直到元素出现在DOM中并处于可操作状态(如可见、可点击)。

最常用的定位策略:

  • 按文本定位:这是Playwright的一大亮点,非常符合直觉。

    # 点击文本为“登录”的按钮 page.locator('text=登录').click() # 点击包含“提交”文本的元素 page.locator('text=提交').click() # 更精确的全文匹配 page.locator('text="Sign in"').click()
  • CSS选择器:基础且强大,与你在前端开发中使用的完全一致。

    page.locator('button#submit').click() page.locator('.list-item:has-text("Item 1")').click()
  • XPath:在CSS选择器无能为力时(如基于复杂层级关系或文本内容),XPath是最后的武器。但应谨慎使用,因为它通常更脆弱。

    page.locator('xpath=//button[contains(@class, \'primary\')]').click()
  • 按角色(Role)定位:这是遵循WAI-ARIA标准的最佳实践,对于现代组件库(如Material-UI, Ant Design)构建的页面尤其稳定。

    page.locator('role=button[name="搜索"]').click() page.locator('role=textbox').fill('关键字')

我的实战心得:定位策略的优先级应该是:Role > Text > CSS > XPath。优先使用Role和Text,因为它们最贴近用户视角,即使前端代码的CSS类名或ID改变了,只要按钮文本或语义角色不变,你的脚本就依然有效。编写选择器时,尽量让它具有描述性且尽可能短。避免使用像div > div:nth-child(3) > span > a这样冗长且脆弱的路径。

3.2 高级交互:不仅仅是点击和输入

掌握了定位,我们来看看Playwright如何模拟复杂的用户交互。

处理下拉框(Select): 传统方式需要点击下拉框再点击选项。Playwright提供了更直接的方法。

# 通过value选择 page.locator('select#country').select_option('cn') # 通过标签文本选择 page.locator('select#country').select_option(label='中国') # 如果是自定义的下拉组件(非原生<select>),则需要模拟点击 page.locator('.custom-select-trigger').click() page.locator('.dropdown-item:has-text("北京")').click()

文件上传: Playwright让文件上传变得极其简单,无需模拟复杂的<input type="file">的点击事件。

# 假设有一个 <input type="file"> page.locator('input[type="file"]').set_input_files('/path/to/my/file.pdf') # 上传多个文件 page.locator('input[type="file"]').set_input_files(['file1.pdf', 'file2.jpg']) # 移除已选择的文件 page.locator('input[type="file"]').set_input_files([])

鼠标与键盘操作

# 鼠标悬停 page.locator('.menu-item').hover() # 右键点击 page.locator('.element').click(button='right') # 双击 page.locator('.icon').dblclick() # 模拟键盘输入,如按Tab键切换焦点,按Enter提交 page.locator('input#username').press('Tab') page.locator('input#password').press('Enter') # 组合键,如Ctrl+C page.keyboard.press('Control+C')

拖放操作

# 将source元素拖放到target元素上 source = page.locator('#draggable') target = page.locator('#droppable') source.drag_to(target)

重要提示:Playwright的自动等待机制在大多数情况下能保证元素可交互。但如果你遇到“元素不可点击”的错误,除了检查选择器是否正确,还可以尝试:

  1. 使用locator.wait_for(state='attached'或'visible')显式等待。
  2. 在操作前先滚动元素到视图中:locator.scroll_into_view_if_needed()
  3. 检查是否有模态框、遮罩层覆盖了目标元素。

4. 等待、断言与网络控制

4.1 智能等待:告别sleeptimeout

硬编码time.sleep(10)是自动化脚本的“癌症”——它不可靠且低效。Playwright内置了智能等待,理解这些等待条件至关重要。

  • 导航等待page.goto()page.click()(如果点击会触发导航)默认会等待页面触发load事件。对于单页应用,这通常不够。

    # 等待直到网络几乎空闲(至少500ms没有网络请求) page.goto('https://spa.example.com', wait_until='networkidle') # 等待直到DOMContentLoaded事件 page.goto('https://example.com', wait_until='domcontentloaded')
  • 元素等待:定位器操作(click,fill)会自动等待元素可操作。

    # 下面的click会一直等待,直到#button元素可见、可点击,最多等待30秒(默认超时时间) page.locator('#button').click() # 你可以自定义超时时间 page.locator('#slow-button').click(timeout=10000) # 等待10秒
  • 显式等待:使用page.wait_for_*系列函数。

    # 等待某个选择器对应的元素出现 page.wait_for_selector('.success-message') # 等待某个URL(常用于表单提交后的跳转) page.wait_for_url('**/dashboard') # 等待一个函数在页面上下文中返回真值 page.wait_for_function('window.status === "ready"')

我的避坑经验wait_until='networkidle'在动态加载内容非常多的页面上可能会等待过久,甚至超时。一个更稳健的策略是结合使用wait_for_selector,等待某个标志性元素(如加载完成的占位符消失、主要内容区域出现)的出现。例如:page.wait_for_selector('.product-list', state='visible')

4.2 断言:验证脚本行为的正确性

Playwright Test(一个基于Playwright的测试运行器)提供了丰富的断言。但即使在不使用测试框架的纯脚本中,我们也可以利用Python自带的assert或Playwright的期望(Expect)API来进行验证。

# 使用Python assert title = page.title() assert 'Dashboard' in title, f'期望标题包含Dashboard,实际是{title}' # 使用Playwright的期望API(更强大,错误信息更友好) from playwright.sync_api import expect expect(page).to_have_title('My Page') expect(page.locator('.status')).to_have_text('Success') expect(page.locator('list-item')).to_have_count(10)

4.3 拦截与模拟网络请求

这是Playwright作为爬虫和测试利器的核心功能。你可以监听、修改甚至伪造任何网络请求和响应。

监听请求/响应

# 打印所有请求的URL page.on('request', lambda request: print(f'>> {request.method} {request.url}')) # 打印所有响应的状态码和URL page.on('response', lambda response: print(f'<< {response.status} {response.url}'))

拦截并修改请求

# 阻止对图片资源的请求以加速 page.route('**/*.{png,jpg,jpeg}', lambda route: route.abort()) # 修改请求头,例如添加一个认证令牌 page.route('**/api/**', lambda route: route.continue_(headers={**route.request.headers, 'Authorization': 'Bearer token'})) # 直接返回一个模拟的响应,不发送真实请求 page.route('https://api.example.com/data', lambda route: route.fulfill( status=200, content_type='application/json', body=json.dumps({'mock': 'data'}) ))

实战场景

  1. 性能测试:通过拦截和延迟响应(route.continue_(delay=1000))来模拟慢速网络。
  2. 爬虫:拦截API接口的响应,直接提取JSON数据,比从渲染后的HTML中解析要高效和准确得多。
  3. 测试:模拟后端API失败(返回500错误)或返回特定数据,来测试前端应用的错误处理和数据渲染逻辑。

5. 实战项目一:构建一个健壮的Web爬虫

让我们用一个实战项目来巩固以上知识。目标是爬取一个模拟的电商网站商品列表和详情,该网站采用JavaScript动态加载数据。

5.1 项目分析与设计

目标网站:https://demo-webstore.com任务:

  1. 遍历所有分类页面。
  2. 获取每个分类下的商品列表(名称、价格、链接)。
  3. 进入每个商品详情页,抓取描述、规格等详细信息。
  4. 处理分页。
  5. 将数据保存为JSON文件。

挑战:商品列表是滚动加载(无限滚动),分类和商品详情数据通过API异步获取。

设计思路

  • 使用一个Browser Context。
  • 主要使用同步API,逻辑清晰。
  • 通过监听网络请求,直接捕获API返回的JSON数据,避免解析HTML。
  • 使用page.evaluate()执行JavaScript来触发滚动,模拟用户行为加载更多商品。

5.2 核心代码实现

import json from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeoutError def scrape_webstore(): all_products = [] with sync_playwright() as p: # 启动浏览器,设置视窗大小 browser = p.chromium.launch(headless=True) # 生产环境用无头模式 context = browser.new_context(viewport={'width': 1920, 'height': 1080}) page = context.new_page() # 监听特定的API响应,直接捕获数据 def handle_api_response(response): if '/api/products' in response.url and response.status == 200: try: # 尝试解析JSON数据 products_data = response.json() # 假设API返回一个包含'items'字段的JSON if 'items' in products_data: print(f'捕获到 {len(products_data["items"])} 个商品') all_products.extend(products_data['items']) except: print(f'解析API响应失败: {response.url}') page.on('response', handle_api_response) # 1. 导航到首页 page.goto('https://demo-webstore.com', wait_until='networkidle') # 2. 获取所有分类链接 category_links = page.locator('nav.categories a').all() category_urls = [link.get_attribute('href') for link in category_links] category_urls = [f'https://demo-webstore.com{url}' for url in category_urls if url] for category_url in category_urls[:2]: # 演示只爬前两个分类 print(f'正在爬取分类: {category_url}') page.goto(category_url, wait_until='domcontentloaded') # 3. 处理滚动加载(无限滚动) last_height = page.evaluate('document.body.scrollHeight') scroll_attempts = 0 max_attempts = 5 # 最多滚动5次,防止无限循环 while scroll_attempts < max_attempts: # 滚动到底部 page.evaluate('window.scrollTo(0, document.body.scrollHeight)') # 等待新内容加载 page.wait_for_timeout(2000) # 等待2秒,网络空闲等待可能不适用于滚动加载 # 检查是否滚动到了底部 new_height = page.evaluate('document.body.scrollHeight') if new_height == last_height: # 高度未变,可能已加载完毕或遇到“加载更多”按钮 load_more_button = page.locator('button:has-text("Load More")') if load_more_button.count() > 0 and load_more_button.is_visible(): load_more_button.click() page.wait_for_timeout(3000) else: break # 没有更多内容了 last_height = new_height scroll_attempts += 1 # 4. 此时,通过监听器,商品列表数据应已捕获到all_products中 # 我们可以进一步访问每个商品详情页(如果需要额外信息) # 这里为了演示,我们只打印已捕获的商品数量 print(f'分类 {category_url} 捕获到 {len([p for p in all_products if p.get("category") == category_url])} 个商品') # 5. 去重并保存数据 # 简单的基于ID的去重 unique_products = {prod['id']: prod for prod in all_products}.values() with open('products.json', 'w', encoding='utf-8') as f: json.dump(list(unique_products), f, ensure_ascii=False, indent=2) print(f'爬取完成!共获取 {len(unique_products)} 个唯一商品。') browser.close() if __name__ == '__main__': scrape_webstore()

5.3 爬虫项目优化与问题排查

常见问题与解决方案:

  1. 被网站检测到是自动化脚本

    • 症状:访问被拒绝、出现验证码、页面布局异常。
    • 对策
      • 使用context.new_context()时,传入user_agent参数模拟真实浏览器。
      • 启用浏览器上下文中的viewport,设置一个常见的分辨率。
      • 避免过快的操作,在关键步骤间使用page.wait_for_timeout(1000-2000)加入随机延迟。
      • (高级)使用playwright-stealth等插件尝试规避检测(需谨慎,可能违反网站条款)。
  2. 网络监听捕获不到数据

    • 症状handle_api_response函数从未被触发。
    • 排查
      • 在监听前先导航到页面?监听器需要在导航之前添加。
      • API的URL模式是否匹配?使用print(response.url)调试。
      • 数据是否不是通过XHR/Fetch,而是WebSocket或SSE?Playwright也支持WebSocket监听,但处理更复杂。
  3. page.evaluate执行JavaScript出错

    • 症状:控制台报JavaScript错误。
    • 对策:确保传入evaluate的字符串是有效的JS代码。对于复杂操作,可以定义一个JS函数字符串。传递参数时使用page.evaluate('(arg) => {...}', arg)格式。

性能优化技巧

  • 并发爬取:对于独立的分类页面,可以使用异步API(asyncio.gather)同时打开多个Page进行爬取,大幅提升速度。
  • 资源拦截:在爬虫场景下,可以拦截不必要的资源(如图片、样式表、字体、媒体文件)以节省带宽和加速页面加载。
    def route_handler(route): if route.request.resource_type in ['image', 'stylesheet', 'font', 'media']: route.abort() else: route.continue_() page.route('**/*', route_handler)
  • 合理使用无头模式headless=True(默认)不显示GUI,资源占用更少,速度更快。

6. 实战项目二:实现端到端(E2E)自动化测试

Playwright最初就是为了测试而生的。我们用它来为一个简单的登录流程编写一个E2E测试脚本。

6.1 测试场景设计

测试应用:一个虚构的博客平台登录页 (https://demo-blog.com/login)。 测试用例:

  1. 使用正确用户名和密码登录,验证跳转到仪表盘。
  2. 使用错误密码登录,验证显示错误提示信息。
  3. 测试“记住我”复选框功能。
  4. 测试忘记密码链接是否有效。

我们将使用Playwright的同步API和pytest测试框架来组织代码,这是社区的主流选择。

6.2 测试代码结构与实现

首先,安装pytest和pytest-playwright插件(它提供了有用的Fixture)。

pip install pytest pytest-playwright

创建测试文件test_login.py

import pytest from playwright.sync_api import Page, expect # 测试数据 VALID_USERNAME = "testuser" VALID_PASSWORD = "TestPass123" INVALID_PASSWORD = "WrongPass" @pytest.fixture(scope='function') def login_page(page: Page): """Fixture:导航到登录页面""" page.goto('https://demo-blog.com/login') yield page def test_successful_login(login_page: Page): """测试用例1:成功登录""" # 1. 定位元素并操作 login_page.locator('input[name="username"]').fill(VALID_USERNAME) login_page.locator('input[name="password"]').fill(VALID_PASSWORD) login_page.locator('button[type="submit"]').click() # 2. 断言:验证是否成功跳转到仪表盘 expect(login_page).to_have_url('**/dashboard') # 断言:页面中包含欢迎用户的文本 expect(login_page.locator('.welcome-message')).to_contain_text(VALID_USERNAME) def test_failed_login_with_wrong_password(login_page: Page): """测试用例2:密码错误登录失败""" login_page.locator('input[name="username"]').fill(VALID_USERNAME) login_page.locator('input[name="password"]').fill(INVALID_PASSWORD) login_page.locator('button[type="submit"]').click() # 断言:错误提示信息应该出现 error_message = login_page.locator('.alert-error') expect(error_message).to_be_visible() expect(error_message).to_contain_text('密码错误') # 断言:页面仍然停留在登录页 expect(login_page).to_have_url('**/login') def test_remember_me_functionality(login_page: Page): """测试用例3:记住我功能""" login_page.locator('input[name="username"]').fill(VALID_USERNAME) login_page.locator('input[name="password"]').fill(VALID_PASSWORD) login_page.locator('input[name="remember"]').check() # 勾选复选框 login_page.locator('button[type="submit"]').click() # 登录成功后,关闭浏览器上下文再重新打开,检查是否自动登录 # 注意:这需要应用实际实现了“记住我”功能,且我们能在同一Context中保持状态。 # 更真实的测试可能需要操作Cookie或LocalStorage,这里简化演示。 expect(login_page).to_have_url('**/dashboard') # 在实际项目中,这里可能会清理Cookie然后重新访问首页,检查是否自动跳转到仪表盘。 def test_forgot_password_link(login_page: Page): """测试用例4:忘记密码链接""" with login_page.context.expect_page() as new_page_info: login_page.locator('text=忘记密码?').click() # 点击链接,预期会打开新标签页 new_page = new_page_info.value # 断言:新页面应该是忘记密码页面 expect(new_page).to_have_url('**/forgot-password') expect(new_page.locator('h1')).to_have_text('重置密码') new_page.close()

运行测试:

pytest test_login.py -v

6.3 测试最佳实践与高级技巧

1. 使用Page Object Model (POM) 设计模式当测试套件变大时,将页面元素定位和操作封装成类,能极大提升代码的可维护性和复用性。

# pages/login_page.py class LoginPage: def __init__(self, page: Page): self.page = page self.username_input = page.locator('input[name="username"]') self.password_input = page.locator('input[name="password"]') self.submit_button = page.locator('button[type="submit"]') self.error_message = page.locator('.alert-error') def navigate(self): self.page.goto('https://demo-blog.com/login') def login(self, username, password): self.username_input.fill(username) self.password_input.fill(password) self.submit_button.click() def get_error_text(self): return self.error_message.text_content()

然后在测试中使用:

def test_login_with_pom(login_page: Page): login = LoginPage(login_page) login.navigate() login.login(VALID_USERNAME, INVALID_PASSWORD) expect(login.error_message).to_be_visible()

2. 配置与钩子 (Hooks)使用pytestconftest.py文件进行全局配置,比如设置全局超时、截图设置、登录态复用等。

# conftest.py import pytest from playwright.sync_api import Page, BrowserContext @pytest.fixture(scope='session') def browser_context_args(browser_context_args): """全局浏览器上下文配置""" return { **browser_context_args, 'viewport': {'width': 1920, 'height': 1080}, 'ignore_https_errors': True, # 忽略HTTPS证书错误(测试环境用) } @pytest.fixture(scope='function') def authed_context(context: BrowserContext): """创建一个已登录状态的上下文Fixture""" page = context.new_page() page.goto('https://demo-blog.com/login') # ... 执行登录操作 yield context # 测试结束后清理(如果需要) @pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_runtest_makereport(item, call): """测试失败时自动截图""" outcome = yield report = outcome.get_result() if report.when == "call" and report.failed: page = item.funcargs.get('page') if page: screenshot_path = f"screenshots/{item.name}_{report.when}.png" page.screenshot(path=screenshot_path, full_page=True) print(f"测试失败截图已保存至: {screenshot_path}")

3. 并行测试Playwright支持在多个浏览器、多个设备上并行运行测试,充分利用多核CPU。可以通过pytest-xdist插件实现。

# 安装 pip install pytest-xdist # 运行,使用3个worker并行 pytest test_login.py -n 3

4. 视觉回归测试Playwright可以轻松集成像pixelmatch这样的库,进行截图对比,检查UI是否有意外变更。

from PIL import Image import pixelmatch def test_homepage_visual(login_page: Page): login_page.goto('https://demo-blog.com') login_page.screenshot(path='screenshot.png') # 将 screenshot.png 与基准图片 baseline.png 进行像素对比 # ... 对比逻辑 ...

7. 部署、调试与性能优化

7.1 在CI/CD中运行Playwright

在无GUI的服务器(如GitHub Actions, Jenkins, GitLab CI)上运行Playwright,需要安装其系统依赖。

Linux (Ubuntu/Debian):

# 使用Playwright CLI安装依赖和浏览器 npx playwright install-deps # 如果使用Node.js的playwright # 对于Python,通常只需安装playwright库并执行`playwright install`,但可能需要手动安装一些系统库 sudo apt-get install -y libwoff1 libopus0 libwebpdemux2 libenchant-2-2 libgudev-1.0-0 libsecret-1-0 libhyphen0 libgles2 libevent-2.1-7

GitHub Actions 示例配置:

name: Playwright Tests on: [push] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.10' - name: Install dependencies run: | pip install -r requirements.txt playwright install playwright install-deps # 安装系统依赖 - name: Run tests run: pytest - name: Upload screenshots on failure if: failure() uses: actions/upload-artifact@v3 with: name: playwright-screenshots path: screenshots/

7.2 调试技巧

  • 慢动作模式:启动浏览器时设置slow_mo参数,让每个操作延迟执行,方便观察。
    browser = p.chromium.launch(headless=False, slow_mo=1000) # 每个操作延迟1秒
  • 录制器:使用Playwright CLI的codegen命令,通过交互自动生成脚本。这是学习API和快速创建原型的神器。
    playwright codegen https://demo-blog.com
  • 浏览器开发者工具:在非无头模式下运行,直接按F12打开开发者工具,查看Console、Network和Elements面板。
  • Playwright Inspector:设置PWDEBUG=1环境变量运行脚本,会自动打开Playwright Inspector,可以单步调试、查看选择器、录制动作。
    PWDEBUG=1 python your_script.py
  • Trace Viewer:在测试运行时记录追踪信息,生成一个可视化的时间线,精确查看每一步发生了什么,包括网络请求、DOM快照等。对于调试偶发性的失败测试极其有用。
    # 在测试开始前启动追踪 context.tracing.start(screenshots=True, snapshots=True, sources=True) # ... 执行测试 ... # 测试结束后保存追踪文件(无论成功失败) context.tracing.stop(path = "trace.zip")
    使用playwright show-trace trace.zip命令查看。

7.3 性能优化与资源管理

  • 复用Browser和Context:创建Browser和Context是昂贵的操作。在测试套件中,使用sessionmodule级别的Fixture来复用它们。
  • 并行化:如前所述,使用pytest-xdist进行测试并行化。
  • 选择性安装浏览器:如果只测试Chrome,就只安装Chromium:playwright install chromium
  • 合理设置超时:为不同的操作设置合适的超时时间。全局超时可以在启动Browser或创建Context时设置,也可以在单个操作上覆盖。
    browser = p.chromium.launch(timeout=30000) # 全局30秒超时 page.locator('#slow-element').click(timeout=15000) # 该操作15秒超时
  • 及时清理:确保在测试结束后关闭Page和Browser,释放资源。使用with语句或pytest的Fixture可以很好地管理生命周期。

从环境搭建到核心概念,从基础交互到高级网络控制,再通过两个完整的实战项目(爬虫与测试)串联所有知识点,最后探讨部署调试与性能优化,这条学习路径旨在让你不仅能写出Playwright脚本,更能理解其设计哲学,从而灵活高效地解决实际工程问题。记住,自动化工具的价值在于将人从重复劳动中解放出来,而Playwright正是目前实现这一目标最得力的武器之一。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询