三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Python Playwright自动化测试:从原理到实战的完整指南

Python Playwright自动化测试:从原理到实战的完整指南

1. 项目概述:为什么说Playwright是Python Web自动化测试的革命者?

如果你和我一样,在Web自动化测试领域摸爬滚打了几年,从Selenium WebDriver的“等待元素出现”到处理各种弹窗和iframe,再到为不同浏览器维护驱动而头疼,那你一定对“稳定”和“高效”这两个词有着近乎偏执的追求。几年前,当微软推出Playwright时,我最初是抱着怀疑态度的——又一个自动化框架?但当我真正把它用在几个大型、复杂的Web应用测试项目后,我的看法彻底改变了。Playwright不是Selenium的简单替代品,它更像是一个从底层重新思考了“浏览器自动化”这件事的现代解决方案。

简单来说,Playwright是一个由微软开发的、用于实现端到端(E2E)Web测试的自动化库。它支持Chromium(Chrome、Edge)、Firefox和WebKit(Safari)三大浏览器引擎,并且为Node.js、Python、Java和C#都提供了原生API。我们今天聚焦在Python上。它的“革命性”体现在哪里?我总结为三点:原生多浏览器支持、无头模式的稳定性、以及基于上下文的强大隔离能力。这听起来可能有点抽象,我举个例子:以前用Selenium做多浏览器并行测试,你得为每个浏览器实例单独管理驱动和会话,进程间通信和资源竞争问题常常让你在深夜调试。而Playwright通过其“浏览器上下文”(Browser Context)的概念,让你能在一个浏览器实例内,轻松创建多个完全隔离的、带有独立cookie、localStorage和会话的“虚拟浏览器环境”,并行执行测试用例,资源消耗大幅降低,稳定性却显著提升。

对于测试工程师、开发自测人员,甚至是需要做网页数据抓取和监控的开发者来说,Playwright提供了一套更现代、更可靠的工具链。它解决了传统自动化工具的几个核心痛点:跨浏览器一致性差、执行速度慢(特别是无头模式)、以及对现代Web技术(如Shadow DOM、网络拦截)支持不足的问题。接下来,我会带你从零开始,深入Playwright在Python下的世界,不仅告诉你“怎么用”,更会分享我在实际项目中“为什么这么用”以及“踩过哪些坑”。

2. 核心设计理念与架构优势解析

2.1 与Selenium的根本性差异:协议与架构

要理解Playwright的强大,首先要明白它和前辈Selenium的核心区别。Selenium WebDriver基于W3C WebDriver协议,这是一个标准的HTTP REST API。你的测试脚本(客户端)通过发送HTTP请求(如POST /session/{sessionId}/element)给浏览器驱动(如ChromeDriver),驱动再通过调试协议(如Chrome DevTools Protocol)控制浏览器。这个链条长,且通信开销大。

Playwright则走了另一条路。它直接使用各个浏览器厂商提供的底层调试协议(如CDP for Chromium)。更重要的是,Playwright启动时,会同时启动一个浏览器服务端和一个客户端。你的Python脚本(客户端)通过WebSocket与Playwright的服务端通信,服务端再通过CDP等协议控制浏览器。这种架构带来了几个立竿见影的好处:

  1. 更快的执行速度:WebSocket是全双工、长连接,比HTTP的请求-响应模式更快,减少了建立连接的开销。
  2. 更丰富的控制能力:因为直连调试协议,Playwright可以做到许多WebDriver协议不支持的事情,比如:
    • 网络拦截与模拟:轻松模拟离线、慢速网络,拦截和修改任何网络请求(XHR, Fetch)。
    • 原生输入模拟:提供更真实的鼠标移动、键盘输入和触摸事件,而不是简单的DOM事件触发。
    • 访问浏览器上下文:直接操作多个页面(Page)、弹出窗口(Popup)、iframe,甚至Service Worker。

2.2 核心概念:Browser, Context 和 Page

