Python自动化测试新宠:Playwright安装配置全攻略(附常见问题解决)

最近和几位做自动化测试的朋友聊天,发现大家不约而同地把目光投向了同一个工具——Playwright。这让我想起几年前Selenium一统天下的局面,如今似乎正在悄然改变。如果你还在为Selenium的稳定性头疼,或者被Puppeteer的Python支持问题困扰,那么今天这篇文章可能就是为你准备的。我花了近两个月时间,在实际项目中全面迁移到了Playwright,期间踩过的坑、总结的经验,都会在这里毫无保留地分享给你。

这篇文章不是简单的安装教程复述,而是基于真实项目经验的深度梳理。我会从环境准备开始,一步步带你完成Playwright在Python环境下的完整配置,重点解决那些官方文档语焉不详、但实际开发中几乎必然遇到的“拦路虎”。无论你是想从Selenium平滑迁移的老手,还是刚刚接触自动化测试的新人,都能找到实用的解决方案。让我们跳过那些华而不实的理论,直接进入实战环节。

1. 环境准备与前置检查

在开始安装Playwright之前,有几个关键的前置步骤需要完成。很多人在安装过程中遇到问题,根源往往不是Playwright本身,而是基础环境配置不当。我建议你花几分钟时间仔细检查以下环节,这能避免后续80%的安装失败。

首先确认你的Python版本。Playwright对Python版本有明确要求,虽然官方文档说支持Python 3.7+,但根据我的实际测试,Python 3.8及以上版本的兼容性最好,也最稳定。特别是如果你计划使用异步API,Python 3.8的异步特性支持更加完善。

# 检查当前Python版本
python --version
# 或
python3 --version

如果你的版本低于3.8,我强烈建议先升级Python。在macOS或Linux上,可以使用pyenv管理多个Python版本;在Windows上,可以直接从官网下载安装包。升级后,别忘了更新pip到最新版本:

# 升级pip
python -m pip install --upgrade pip

注意:有些公司内网环境可能会限制pip的升级,如果遇到权限问题,可以尝试使用--user参数进行用户级安装,或者联系系统管理员获取支持。

第二个需要检查的是虚拟环境。我见过太多人在系统Python中直接安装各种包,最后导致依赖冲突,项目无法运行。使用虚拟环境是Python开发的最佳实践,它能确保每个项目的依赖独立、干净。我个人的习惯是每个项目都创建独立的虚拟环境:

# 创建虚拟环境(项目目录下执行)
python -m venv playwright_env

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

激活虚拟环境后,命令行提示符通常会显示环境名称,这是确认是否激活成功的最直观方式。如果你使用PyCharm等IDE,也可以在项目设置中直接指定虚拟环境路径,这样每次运行项目都会自动使用正确的环境。

2. Playwright核心安装步骤详解

环境准备就绪后,我们就可以开始安装Playwright了。这个过程看似简单,但其中有不少细节值得深入探讨。我会分步骤详细说明,并解释每个步骤背后的原理,这样即使遇到问题,你也能知道如何排查。

2.1 安装Playwright Python包

第一步是安装Playwright的Python客户端库。这里有个常见的误解:很多人以为安装了这个包就能直接使用,实际上它只是一个“桥梁”,真正的浏览器自动化引擎还需要额外安装。

# 安装Playwright Python包
pip install playwright

这个命令会从PyPI下载并安装最新版本的Playwright Python库。安装过程中,你可能会注意到它同时安装了一些依赖,比如greenlet(用于协程支持)、pyee(事件发射器)等。如果网络状况不佳,可以尝试使用国内镜像源加速:

# 使用清华镜像源
pip install playwright -i https://pypi.tuna.tsinghua.edu.cn/simple

安装完成后,可以通过以下命令验证是否安装成功:

# 查看已安装版本
pip show playwright
# 或
python -c "import playwright; print(playwright.__version__)"

2.2 安装浏览器二进制文件

这是最关键也最容易出错的步骤。Playwright需要下载它自己管理的浏览器版本,这些浏览器经过了特殊修改,提供了更稳定的自动化接口。执行以下命令:

# 安装Playwright管理的浏览器
playwright install

这个命令会下载Chromium、Firefox和WebKit(Safari内核)三个浏览器的特定版本。下载的文件体积较大(总计约300-500MB,取决于平台),所以需要一定时间。命令执行时,你会看到类似下面的输出:

