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

如果你正在寻找一个能稳定、高效地搞定浏览器自动化测试的工具,无论是做UI测试、数据抓取还是RPA流程,那么Python Playwright绝对值得你花时间研究。我最初接触它是因为被Selenium的驱动版本兼容性问题折磨得够呛,一个Chrome版本更新,整个测试脚本就可能瘫痪,光是下载和配置对应版本的WebDriver就能耗掉半天。后来尝试了Puppeteer,体验好了不少,但它是Node.js生态的,对于我这种主要用Python的开发者来说,总感觉隔了一层。直到遇见了Playwright,它由微软出品,原生支持Python、JavaScript、.NET和Java,并且承诺了“跨浏览器、跨平台、跨语言”的稳定性和现代化API,我抱着试试看的心态用了一下,结果直接“真香”了。

简单来说,Playwright是一个用于Web自动化和测试的库。它的核心优势在于,它不像Selenium那样依赖外部的浏览器驱动,而是通过其自带的 playwright 命令行工具,直接下载并管理特定版本的浏览器(Chromium、Firefox、WebKit)以及一个轻量级的“Playwright驱动层”。这意味着你不再需要手动去Chrome官网找对应版本的 chromedriver ,版本匹配问题从根源上被解决了。它提供了同步和异步两套API,能模拟几乎所有的用户操作,比如点击、输入、拖拽、文件上传,甚至能拦截网络请求、模拟地理位置和设备类型,功能非常强大。对于测试来说,它内置了等待机制,能智能地等待元素出现、可点击或加载完成,大大减少了编写 time.sleep 这种不稳定等待的需求。

这个教程的目标读者,是那些已经有一定Python基础,可能用过一点Selenium或者完全没接触过浏览器自动化,但被手动测试、重复性网页操作或数据采集需求困扰的开发者、测试工程师或业务人员。我会从最基础的安装、环境配置讲起,重点解决新手最容易卡住的“驱动下载”环节,然后带你一步步写出第一个自动化脚本,并分享一些我踩过坑才总结出来的实战技巧。我们的目标是,让你看完就能动手,避开我走过的弯路。

2. 环境准备与“驱动”安装避坑指南

这是整个流程中最关键,也最容易出问题的一步。很多人在这里放弃,其实只是没搞明白Playwright的安装逻辑。我们常说的“驱动下载”,在Playwright语境下,指的不是一个单独的 exe dll 文件,而是一整套浏览器二进制文件及其配套的通信库。

2.1 Python与Playwright库安装

首先,确保你有一个Python环境(3.7及以上)。我强烈建议使用虚拟环境来管理项目依赖,这能避免不同项目间的包冲突。打开你的终端(CMD、PowerShell或终端)。

# 创建一个新的虚拟环境,名字叫 playwright_env,你可以换成任何你喜欢的名字
python -m venv playwright_env

# 激活虚拟环境
# Windows:
playwright_env\Scripts\activate
# macOS/Linux:
source playwright_env/bin/activate

激活后,你的命令行提示符前面通常会显示环境名 (playwright_env) 。接下来安装Playwright的Python库:

pip install playwright

这个命令会安装 playwright 这个Python包,它提供了我们编写脚本所需的API。但请注意, 仅仅安装这个Python包,还不能直接运行脚本 。因为Playwright需要它自己管理的浏览器来执行操作。

2.2 核心避坑点:安装浏览器(“驱动”)

安装完Python库后,你需要让Playwright去下载它所需要的浏览器。这是通过Playwright自带的一个CLI(命令行界面)工具完成的。

playwright install

这就是最核心、也最容易误解的命令。 当你运行 playwright install 时,它会自动做以下几件事:

  1. 检查你的系统(Windows、macOS、Linux)。
  2. 从Playwright的官方托管服务器(通常是微软的Azure存储)下载对应系统的最新稳定版Chromium、Firefox和WebKit(Safari的开源核心)浏览器。
  3. 将这些浏览器安装到你的用户目录下一个特定的缓存路径里(例如,Windows通常在 %USERPROFILE%\AppData\Local\ms-playwright )。
  4. 同时会下载一个轻量级的“Playwright驱动”,这个驱动负责Python代码和这些浏览器进程之间的通信。