这是Playwright架构中最精妙的部分,理解它们的关系至关重要。

  • Browser:代表一个实际的浏览器进程实例。你可以通过playwright.chromium.launch()启动一个Chrome/Edge,或者playwright.firefox.launch()启动Firefox。启动浏览器是资源消耗最大的操作。
  • BrowserContext(上下文):这是Playwright的“王牌”特性。一个Browser实例下可以创建多个完全隔离的BrowserContext。每个Context都拥有独立的:
    • Cookie、localStorage、sessionStorage
    • 缓存、权限设置(如地理位置、通知)
    • 代理配置为什么这很重要?想象一下你需要测试同一个用户在不同标签页登录两个不同账号的场景。在Selenium里,这几乎不可能(因为cookie共享)。在Playwright里,你只需要创建两个Context,分别登录即可,它们互不干扰。这为并行测试和数据隔离提供了完美基础。
  • Page:代表一个浏览器标签页或一个弹出窗口。它存在于某个BrowserContext中。我们绝大部分的自动化操作(如page.goto(),page.click())都是在Page对象上进行的。

它们的关系可以这样类比:Browser是电脑,Context是电脑上的不同用户账户,Page是每个用户账户下打开的浏览器窗口或标签页。这种层级设计让资源管理和测试隔离变得异常清晰和高效。

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

Playwright的Python API提供了两套:playwright.sync_api(同步)和playwright.async_api(异步)。这不是简单的语法糖,而是性能的关键。

  • 同步API:使用with sync_playwright() as p:上下文管理器。代码写起来是线性的,更符合传统脚本的思维,易于理解和调试。但它本质上是阻塞的。当执行page.click()等待元素时,整个线程都在等待。
  • 异步API:使用async with async_playwright() as p:,并且所有方法前都需要加await。它基于Python的asyncio库。它的优势在于可以高效处理I/O等待。当某个页面在等待网络请求或元素加载时,事件循环可以去执行其他页面的操作。

如何选择?

  • 新手或简单脚本:从同步API开始,逻辑直观。
  • 高性能爬虫或需要同时控制大量页面的测试务必使用异步API。在我的一个监控项目中,需要同时打开50个页面检查状态。使用同步API耗时约120秒,而改用异步API后,时间缩短到15秒以内,因为所有页面的网络等待时间被重叠利用了。
  • 与异步Web框架(如FastAPI)集成:自然选择异步API。

一个常见的误区是认为异步编程复杂。对于Playwright来说,你只需要记住给方法和with语句前加上async/await,其编程模型和同步API几乎一致,学习成本很低,但收益巨大。

3. 环境搭建与快速上手实战

3.1 一步到位的安装与配置

Playwright的安装可能是所有自动化工具里最省心的。它采用“电池包含”理念,浏览器驱动和浏览器本身都帮你管理好了。

# 1. 安装Playwright Python库 pip install playwright # 2. 安装所需的浏览器二进制文件(Chromium, Firefox, WebKit) playwright install

playwright install这个命令是关键。它会自动下载Chromium、Firefox和WebKit(Safari内核)的预备版本到你的用户目录下(Windows在%USERPROFILE%\AppData\Local\ms-playwright)。这意味着你不需要再去单独下载ChromeDriver或geckodriver,也无需手动配置PATH。所有浏览器版本都由Playwright团队测试并锁定,保证了API的兼容性和稳定性。

如果你只需要特定浏览器,可以指定安装:

playwright install chromium # 只安装Chromium(Chrome/Edge内核) playwright install firefox # 只安装Firefox playwright install webkit # 只安装WebKit

注意事项与避坑指南:

  • 网络问题:首次安装浏览器可能需要从国外CDN下载几百MB的文件。如果遇到网络超时,可以尝试设置环境变量PLAYWRIGHT_DOWNLOAD_HOST为国内镜像源(如果有),或者使用代理。但请注意,必须通过系统或网络层面的合法方式进行,Playwright安装命令本身不提供代理参数。
  • 系统权限:在Linux或macOS上,可能需要sudo权限来安装一些系统依赖(如libenchant用于字体)。根据终端提示操作即可。
  • IDE配置:我强烈推荐使用VS Code,并安装官方的“Playwright Test for VSCode”扩展。它能提供代码自动补全、测试运行和调试功能,体验极佳。