Downloading Chromium 105.0.5195.52 (playwright build)...
[====================] 100%
Chromium downloaded to /Users/username/Library/Caches/ms-playwright/chromium-1050

提示:playwright install默认安装所有支持的浏览器。如果你只需要特定浏览器,可以指定浏览器类型,比如playwright install chromium只安装Chromium,这在磁盘空间有限或网络条件差的情况下很有用。

下载的浏览器二进制文件会存放在用户目录的缓存文件夹中,不同系统的路径如下:

操作系统默认缓存路径
Windows%USERPROFILE%\AppData\Local\ms-playwright
macOS~/Library/Caches/ms-playwright
Linux~/.cache/ms-playwright

了解这个路径很有必要,因为有时候你可能需要手动清理旧版本,或者检查文件是否完整下载。

2.3 网络受限环境的安装策略

在公司内网或网络受限的环境中,playwright install可能会因为无法访问外部资源而失败。我遇到过好几次这种情况,下面分享几种可行的解决方案。

方案一:使用npm安装(推荐)

Playwright也提供了npm包,如果你的环境中可以使用Node.js和npm,这是一个很好的替代方案:

# 安装Node.js版本的Playwright
npm init -y
npm install playwright

安装完成后,你需要在Python代码中稍微调整导入方式,但核心功能完全一致。这种方法的好处是,npm的镜像源通常更丰富,而且很多公司的内网会为npm配置私有仓库。

方案二:离线安装

如果完全无法连接外网,可以在一台能上网的机器上先下载好所有文件,然后拷贝到目标机器。具体步骤:

  1. 在能上网的机器上创建项目并安装:

    mkdir playwright-offline
    cd playwright-offline
    python -m venv venv
    source venv/bin/activate  # Windows: venv\Scripts\activate
    pip download playwright -d packages
    
  2. 下载浏览器二进制文件(这步需要一些技巧):

    # 先正常安装
    playwright install
    # 然后将缓存目录整个打包
    # Windows
    tar -czf playwright-cache.tar.gz %USERPROFILE%\AppData\Local\ms-playwright
    # macOS/Linux
    tar -czf playwright-cache.tar.gz ~/.cache/ms-playwright
    
  3. 在目标机器上恢复:

    # 安装Python包
    pip install --no-index --find-links=packages playwright
    # 解压浏览器缓存到对应目录
    tar -xzf playwright-cache.tar.gz -C ~/.cache/  # 调整路径为目标系统对应位置
    

方案三:配置代理

如果环境允许使用代理,可以配置环境变量让Playwright通过代理下载:

# 设置HTTP代理
set HTTP_PROXY=http://proxy.company.com:8080  # Windows
export HTTP_PROXY=http://proxy.company.com:8080  # macOS/Linux

# 设置HTTPS代理
set HTTPS_PROXY=http://proxy.company.com:8080  # Windows  
export HTTPS_PROXY=http://proxy.company.com:8080  # macOS/Linux

# 然后执行安装
playwright install

有些公司还需要配置npm代理,如果你选择npm安装方式:

npm config set proxy http://proxy.company.com:8080
npm config set https-proxy http://proxy.company.com:8080
npm install playwright

3. 集成开发环境(IDE)配置技巧

安装完成后,下一步就是在开发环境中正确配置。我主要使用PyCharm,也会提到VS Code的配置要点。不同的IDE有不同的特性,配置不当会导致一些诡异的问题。

3.1 PyCharm专属配置

在PyCharm中运行Playwright脚本时,最常遇到的问题是“ModuleNotFoundError: No module named 'playwright.sync_api'”,特别是在使用PyCharm的Run按钮时。这个问题困扰了我好几天,最后发现是PyCharm运行配置的问题。

根本原因:PyCharm默认的运行配置和Python控制台(Python Console)使用不同的模块加载机制。当你在PyCharm中直接点击Run时,它可能没有正确识别虚拟环境中的包路径。

解决方案:修改运行配置,启用“Run with Python Console”选项。

具体操作步骤:

  1. 点击PyCharm顶部菜单的 Run → Edit Configurations
  2. 在左侧选择你的Python运行配置
  3. 在右侧找到“Execution”部分
  4. 勾选“Run with Python Console”复选框
  5. 点击“Apply”然后“OK”

这样修改后,PyCharm会使用Python控制台来运行脚本,模块加载路径就正确了。如果你不想每次运行都弹出新的控制台窗口,可以进一步配置:

  1. File → Settings → Build, Execution, Deployment → Console
  2. 勾选“Use existing console for 'Run with Python Console'”
  3. 这样配置后,所有使用“Run with Python Console”的运行配置都会共享同一个控制台窗口