常见坑点与解决方案:

  1. 下载速度慢或失败 :由于网络原因,从默认源下载可能会非常慢甚至超时。这是新手最大的拦路虎。

    • 解决方案 :使用国内镜像源。Playwright CLI支持通过环境变量指定下载镜像。
    • 对于Windows PowerShell或CMD
      # 设置环境变量并执行安装(一次性的)
      set PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright
      playwright install chromium  # 可以只安装你需要的浏览器,比如先装Chromium
      
    • 对于macOS/Linux的bash或zsh
      PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright playwright install chromium
      
    • 这里以淘宝的npm镜像为例,它镜像了Playwright的发布文件,亲测速度很快。你也可以搜索其他可用的镜像。
  2. 只安装特定浏览器 :如果你只需要Chrome(或Chromium),可以只安装一个,节省时间和磁盘空间。

    playwright install chromium
    # 或者
    playwright install firefox
    # 或者
    playwright install webkit
    
  3. 安装路径问题 :Playwright默认将浏览器安装到用户目录。如果你的系统盘空间紧张,或者公司策略限制,可能需要更改路径。 但请注意,官方不直接支持自定义安装路径 。一种变通方法是,先正常安装,然后将整个 ms-playwright 文件夹移动到其他位置,并设置 PLAYWRIGHT_BROWSERS_PATH 环境变量指向新位置。不过,这可能会在更新时带来复杂性,非必要不建议。

  4. 验证安装 :安装完成后,运行一个快速检查命令:

    playwright --version
    

    这会输出Playwright CLI的版本。更重要的验证是写一个简单的脚本,我们马上就来。

实操心得 :我建议在项目初期,直接使用 playwright install chromium 只安装Chromium。因为Chromium对现代Web标准支持最好,且是Playwright测试最全面的浏览器。在脚本稳定后,如果需要跨浏览器测试,再补充安装Firefox和WebKit。另外,务必在稳定的网络环境下,并善用镜像源完成这一步,它能解决90%的初期环境问题。

3. 第一个自动化脚本:从打开浏览器到截图

环境搞定,我们来写第一个脚本,感受一下Playwright的简洁。创建一个新文件,比如 first_test.py

3.1 同步API入门

Playwright提供了同步和异步两种API。对于大多数线性任务(打开页面,操作,关闭),同步API更直观,我们先从它开始。

from playwright.sync_api import sync_playwright

def main():
    # 1. 启动Playwright,它负责管理浏览器上下文
    with sync_playwright() as p:
        # 2. 启动一个Chromium浏览器实例。headless=False表示显示浏览器界面,方便调试。
        browser = p.chromium.launch(headless=False)
        # 3. 创建一个新的浏览器上下文(类似于一个独立的隐身会话)
        context = browser.new_context()
        # 4. 在上下文中打开一个新页面
        page = context.new_page()

        # 5. 导航到目标网址
        page.goto("https://www.example.com")
        # 等待页面加载到“网络空闲”状态,比简单的固定等待更智能
        page.wait_for_load_state("networkidle")

        # 6. 进行一些操作,比如截图
        page.screenshot(path="example.png")
        print("截图已保存为 example.png")

        # 7. 模拟用户输入(假设我们去了一个搜索页面)
        # page.goto("https://www.google.com")
        # page.locator('textarea[name="q"]').fill("Playwright自动化")
        # page.locator('input[value="Google 搜索"]').first.click()

        # 8. 关闭上下文和浏览器
        context.close()
        browser.close()

if __name__ == "__main__":
    main()