3.2 你的第一个自动化脚本:从录用到编码

Playwright提供了一个强大的工具——Codegen(代码生成器)。对于初学者或快速探索一个网站的操作流来说,这是神器。

打开命令行,运行:

playwright codegen https://www.baidu.com

这会自动打开一个浏览器窗口和一个“Playwright Inspector”侧边栏。你在浏览器里的所有点击、输入操作,都会实时生成对应的Python代码(默认是同步API)。你可以直接复制这些代码到你的脚本中使用。

但作为一名有经验的开发者,我们不能只停留在“录制”。让我们手写一个完整的、带错误处理的搜索例子:

from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeoutError def baidu_search(keyword: str): """ 一个健壮的百度搜索示例 """ with sync_playwright() as p: # 启动浏览器,headless=False表示显示界面,方便调试 browser = p.chromium.launch(headless=False, slow_mo=1000) # slow_mo让操作变慢,方便观察 # 创建一个浏览器上下文(Context) context = browser.new_context( viewport={'width': 1920, 'height': 1080}, # 设置视口大小 locale='zh-CN' # 设置语言环境为中国 ) # 在上下文中创建一个新页面 page = context.new_page() try: # 导航到百度 page.goto('https://www.baidu.com') # 等待页面加载完成,直到搜索框出现 page.wait_for_selector('input#kw', state='visible') # 输入搜索关键词 page.fill('input#kw', keyword) # 点击“百度一下”按钮 # 这里使用了更精确的定位:通过CSS选择器 + 文本内容 page.click('input[type="submit"][value="百度一下"]') # 等待搜索结果页面加载,这里等待第一个搜索结果标题出现 page.wait_for_selector('div.result h3 a', state='attached', timeout=10000) # 获取第一个搜索结果的标题和链接 first_result = page.query_selector('div.result h3 a') if first_result: title = first_result.text_content() href = first_result.get_attribute('href') print(f"第一个搜索结果: {title}") print(f"链接: {href}") # 可以点击进入第一个结果 # with page.expect_navigation(): # 等待新页面导航完成 # first_result.click() else: print("未找到搜索结果。") except PlaywrightTimeoutError: print("操作超时,可能页面元素未加载或选择器不正确。") # 可以在这里截图,方便事后分析 page.screenshot(path='timeout_error.png') except Exception as e: print(f"发生未知错误: {e}") finally: # 无论如何,最后都要关闭浏览器,释放资源 context.close() browser.close() if __name__ == "__main__": baidu_search("Playwright Python自动化测试")