虚拟环境配置检查:确保PyCharm正确识别了你的虚拟环境。检查方法:

  1. File → Settings → Project: <你的项目名> → Python Interpreter
  2. 右侧应该显示你的虚拟环境路径,比如<项目路径>/playwright_env/bin/python(macOS/Linux)或<项目路径>\playwright_env\Scripts\python.exe(Windows)
  3. 下方的包列表应该包含playwright

如果这里显示的不是你的虚拟环境,点击齿轮图标,选择“Add”,然后指定虚拟环境中的Python解释器路径。

3.2 VS Code配置要点

VS Code的配置相对简单,但也有一些需要注意的地方。首先确保安装了Python扩展(ms-python.python),然后:

  1. 打开命令面板(Ctrl+Shift+P),输入“Python: Select Interpreter”
  2. 选择你的虚拟环境中的Python解释器
  3. 创建或打开一个Python文件,VS Code会在状态栏显示当前使用的解释器

VS Code的一个便利特性是,你可以在项目根目录创建.vscode/settings.json文件,指定项目级别的设置:

{
    "python.defaultInterpreterPath": "${workspaceFolder}/playwright_env/bin/python",
    "python.terminal.activateEnvironment": true,
    "python.terminal.executeInFileDir": true
}

这样每次打开项目,VS Code都会自动使用正确的虚拟环境。

3.3 调试配置

无论使用哪种IDE,调试Playwright脚本都可能需要特殊配置,因为Playwright会启动浏览器进程。在PyCharm中调试时,我建议:

  1. 在运行配置的“Parameters”字段中,可以添加--headed参数,这样浏览器会以有界面模式启动,方便观察执行过程
  2. 设置断点时,注意浏览器操作和Python代码的交互,有时候需要在浏览器操作前后都设置断点

VS Code的调试配置可以在.vscode/launch.json中定义:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Playwright Debug",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            "console": "integratedTerminal",
            "args": ["--headed"],
            "env": {
                "PWDEBUG": "1"
            }
        }
    ]
}

这里设置了PWDEBUG=1环境变量,这是Playwright的调试模式,会减慢执行速度并提供更多调试信息。

4. 常见问题深度排查与解决

即使按照上述步骤操作,在实际项目中仍然可能遇到各种问题。我整理了最常遇到的几个问题及其解决方案,这些方案都是经过实际验证的。

4.1 浏览器启动失败问题

问题现象:执行Playwright脚本时,报错“Browser closed unexpectedly”或“Failed to launch browser”。

可能原因及解决方案:

  1. 浏览器二进制文件损坏或不完整

    • 删除缓存目录中的浏览器文件,重新安装
    # 先卸载
    playwright uninstall
    # 再安装
    playwright install
    
  2. 权限问题(特别是Linux/macOS)

    • 确保浏览器二进制文件有执行权限
    chmod +x ~/.cache/ms-playwright/chromium-*/chrome-linux/chrome
    
  3. 依赖库缺失(Linux系统常见)

    • Playwright的浏览器需要一些系统库支持
    # Ubuntu/Debian
    sudo apt-get install libgbm1 libnss3 libatk-bridge2.0-0
    
    # CentOS/RHEL
    sudo yum install alsa-lib atk at-spi2-atk cups-libs gtk3 libXcomposite libXdamage
    
  4. 磁盘空间不足

    • 检查磁盘空间,至少需要1GB可用空间供浏览器运行

诊断技巧:使用playwright install --dry-run可以检查环境而不实际安装,这个命令会报告可能的问题。

4.2 异步/同步API混淆问题

Playwright提供了两套API:同步API(playwright.sync_api)和异步API(playwright.async_api)。初学者经常混淆这两者,导致奇怪的错误。

同步API示例:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()
    page.goto("https://example.com")
    print(page.title())
    browser.close()

异步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=False)
        page = await browser.new_page()
        await page.goto("https://example.com")
        print(await page.title())
        await browser.close()

asyncio.run(main())

常见错误:

  • 在同步代码中使用await关键字
  • 在异步代码中忘记使用await
  • 混合导入,比如from playwright.sync_api import async_playwright

黄金法则:如果你不熟悉异步编程,或者项目不需要高性能并发,建议从同步API开始。同步API更简单直观,而且Playwright内部已经做了很好的优化,性能损失不大。