逐行解释:

  • sync_playwright() : 这是一个上下文管理器,它负责Playwright进程的启动和清理。
  • p.chromium.launch(headless=False) : 启动一个Chromium浏览器。 headless=False 意味着你会看到一个真实的浏览器窗口弹出。在调试阶段这非常有用。当脚本稳定后,可以改为 headless=True 在后台无界面运行,效率更高。
  • browser.new_context() : 创建一个“上下文”。这个概念很重要,它代表一个独立的浏览器会话,拥有独立的cookie、本地存储和缓存,相互隔离。你可以创建多个上下文来模拟不同用户。
  • context.new_page() : 在上下文中打开一个新标签页。
  • page.goto() : 导航到指定URL。
  • page.wait_for_load_state(“networkidle”) : 这是Playwright的一大优势。它会等待页面直到网络连接基本空闲(通常是在500ms内没有超过2个网络请求),这比等待一个固定时间或者等待某个元素出现更符合页面真实加载完成的场景。
  • page.screenshot() : 对整个页面进行截图并保存。

运行这个脚本:

python first_test.py

你应该能看到一个浏览器窗口打开,访问 example.com ,然后保存一张截图,最后浏览器关闭。

3.2 元素定位与交互

自动化测试的核心是找到页面上的元素并与之交互。Playwright提供了强大且灵活的选择器引擎。

# 接上面的代码,在 page.goto 之后,我们可以进行更多操作
# 假设我们导航到一个练习网站
page.goto("https://demo.playwright.dev/todomvc")

# 定位元素并输入
# 方式1:使用CSS选择器(最常用)
input_box = page.locator(".new-todo")
input_box.fill("学习Playwright")
input_box.press("Enter")

# 方式2:使用文本内容定位
page.locator("text=学习Playwright").click()

# 方式3:使用XPath(在CSS选择器难以表达时使用)
# page.locator("xpath=//li[contains(@class, 'todo')]").click()

# 方式4:组合定位,比如根据属性和文本
page.locator(".todo-list li:has-text('学习Playwright') .toggle").click()

# 断言:检查事项是否被标记为已完成
completed_item = page.locator(".todo-list li.completed")
assert completed_item.count() == 1
print("待办事项已完成标记验证成功!")

关于 page.locator() 和选择器的经验:

  • page.locator(selector) 返回一个 Locator 对象,它代表一个或一组页面元素。 关键点在于,这个定位操作是“惰性”的,它不会立即去DOM中查找元素,而是在你调用 .click() .fill() 等方法时才会真正执行查找。 这避免了因元素未加载而导致的瞬时错误。
  • 优先使用CSS选择器 ,它通常性能更好,可读性更高。Playwright对CSS选择器有增强,比如 :has-text()
  • text= 选择器非常实用,可以直接根据元素可见文本定位,但要注意文本内容的精确匹配或部分匹配。
  • 尽量避免使用绝对的XPath,因为它们易受页面结构微小变动的影响。使用相对XPath或CSS选择器更稳健。

4. 高级特性与实战技巧

掌握了基础操作后,我们来探讨一些让脚本更健壮、更高效的高级特性。

4.1 自动等待与超时控制

Playwright内置了智能等待,这是它比Selenium省心的地方。大多数操作(如 click , fill , wait_for_selector )都会自动等待元素变得 可操作 (可见、启用、稳定等)。