实操心得:

  1. wait_for_selector是关键:不要假设页面瞬间加载完。在关键操作(如点击、输入)前,使用wait_for_selector等待目标元素变为可用状态(visible,attached,hidden等)。这是编写稳定脚本的第一原则。
  2. 善用slow_mo:在调试阶段,将launch参数中的slow_mo设置为几百毫秒,可以让你看清每一步自动化操作,非常有用。
  3. 选择器策略:优先使用id># 多种定位方式示例 page.locator('button').click() # 匹配第一个button元素 page.locator('text=登录').click() # 通过文本内容定位 page.locator('#username').fill('admin') # 通过ID定位 page.locator('.submit-btn').click() # 通过类名定位 page.locator('input[name="email"]').fill('test@example.com') # 通过属性定位 page.locator('div:has-text("Welcome")').click() # 使用:has-text()伪类 # 组合定位与链式调用 page.locator('nav').locator('a').filter(has_text='Home').click() # 获取多个元素 all_links = page.locator('a').all() for link in all_links: print(link.text_content()) # 等待元素状态变化 page.locator('#loading-spinner').wait_for(state='hidden')

    为什么Locator比直接使用page.click(selector)更好?Locator对象是惰性求值的,并且支持链式调用和复用。更重要的是,它内部包含了自动等待机制。当你调用locator.click()时,Playwright会自动等待该元素变为可点击状态(可见、未禁用、在视口内),然后再执行点击。这大大减少了需要手动编写等待语句的情况。

    4. 高级特性与实战应用场景

    4.1 网络拦截与模拟:测试边缘情况的利器

    现代Web应用高度依赖API。Playwright允许你拦截和修改任何网络请求,这对于测试以下场景不可或缺:

    • 模拟API失败:测试前端在API返回404或500错误时的降级处理。
    • Mock数据:在后端接口未准备好时,前端可以先进行自动化测试。
    • 性能测试:模拟慢速网络(3G),测试页面加载性能。
    • 阻止不必要的资源加载:如图片、广告,加速测试执行。
    async def intercept_requests(): async with async_playwright() as p: browser = await p.chromium.launch() context = await browser.new_context() page = await context.new_page() # 路由(拦截)所有请求 await page.route('**/*', lambda route: handle_route(route)) await page.goto('https://example.com') await browser.close() async def handle_route(route): request = route.request # 1. 拦截特定API请求并返回Mock数据 if 'api/user' in request.url: await route.fulfill( status=200, content_type='application/json', body=json.dumps({'name': 'Mock User', 'id': 123}) ) # 2. 阻止图片加载,加速测试 elif request.resource_type == 'image': await route.abort() # 3. 修改请求头 elif 'special-header' in request.url: headers = request.headers headers['x-custom-token'] = 'my-token' await route.continue_(headers=headers) # 4. 其他请求正常继续 else: await route.continue_()

    4.2 处理复杂页面结构:iframe、弹窗与新窗口

    处理iframe和弹窗是传统自动化测试的噩梦。Playwright让这一切变得简单。

    # 处理iframe page.frame_locator('iframe[name="content"]').locator('button.submit').click() # 处理弹窗(alert, confirm, prompt) page.on('dialog', lambda dialog: dialog.accept()) # 自动接受所有弹窗 # 或者更精细的控制 def handle_dialog(dialog): if dialog.type == 'alert': print(dialog.message) dialog.accept() elif dialog.type == 'confirm': dialog.dismiss() # 点击取消 page.on('dialog', handle_dialog) # 处理新窗口/标签页 # 方法1:监听‘popup’事件(由target=_blank的链接触发) async with page.expect_popup() as popup_info: page.click('a[target="_blank"]') new_page = await popup_info.value await new_page.wait_for_load_state() print(await new_page.title()) # 方法2:获取所有打开的页面 all_pages = context.pages second_page = all_pages[1]

    4.3 文件上传与下载

    Playwright处理文件上传不再需要找隐藏的<input type="file">元素然后send_keys,它提供了更符合用户直觉的方式。

    # 文件上传 - 推荐方式:直接设置文件输入 page.locator('input[type="file"]').set_input_files('/path/to/myfile.pdf') # 上传多个文件 page.locator('input[type="file"]').set_input_files(['file1.pdf', 'file2.jpg']) # 清空已选文件 page.locator('input[type="file"]').set_input_files([]) # 文件下载 # 首先,需要在创建context时启用下载 context = await browser.new_context(accept_downloads=True) page = await context.new_page() # 监听下载事件 async with page.expect_download() as download_info: page.click('a#download-link') # 触发下载的链接 download = await download_info.value # 等待下载完成并保存到指定路径 save_path = f'./downloads/{download.suggested_filename}' await download.save_as(save_path) print(f'文件已下载到: {save_path}')

    4.4 设备模拟与移动端测试

    Playwright内置了数十种主流移动设备(如iPhone, iPad, Pixel)的配置,可以一键模拟其视口、User-Agent、设备比例等,非常适合响应式测试。

    from playwright.sync_api import sync_playwright def test_mobile_view(): with sync_playwright() as p: # 方法1:使用内置设备模拟 iphone = p.devices['iPhone 12 Pro'] browser = p.chromium.launch(headless=False) # 创建上下文时传入设备参数 context = browser.new_context(**iphone) page = context.new_page() page.goto('https://m.example.com') # 此时页面看到的就是iPhone 12 Pro的模拟效果 # 方法2:手动指定视口和User-Agent context2 = browser.new_context( viewport={'width': 390, 'height': 844}, user_agent='Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) ...', device_scale_factor=3, # 视网膜屏缩放 is_mobile=True, has_touch=True # 启用触摸事件 ) page2 = context2.new_page() # 模拟触摸滑动 page2.touchscreen.tap(100, 200)

    5. 集成到专业测试框架:Pytest实战

    单独使用Playwright脚本可以做自动化,但要构建可维护、可报告、可并行的测试套件,必须集成到专业的测试框架中。Pytest是Python生态的不二之选。

    5.1 基础集成与Fixture使用

    Pytest的Fixture机制非常适合管理Playwright昂贵的资源(如Browser实例)。

    # conftest.py - 放在项目根目录或测试目录下 import pytest from playwright.sync_api import Page, BrowserContext, BrowserType import playwright.sync_api as p @pytest.fixture(scope='session') # 整个测试会话只启动一次浏览器 def browser(): # 启动浏览器,可以在这里配置全局参数,如headless模式 with p.sync_playwright() as playwright: browser = playwright.chromium.launch(headless=True) # CI环境通常用无头模式 yield browser browser.close() @pytest.fixture(scope='function') # 每个测试函数一个干净的上下文 def context(browser): context = browser.new_context( viewport={'width': 1920, 'height': 1080}, locale='zh-CN', # 可以在这里注入初始状态,如登录cookie # storage_state='auth.json' ) yield context context.close() @pytest.fixture(scope='function') def page(context): page = context.new_page() yield page page.close() # test_example.py def test_baidu_search(page: Page): page.goto('https://www.baidu.com') assert '百度' in page.title() page.fill('#kw', 'Playwright') page.click('#su') # 使用Playwright内置的断言,更强大 expect(page).to_have_title(re.compile(r'.*Playwright.*'))

    5.2 使用Pytest插件实现高级功能

    社区有优秀的pytest-playwright插件,它封装了上述Fixture,并提供了更多便利功能。

    pip install pytest-playwright
    # 使用插件提供的Fixture,它自动管理了browser, context, page def test_with_plugin(page): page.goto('https://example.com') # ... # 插件还提供了`is_chromium`, `is_firefox`, `is_webkit`等Fixture,用于编写特定浏览器的测试 def test_only_chrome(page, is_chromium): if not is_chromium: pytest.skip('此测试仅适用于Chromium浏览器') # ... Chrome-specific test

    5.3 生成漂亮的测试报告:集成Allure

    Allure报告能直观展示测试步骤、截图和错误信息,是团队协作的利器。

    pip install allure-pytest
    # conftest.py 中配置自动截图 @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 Fixture在测试中可用 if "page" in item.fixturenames: page = item.funcargs["page"] # 将截图附加到Allure报告 allure.attach( page.screenshot(full_page=True, type='png'), name=f"screenshot_{item.name}", attachment_type=allure.attachment_type.PNG ) # 也可以附加页面源代码 allure.attach( page.content(), name=f"page_source_{item.name}", attachment_type=allure.attachment_type.HTML ) # 运行测试并生成报告 # pytest --alluredir=./allure-results ./tests # allure serve ./allure-results # 本地查看报告

    6. 常见问题排查与性能优化实录

    6.1 元素定位失败:稳定性提升技巧

    这是自动化测试中最常见的问题。除了基本的等待,还有以下高级技巧:

    1. 使用page.wait_for_function等待复杂条件

      # 等待直到某个元素包含特定文本 await page.wait_for_function(""" () => { const el = document.querySelector('.status'); return el && el.textContent.includes('加载完成'); } """)
    2. 自定义重试逻辑

      from tenacity import retry, stop_after_attempt, wait_exponential @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10)) def click_with_retry(page, selector): """带有指数退避重试的点击函数""" page.click(selector) # 使用 click_with_retry(page, 'button.submit')
    3. 应对动态ID和类名:使用XPath的contains函数或CSS属性选择器。

      # 不好的选择器:id是动态生成的 # page.click('#button-123456') # 好的选择器:使用稳定的属性部分 page.click('[id^="button-"]') # id以"button-"开头 page.click('[data-testid="submit-btn"]') # 最好让开发加上固定的data-testid page.click('button:has-text("提交")') # 结合文本

    6.2 异步操作与竞态条件

    在异步API中,如果不注意操作顺序,很容易产生竞态条件。

    # 错误示例:点击后立即断言,此时导航可能还未开始 await page.click('a#next-page') await expect(page).to_have_url('/page2') # 可能失败 # 正确示例:使用 `page.wait_for_navigation` 或 `page.expect_navigation` async with page.expect_navigation(): await page.click('a#next-page') # 此时导航已完成,可以安全断言 await expect(page).to_have_url('/page2') # 对于非导航的异步操作(如API调用后的UI更新),使用等待选择器 await page.click('button#fetch-data') await page.wait_for_selector('.data-loaded', state='visible')

    6.3 性能优化:让测试跑得更快

    1. 复用Browser实例:如前所述,启动Browser开销大。使用Pytest的session作用域Fixture,让所有测试共享一个Browser实例,但各自使用独立的Context。
    2. 并行执行:Pytest可以通过pytest-xdist插件实现并行。
      pip install pytest-xdist pytest -n auto ./tests # 自动根据CPU核心数并行
      确保你的测试用例之间是独立的(通过独立的Context隔离),这是并行成功的前提。
    3. 禁用不必要的功能:在CI环境中,可以关闭一些功能加速。
      browser = playwright.chromium.launch( headless=True, args=[ '--disable-gpu', '--disable-dev-shm-usage', # 解决Docker中共享内存问题 '--disable-setuid-sandbox', '--no-sandbox' ] ) context = browser.new_context( java_script_enabled=True, # 默认True,如果测试静态页可设为False ignore_https_errors=True, # 忽略HTTPS证书错误(测试环境) bypass_csp=True # 绕过内容安全策略(谨慎使用) )
    4. 智能等待,避免sleep:绝对不要使用time.sleep(10)。使用Playwright提供的条件等待(wait_for_selector,wait_for_function,expect)。

    6.4 在CI/CD流水线中运行

    在GitHub Actions、GitLab CI或Jenkins中运行Playwright测试,需要注意环境配置。

    # .github/workflows/playwright.yml 示例 (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 --with-deps chromium # 只安装Chromium及其系统依赖 - name: Run tests run: | pytest --browser chromium --headless ./tests - name: Upload test results (if failed) if: failure() uses: actions/upload-artifact@v3 with: name: playwright-screenshots path: ./test-results/ # 你的测试截图输出目录

    关键点:在CI中,使用--headless模式,并使用playwright install --with-deps确保安装所有必要的系统库(如字体库)。对于Linux Docker镜像,可能需要额外安装一些依赖。

    7. 超越测试:Playwright的其他应用场景

    Playwright的能力不止于测试。它的稳定性和强大的浏览器控制能力,使其在其他领域也大放异彩。

    7.1 网页截图与PDF生成

    生成网页快照或PDF,用于监控、归档或报告。

    async def generate_assets(url): async with async_playwright() as p: browser = await p.chromium.launch() context = await browser.new_context(viewport={'width': 1280, 'height': 800}) page = await context.new_page() await page.goto(url) await page.wait_for_load_state('networkidle') # 等待网络空闲 # 1. 截图(可视区域) await page.screenshot(path='screenshot.png') # 2. 全屏截图 await page.screenshot(path='fullpage.png', full_page=True) # 3. 对特定元素截图 element = page.locator('.chart') await element.screenshot(path='chart.png') # 4. 生成PDF(支持页眉页脚) await page.pdf( path='report.pdf', format='A4', print_background=True, display_header_footer=True, header_template='<div style="font-size:10px; margin-left:20px;">Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>', margin={'top': '1cm', 'bottom': '1cm'} ) await browser.close()

    7.2 端到端监控与健康检查

    编写脚本定期检查关键业务流程是否畅通。

    import asyncio import time from playwright.async_api import async_playwright, TimeoutError SITES_TO_CHECK = [ {'name': '首页', 'url': 'https://myapp.com', 'check_selector': '.hero-section'}, {'name': '登录页', 'url': 'https://myapp.com/login', 'check_selector': 'form#login'}, {'name': 'API状态', 'url': 'https://api.myapp.com/health', 'check_text': '{"status":"ok"}'}, ] async def check_site(site): async with async_playwright() as p: browser = await p.chromium.launch(headless=True) context = await browser.new_context() page = await context.new_page() try: # 设置全局超时 page.set_default_timeout(30000) response = await page.goto(site['url'], wait_until='networkidle') if not response or not response.ok: return {'site': site['name'], 'status': 'ERROR', 'detail': f'HTTP {response.status if response else "No Response"}'} if 'check_selector' in site: await page.wait_for_selector(site['check_selector'], timeout=10000) status = 'OK' elif 'check_text' in site: content = await page.text_content('body') if site['check_text'] in content: status = 'OK' else: status = 'ERROR' detail = 'Expected text not found' else: status = 'OK' return {'site': site['name'], 'status': status, 'detail': 'Check passed'} except TimeoutError: return {'site': site['name'], 'status': 'TIMEOUT', 'detail': 'Page load or element check timeout'} except Exception as e: return {'site': site['name'], 'status': 'ERROR', 'detail': str(e)} finally: await browser.close() async def main(): tasks = [check_site(site) for site in SITES_TO_CHECK] results = await asyncio.gather(*tasks, return_exceptions=True) for result in results: print(f"{result['site']}: {result['status']} - {result.get('detail', '')}") if result['status'] != 'OK': # 发送告警:邮件、Slack、钉钉等 send_alert(result) # 用 cron 或 Celery 定时执行这个脚本

    7.3 自动化数据抓取(需谨慎合法使用)

    对于需要JavaScript渲染的动态网页,Playwright是比Requests+BeautifulSoup更强大的工具。

    async def scrape_dynamic_content(): async with async_playwright() as p: browser = await p.chromium.launch(headless=True) context = await browser.new_context( user_agent='Mozilla/5.0 ...' # 伪装成普通浏览器 ) page = await context.new_page() await page.goto('https://example.com/data-table') # 等待表格数据通过JS加载 await page.wait_for_selector('table tbody tr') # 提取数据 rows = await page.locator('table tbody tr').all() data = [] for row in rows: cells = await row.locator('td').all_text_contents() data.append({ 'name': cells[0], 'value': cells[1], 'date': cells[2] }) # 处理分页 while await page.locator('button.next-page:not([disabled])').is_visible(): await page.click('button.next-page') await page.wait_for_load_state('networkidle') # ... 继续提取新页面的数据 await browser.close() return data

    重要提醒:用于数据抓取时,务必遵守网站的robots.txt协议,尊重版权,控制请求频率,避免对目标服务器造成压力。商业用途需获得授权。

    从我的实际经验来看,Playwright最大的价值在于它统一了开发、测试和运维的浏览器自动化需求。开发者可以用它做E2E测试,测试工程师用它构建稳定的自动化套件,运维可以用它做监控。它学习曲线平缓,但天花板很高。当你熟悉了它的核心概念(Browser, Context, Page)和异步模式后,你会发现很多以前棘手的Web自动化问题,现在都有了优雅的解决方案。它可能不是所有场景下的银弹,但对于现代Web应用的自动化而言,它无疑是当前最强大、最值得投入学习的工具之一。

← 返回列表