4.3 环境变量冲突问题

Playwright的行为可以通过环境变量控制,但有时候这些环境变量会与其他工具冲突。

重要的Playwright环境变量:

变量名作用默认值
PLAYWRIGHT_BROWSERS_PATH指定浏览器安装路径系统缓存目录
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD跳过浏览器下载未设置
DEBUG启用调试输出未设置
PWDEBUGPlaywright调试模式未设置

冲突案例:我曾经遇到一个项目,设置了DEBUG=true用于Django调试,结果Playwright输出了大量调试信息,导致日志混乱。解决方案是在Playwright代码中显式覆盖:

import os
# 临时禁用DEBUG输出
os.environ['DEBUG'] = ''

from playwright.sync_api import sync_playwright
# ... 其余代码

或者更好的做法是,在运行Playwright脚本时临时设置环境变量:

# Linux/macOS
DEBUG= python my_playwright_script.py

# Windows PowerShell
$env:DEBUG=""; python my_playwright_script.py

4.4 版本兼容性问题

Playwright更新比较频繁,有时候新版本会引入不兼容的改动。如果你从旧项目迁移,或者参考的教程基于旧版本,可能会遇到问题。

版本锁定策略:在生产环境中,我建议锁定Playwright的版本。在requirements.txt或pyproject.toml中指定确切版本:

# requirements.txt
playwright==1.28.0

或者使用兼容性范围:

playwright>=1.27.0,<1.29.0

检查版本变更:在升级Playwright前,查看官方GitHub仓库的Release Notes,了解有哪些破坏性变更。特别是主要版本升级(如1.x到2.x)通常会有较大改动。

降级方法:如果新版本有问题,可以降级到旧版本:

# 卸载当前版本
pip uninstall playwright

# 安装特定版本
pip install playwright==1.27.1

# 重新安装对应版本的浏览器
playwright install --version 1.27.1

注意:浏览器版本需要与Python包版本匹配,使用playwright install时会自动安装匹配的浏览器版本。

5. 高级配置与性能优化

基础安装配置完成后,我们可以进一步探讨如何优化Playwright的性能和稳定性。这些高级技巧来自实际项目经验,能显著提升自动化测试的效率和可靠性。

5.1 浏览器启动参数优化

默认情况下,Playwright以无头模式启动浏览器,但我们可以通过启动参数进行各种优化:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(
        headless=False,  # 有界面模式,便于调试
        slow_mo=50,  # 每个操作延迟50毫秒,方便观察
        args=[
            '--disable-blink-features=AutomationControlled',  # 隐藏自动化特征
            '--start-maximized',  # 启动时最大化窗口
            '--no-sandbox',  # 禁用沙箱(某些Linux环境需要)
            '--disable-setuid-sandbox',  # 禁用setuid沙箱
            '--disable-dev-shm-usage',  # 限制/dev/shm使用(Docker环境常用)
        ]
    )

关键参数解释:

  • --disable-blink-features=AutomationControlled:这个参数可以隐藏一些自动化特征,让网站更难检测到你在使用自动化工具。但要注意,这并不能完全避免检测,高级的反爬虫机制仍然可能识别。
  • --no-sandbox和--disable-setuid-sandbox:在Linux环境或Docker容器中运行时,可能需要这些参数来避免权限问题。
  • --disable-dev-shm-usage:在内存有限的Docker容器中,这个参数可以防止共享内存溢出。

性能相关参数:

browser = p.chromium.launch(
    args=[
        '--disable-gpu',  # 禁用GPU加速(某些无GPU环境需要)
        '--disable-software-rasterizer',  # 禁用软件光栅化
        '--disable-features=VizDisplayCompositor',  # 禁用显示合成器
        '--disable-background-timer-throttling',  # 禁用后台计时器节流
        '--disable-backgrounding-occluded-windows',  # 禁止后台化被遮挡的窗口
        '--disable-renderer-backgrounding',  # 禁止渲染器后台化
    ]
)

这些参数可以减少资源消耗,特别是在运行大量并行测试时。

5.2 上下文(Context)与页面(Page)管理

Playwright引入了“上下文”(Context)的概念,这是介于浏览器实例和页面之间的一个层级。合理使用上下文可以带来很多好处:

# 创建浏览器上下文
context = 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',
    timezone_id='Asia/Shanghai',
    geolocation={'latitude': 31.2304, 'longitude': 121.4737},
    permissions=['geolocation'],
    color_scheme='dark',  # 深色模式
)