# 默认情况下,操作会等待元素最多30秒
page.click(“button#submit”)

# 你可以自定义超时时间
page.click(“button#submit”, timeout=5000) # 等待5秒

# 显式等待某个条件成立
page.wait_for_selector(“.success-message”, state=“visible”, timeout=10000)
page.wait_for_function(“window.innerWidth > 1000”) # 等待JS条件成立

技巧 :不要滥用 time.sleep() 。优先使用Playwright内置的等待条件( wait_for_selector , wait_for_load_state , wait_for_function )和操作的自动等待。只有当需要等待一个非元素相关的、固定的后台处理时间时,才考虑使用 page.wait_for_timeout(3000) (相当于 sleep(3) ),并务必在注释中说明理由。

4.2 处理弹窗、新窗口和iframe

  • 弹窗(Dialog) :如 alert , confirm , prompt

    # 监听弹窗事件,并在触发时接受(或驳回)
    page.on(“dialog”, lambda dialog: dialog.accept())
    # 然后触发会产生弹窗的操作
    page.click(“button#trigger-alert”)
    
  • 新窗口/标签页

    # 在点击会打开新窗口的链接前,先监听‘popup’事件
    with page.expect_popup() as popup_info:
        page.click(“a[target=‘_blank’]”)
    new_page = popup_info.value
    # 现在可以在 new_page 上操作了
    new_page.wait_for_load_state()
    print(new_page.title())
    
  • iframe :需要先定位到iframe元素,然后获取其 content_frame

    # 假设iframe有name或选择器
    iframe_element = page.frame_locator(“iframe#payment-form”)
    # 在iframe内部定位元素
    iframe_element.locator(“input#card-number”).fill(“1234567812345678”)
    

4.3 网络请求拦截与模拟

这是Playwright非常强大的功能,可以用于测试、屏蔽不必要的资源(如图片、样式表)以加速,或者模拟API响应。

# 拦截所有请求,并可以修改或阻止
def handle_route(route):
    # 如果是图片请求,则中止以加快速度
    if “.jpg” in route.request.url or “.png” in route.request.url:
        route.abort()
    else:
        route.continue_()

page.route(“**/*”, handle_route)

# 拦截特定请求并返回模拟响应
page.route(“**/api/user/profile”, lambda route: route.fulfill(
    status=200,
    content_type=“application/json”,
    body=json.dumps({“name”: “Mock User”, “age”: 30})
))

4.4 异步API的使用

对于需要并发操作或集成到异步框架(如FastAPI)中的场景,异步API是更好的选择。

import asyncio
from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        context = await browser.new_context()
        page = await context.new_page()

        await page.goto(“https://www.example.com”)
        title = await page.title()
        print(f”页面标题: {title}“)

        await browser.close()

asyncio.run(main())

选择同步还是异步? 对于简单的线性脚本,同步API更简单直接。如果你需要同时控制多个浏览器页面,或者你的应用本身就是异步的,那么异步API能提供更好的性能和资源利用率。

5. 项目结构建议与持续集成集成

当你的自动化脚本越来越多时,一个好的项目结构至关重要。

my_playwright_project/
├── requirements.txt          # 项目依赖
├── conftest.py              # Pytest配置(如果使用Pytest)
├── pages/                   # 页面对象模型(Page Object Model)
│   ├── __init__.py
│   ├── login_page.py
│   └── dashboard_page.py
├── tests/                   # 测试用例
│   ├── __init__.py
│   ├── test_login.py
│   └── test_dashboard.py
├── fixtures/                # 测试夹具
│   └── browser_context.py
├── utils/                   # 工具函数
│   ├── helpers.py
│   └── data_generator.py
└── reports/                 # 测试报告(由Allure等生成)

使用Pytest作为测试框架 :Pytest与Playwright结合得很好。你可以使用 pytest-playwright 插件,它提供了方便的Fixture(如 page , context , browser )。

# test_example.py
import pytest

def test_homepage_has_correct_title(page): # ‘page’ fixture由插件提供
    page.goto(“https://www.example.com”)
    assert page.title() == “Example Domain”

def test_search_functionality(page):
    page.goto(“https://www.google.com”)
    page.locator(‘[name=“q”]’).fill(“Playwright”)
    page.locator(‘[name=“btnK”]’).first.click()
    # 等待结果出现
    page.wait_for_selector(“#search”)
    assert “playwright” in page.title().lower()

在CI/CD中运行 :在GitHub Actions、GitLab CI或Jenkins中运行Playwright测试,需要确保CI环境安装了浏览器依赖。Playwright提供了一个专用命令:

# GitHub Actions 示例步骤
- name: Install Playwright Browsers
  run: npx playwright install --with-deps chromium
  # 注意:这里用了npx,对应Node.js环境。Python项目在CI中通常也需要安装Python依赖后,再运行`playwright install`
# 更常见的Python项目CI步骤:
- run: pip install -r requirements.txt
- run: playwright install chromium
- run: pytest tests/ --headless # 以无头模式运行测试

6. 常见问题排查与调试技巧

即使一切配置正确,在复杂的网页上编写脚本也会遇到各种问题。以下是我总结的排查清单。

问题现象 可能原因 排查步骤与解决方案
playwright install 失败或极慢 网络连接问题,默认源被墙或限速。 1. 使用国内镜像源: PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright playwright install chromium
2. 检查代理设置(如果公司有代理)。
3. 尝试只安装一个浏览器。
Error: Executable doesn‘t exist at ... 浏览器可执行文件路径错误或损坏。 1. 重新运行 playwright install
2. 检查环境变量 PLAYWRIGHT_BROWSERS_PATH 是否被意外设置,指向了错误路径。
3. 手动删除缓存目录(如 ~/AppData/Local/ms-playwright on Windows)后重装。
元素找不到(TimeoutError) 1. 选择器写错了。
2. 元素在iframe里。
3. 元素是动态加载的,等待时间不足。
4. 元素被其他元素遮挡。
1. 调试利器 :使用 page.pause() 。在脚本中插入这行,运行时会打开Playwright Inspector,你可以实时查看页面、测试选择器、记录操作。
2. 使用浏览器开发者工具检查元素,复制其CSS选择器或XPath。
3. 增加超时时间,或使用更精确的等待条件( wait_for_selector )。
4. 检查是否有弹窗、遮罩层挡住了目标元素。
点击或输入没有效果 1. 元素不可交互(disabled, hidden)。
2. 点击在了错误的位置。
3. 页面有未处理的弹窗阻塞。
1. 使用 page.locator(“button”).is_enabled() 检查状态。
2. 尝试 page.locator(“button”).click(force=True) 强制点击(非必要不用)。
3. 使用 page.locator(“button”).dispatch_event(‘click’) 模拟JS事件。
4. 监听并处理 dialog 事件。
脚本在CI上通过,本地失败(或反之) 环境差异:屏幕分辨率、浏览器版本、Cookie/缓存、网络延迟。 1. 在CI配置中固定浏览器版本: playwright install chromium@版本号
2. 在 browser.new_context() 中统一视口大小: viewport={‘width’: 1920, ‘height’: 1080}
3. 使用 browser.new_context(ignore_https_errors=True) 忽略可能的证书错误(测试环境)。
4. 在本地尝试以 headless=True 模式运行,模拟CI环境。
性能慢 1. 页面加载了太多资源。
2. 等待策略不佳,用了太多 sleep
1. 使用 page.route 拦截并中止不必要的资源请求(如图片、字体、样式表)。
2. 用 wait_for_load_state(‘networkidle’) 代替固定等待。
3. 考虑使用异步API并发处理多个独立任务。

调试金钥匙:Playwright Inspector 这是Playwright自带的图形化调试工具,强烈推荐。

  • 方式1 :在代码中需要调试的地方插入 page.pause() 。运行脚本时,浏览器和Inspector都会打开。
  • 方式2 :设置环境变量 PWDEBUG=1 运行脚本。 PWDEBUG=1 python your_script.py 。这会在调试模式下运行,并提供逐步执行、查看选择器等功能。
  • 方式3 :使用 playwright codegen 命令,它可以打开一个浏览器并记录你的操作,实时生成对应的Python代码,是学习选择器和API的绝佳方式。
    playwright codegen https://www.example.com
    

最后,保持耐心,复杂网页的自动化本身就是一个与页面结构“斗智斗勇”的过程。多利用Inspector,多查阅 官方文档 ,社区的解决方案通常也很丰富。当你成功地将那些繁琐、重复的网页操作变成一键执行的脚本时,那种效率提升的成就感,就是坚持下来的最好回报。

更多推荐