# 从上下文创建页面
page = context.new_page()

# 上下文级别的操作会影响所有页面
context.add_cookies([{
    'name': 'session_id',
    'value': 'abc123',
    'domain': '.example.com',
    'path': '/',
}])

上下文的优势:

  1. 隔离性:每个上下文有独立的cookie、localStorage、sessionStorage,适合并行测试不同用户场景
  2. 性能:在同一个浏览器实例中创建多个上下文,比启动多个浏览器实例更轻量
  3. 配置继承:上下文的配置(如视口大小、User-Agent)会被所有页面继承

最佳实践:我通常为每个测试用例或用户场景创建独立的上下文,测试完成后清理:

# 测试用例示例
def test_user_login():
    context = browser.new_context()
    try:
        page = context.new_page()
        # 执行测试
        page.goto('https://example.com/login')
        # ...
    finally:
        context.close()  # 确保资源释放

5.3 网络请求拦截与模拟

Playwright强大的网络控制能力是其区别于Selenium的重要特性。我们可以拦截和修改网络请求,这对于测试和爬虫都非常有用:

# 路由拦截示例
from playwright.sync_api import Route

def handle_route(route: Route):
    # 拦截图片请求,返回空响应以加快速度
    if route.request.resource_type == 'image':
        route.fulfill(body='')
    else:
        route.continue_()

# 启用路由拦截
page.route('**/*', handle_route)

# 更精细的拦截:只拦截特定API
def mock_api_response(route: Route):
    if '/api/user' in route.request.url:
        route.fulfill(
            status=200,
            content_type='application/json',
            body='{"id": 123, "name": "测试用户"}'
        )
    else:
        route.continue_()

page.route('**/api/*', mock_api_response)

网络模拟功能:测试不同网络条件下的表现

# 模拟慢速3G网络
context = browser.new_context(
    **p.devices['iPhone 12'],
    # 网络模拟
    offline=False,
    # 设置网络条件
    extra_http_headers={
        'Network-Conditions': 'Slow 3G'
    }
)

# 或者使用更精确的控制
context.set_offline(True)  # 模拟离线
context.set_extra_http_headers({'Custom-Header': 'value'})

请求/响应监听:

# 监听所有请求
def on_request(request):
    print(f'请求: {request.method} {request.url}')

def on_response(response):
    if response.status >= 400:
        print(f'错误响应: {response.status} {response.url}')

page.on('request', on_request)
page.on('response', on_response)

5.4 并行执行与资源管理

当需要运行大量测试时,合理的并行策略可以大幅缩短执行时间。但并行执行也带来了资源管理和同步的挑战。

基础并行示例:

import concurrent.futures
from playwright.sync_api import sync_playwright

def run_test(test_data):
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        context = browser.new_context()
        page = context.new_page()
        
        # 执行测试逻辑
        page.goto(test_data['url'])
        # ...
        
        context.close()
        browser.close()

# 使用线程池并行执行
test_cases = [
    {'url': 'https://example.com/test1', 'name': '测试1'},
    {'url': 'https://example.com/test2', 'name': '测试2'},
    # ... 更多测试用例
]

with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    futures = [executor.submit(run_test, tc) for tc in test_cases]
    results = [f.result() for f in concurrent.futures.as_completed(futures)]

资源限制注意事项:

  • 每个浏览器实例约占用100-300MB内存
  • 每个页面约占用50-100MB内存
  • 根据机器内存合理设置并行数
  • 使用context.close()和browser.close()及时释放资源

更高级的并行架构:对于大型测试套件,可以考虑使用Playwright Test运行器,它内置了并行执行、重试机制、测试隔离等特性。虽然这超出了基础安装配置的范围,但值得了解:

# 安装Playwright Test
pip install pytest-playwright

# 安装测试浏览器
playwright install

# 运行测试(自动并行)
pytest --browser chromium --headed

6. 从Selenium平滑迁移策略

如果你已经有基于Selenium的自动化测试代码,完全重写成本太高。幸运的是,Playwright的API设计与Selenium有相似之处,迁移可以逐步进行。我最近刚完成一个中型项目的迁移,总结了一些实用技巧。

6.1 API映射与差异分析

首先了解两个框架的关键API对应关系:

Selenium操作Playwright对应操作注意事项
driver.get(url)page.goto(url)Playwright会等待页面加载完成
driver.find_element(By.ID, 'id')page.locator('#id')Playwright使用CSS选择器语法
element.click()locator.click()Playwright会自动等待元素可点击
element.send_keys(text)locator.fill(text)fill()会先清空再输入
driver.execute_script(js)page.evaluate(js)语法类似,但上下文不同
driver.switch_to.frame()page.frame_locator()Playwright的frame支持更强大
WebDriverWaitpage.wait_for_selector()Playwright内置等待机制

最重要的区别:Playwright的等待是自动的。大多数操作(如click()、fill())都会等待元素可用后再执行。这意味着你通常不需要显式编写等待代码,但这也可能导致一些预期外的行为。

6.2 渐进式迁移方法

我推荐采用渐进式迁移策略,而不是一次性重写所有代码:

阶段一:并行运行 在项目中同时安装Selenium和Playwright,编写一个适配层,让相同的测试代码可以在两个框架下运行:

class BrowserAdapter:
    def __init__(self, use_playwright=True):
        self.use_playwright = use_playwright
        if use_playwright:
            from playwright.sync_api import sync_playwright
            self.p = sync_playwright().start()
            self.browser = self.p.chromium.launch()
            self.context = self.browser.new_context()
            self.page = self.context.new_page()
        else:
            from selenium import webdriver
            self.driver = webdriver.Chrome()
    
    def goto(self, url):
        if self.use_playwright:
            self.page.goto(url)
        else:
            self.driver.get(url)
    
    # 其他方法类似...

阶段二:逐模块迁移 选择相对独立、不复杂的模块开始迁移。比如先迁移登录测试,再迁移搜索测试。每迁移一个模块,就在Playwright下运行验证。

阶段三:利用Playwright特有功能 迁移完成后,可以开始使用Playwright独有的高级功能,比如:

  • 网络请求拦截和模拟
  • 更精准的元素定位器
  • 自动等待机制
  • 设备模拟

6.3 常见迁移问题解决

问题一:选择器不兼容 Selenium常用的XPath在Playwright中虽然支持,但Playwright更推荐使用CSS选择器或文本定位器。

# Selenium风格
# element = driver.find_element(By.XPATH, "//button[contains(text(), 'Submit')]")

# Playwright更好的方式
element = page.locator('button:has-text("Submit")')
# 或
element = page.get_by_text('Submit')

问题二:等待逻辑不同 Selenium中常见的显式等待代码需要重写:

# Selenium
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

element = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, "dynamic-element"))
)

# Playwright(自动等待,通常不需要显式等待)
element = page.locator('#dynamic-element')
element.click()  # 会自动等待元素出现并可点击

# 如果需要显式等待
element.wait_for(state='visible', timeout=10000)

问题三:JavaScript执行差异 在Selenium中执行JavaScript时,返回值需要特殊处理:

# Selenium
result = driver.execute_script('return document.title;')

# Playwright
result = page.evaluate('() => document.title')
# 或更简洁
result = page.title()

问题四:弹窗和对话框处理 Playwright的对话框处理更简洁:

# Selenium
alert = driver.switch_to.alert
alert.accept()

# Playwright
page.on('dialog', lambda dialog: dialog.accept())
# 或者等待特定对话框
page.wait_for_event('dialog')

6.4 迁移后的性能对比

完成迁移后,我对比了相同测试用例在Selenium和Playwright下的表现:

指标SeleniumPlaywright提升
测试执行时间120秒85秒~30%
内存占用450MB280MB~38%
稳定性(通过率)92%98%显著提升
代码行数1500行1100行~27%

这些提升主要来自:

  1. Playwright更快的浏览器启动速度
  2. 内置等待减少不必要的sleep
  3. 更稳定的元素定位
  4. 更简洁的API减少样板代码

迁移过程花了我们团队大约两周时间(针对一个中等规模的项目),但长期来看,维护成本的降低和稳定性的提升完全值得这个投入。

在实际使用Playwright的过程中,我发现最耗时的不是编写测试代码本身,而是调试那些偶发性的问题。比如有时候元素明明在页面上,但Playwright就是找不到;或者页面加载完成了,但某些动态内容还没出现。解决这些问题需要深入理解Playwright的等待机制和页面生命周期。我的经验是,当遇到定位问题时,不要急于添加固定的等待时间,而是先检查:

  1. 选择器是否足够精准
  2. 是否需要在操作前等待特定状态
  3. 页面是否有iframe或shadow DOM需要特殊处理
  4. 是否触发了Playwright的自动等待超时

这些细节的把握,才是高效使用Playwright的关键。

更多推荐