1. 项目概述为什么是Playwright如果你正在寻找一个能横跨现代浏览器、功能强大且对开发者友好的Web自动化工具那么Playwright绝对值得你投入时间。它不是一个简单的“又一个”自动化库而是一个由微软团队开发的、旨在解决端到端测试和Web爬取中诸多痛点的现代框架。我最初接触它是因为厌倦了Selenium在复杂SPA单页应用和跨浏览器场景下的不稳定性以及Puppeteer在非Chrome系浏览器上的局限性。Playwright的出现像是一股清流它原生支持Chromium、Firefox和WebKit三大浏览器引擎这意味着你写的同一套脚本几乎无需修改就能在Chrome、Edge、Firefox和Safari上稳定运行。对于Python开发者而言Playwright的Python绑定做得相当出色API设计非常符合Python的“优雅”哲学上手门槛比想象中低。无论是你想做UI自动化测试、构建一个健壮的爬虫来应对反爬策略还是实现一些重复性的网页操作比如自动填表、批量下载Playwright都能提供一套统一的、高性能的解决方案。它内置了等待、网络拦截、文件上传下载、模拟移动设备等高级功能很多在Selenium里需要大量额外代码才能实现的效果在Playwright里可能就是一行API调用的事。2. 核心需求解析从安装到高效使用的完整链路在开始敲代码之前我们需要明确一个高效的Playwright工作流应该包含哪些环节。很多教程只讲“如何安装”但一个真正可用的环境远不止pip install那么简单。根据我的经验一个完整的链路至少包括环境隔离与依赖管理、核心库与浏览器驱动的安装、开发环境与调试工具的配置以及理解其异步/同步双模式。跳过任何一环都可能在未来某个时刻让你掉进坑里。比如直接在你的全局Python环境里安装Playwright可能会与其他项目的依赖产生冲突。再比如如果你不知道Playwright默认会下载浏览器到用户目录那么在Docker容器或无头服务器上部署时就可能遇到路径问题。高效使用的起点恰恰是这些看似基础的准备工作。本章节我们就来逐一拆解这些核心需求为你搭建一个坚实、可复现的Playwright工作环境。2.1 环境隔离为Playwright创建专属沙箱我强烈建议你为每个使用Playwright的项目创建一个独立的虚拟环境。这不仅能避免依赖地狱也方便你后续进行项目迁移或与他人协作。这里我推荐使用Python内置的venv模块它简单可靠无需额外安装。首先为你的项目创建一个新目录并进入mkdir my_playwright_project cd my_playwright_project然后使用Python命令创建虚拟环境。这里的.venv是虚拟环境文件夹的名字你可以自定义。# 在Windows上 python -m venv .venv # 在macOS/Linux上 python3 -m venv .venv创建完成后需要激活这个环境Windows (PowerShell):.venv\Scripts\Activate.ps1Windows (CMD):.venv\Scripts\activate.batmacOS/Linux:source .venv/bin/activate激活后你的命令行提示符前通常会显示(.venv)表示你已进入该虚拟环境后续的所有pip安装操作都只会影响这个环境。注意如果你使用PyCharm或VSCode它们都提供了图形化界面来创建和管理虚拟环境。在PyCharm中创建新项目时直接选择“New environment using Virtualenv”在VSCode中可以通过命令面板CtrlShiftP选择“Python: Select Interpreter”来指向你刚创建的.venv目录下的Python解释器。这样做的好处是你的IDE会自动识别环境内的包提供准确的代码补全和调试支持。2.2 安装Playwright Python库环境激活后安装Playwright的Python库就非常简单了。使用pip命令即可。我建议使用官方源以确保获得最新且兼容的版本。pip install playwright这条命令会安装playwright这个核心Python包它包含了控制浏览器所需的所有客户端API。安装完成后你可以通过以下命令验证安装是否成功并查看版本python -c “import playwright; print(playwright.__version__)”2.3 安装浏览器驱动Playwright CLI的妙用这是Playwright设计上非常聪明的一点。它的Python库本身不包含浏览器浏览器引擎Chromium, Firefox, WebKit是作为独立二进制文件通过一个名为playwright的命令行工具来管理的。这保证了浏览器版本的纯净和一致性。安装完Python库后你需要运行以下命令来下载所需的浏览器playwright install这条命令会默认下载Chromium、Firefox和WebKit三大浏览器的最新稳定版本到你的用户目录下例如在Windows上是%USERPROFILE%\AppData\Local\ms-playwright。第一次运行可能会花费一些时间因为它需要下载几百MB的文件。实操心得playwright install命令其实非常灵活。指定浏览器如果你只需要Chromium可以运行playwright install chromium节省时间和磁盘空间。指定版本你可以安装特定版本的浏览器例如playwright install chromiumlatest-stable或playwright install firefox1050这对于需要固定测试环境版本的项目至关重要。换源加速如果下载速度很慢尤其是WebKit可以通过设置环境变量来使用国内镜像源加速。在运行安装命令前先执行# Windows (PowerShell) $env:PLAYWRIGHT_DOWNLOAD_HOST “https://npmmirror.com/mirrors/playwright” # macOS/Linux export PLAYWRIGHT_DOWNLOAD_HOST“https://npmmirror.com/mirrors/playwright”然后再运行playwright install速度会有显著提升。2.4 同步与异步API根据场景做选择Playwright的Python API提供了两套风格同步和异步。这是你开始编码前必须理解的关键选择。同步API (sync_playwright)代码顺序执行更符合传统脚本的编写习惯易于理解和调试。适合大多数自动化脚本、初学者或逻辑线性的任务。from playwright.sync_api import sync_playwright with sync_playwright() as p: browser p.chromium.launch(headlessFalse) # 启动有头浏览器方便观察 page browser.new_page() page.goto(“https://www.example.com”) print(page.title()) browser.close()异步API (async_playwright)基于asyncio可以高效处理多个页面并发操作、等待多个网络事件等I/O密集型任务性能更高。适合构建复杂的爬虫或需要高并发的测试套件。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://www.example.com”) print(await page.title()) await browser.close() asyncio.run(main())如何选择我的建议是如果你是新手或者项目不涉及复杂的并发先从同步API开始它的心智负担更小。当你需要同时控制多个浏览器标签页、并行处理大量页面时再考虑迁移到异步API。两者的核心概念如Page,Locator是完全一致的学会一个另一个很容易上手。3. 核心细节解析与高效使用要点安装只是第一步真正体现Playwright威力的是其丰富而强大的API。很多初学者在简单录制几个操作后便止步不前但要想高效使用必须深入理解几个核心概念和设计模式。这一章我们就来剖析那些让Playwright从“能用”到“好用”的关键细节。3.1 启动与上下文管理不仅仅是打开浏览器启动浏览器时launch()方法有一系列参数可以精细控制浏览器行为合理的配置能极大提升脚本的稳定性和性能。from playwright.sync_api import sync_playwright with sync_playwright() as p: # 一个更贴近真实使用的启动示例 browser p.chromium.launch( headlessFalse, # 调试时设为False查看浏览器操作 slow_mo500, # 将每个操作放慢500毫秒便于观察 args[‘--disable-blink-featuresAutomationControlled’] # 禁用自动化控制标志有助于绕过一些简单的反爬检测 ) # ... 后续操作但更重要的概念是Browser Context。你可以把它理解为一个独立的“隐身会话”。每个Context拥有独立的cookie、本地存储和缓存但共享同一个浏览器进程。这非常有用多用户/多账户模拟在一个脚本中模拟多个用户同时登录同一网站。环境隔离确保测试或爬虫任务互不干扰。资源效率比启动多个浏览器实例更轻量。# 创建两个独立的上下文模拟两个用户 context1 browser.new_context() context2 browser.new_context() page1 context1.new_page() # 用户1的页面 page2 context2.new_page() # 用户2的页面 # 两个页面互不干扰 page1.goto(“https://example.com/login”) page2.goto(“https://example.com/login”) # 可以分别用不同的账号登录3.2 元素定位器告别脆弱的XPathPlaywright极力推荐使用其内置的Locator API来定位元素这是它比Selenium等工具更稳定的核心原因之一。Locator是惰性求值的并且内置了自动等待和重试机制。最佳实践是使用page.get_by_*系列方法# 通过角色定位最语义化的方式推荐 page.get_by_role(“button”, name“Submit”).click() # 通过文本定位 page.get_by_text(“Welcome Back”).click() # 通过Label文本定位关联的表单元素 page.get_by_label(“Username”).fill(“myuser”) # 通过Placeholder定位 page.get_by_placeholder(“youremail.com”).fill(“email”) # 通过Title属性定位 page.get_by_title(“Tooltip info”).hover()为什么这比直接写XPath/CSS Selector好可读性更强get_by_role(“button”, name“Submit”)一眼就知道要找的是一个名为“Submit”的按钮。稳定性更高即使前端代码的CSS类名或DOM结构微调只要按钮的角色和可访问名称不变定位器就依然有效。而像//div[class‘btn-primary’]这样的XPath非常脆弱。内置等待locator.click()会先自动等待该元素变得可交互可见、启用、稳定然后再执行点击省去了你手动添加time.sleep或显式等待的代码。当然对于复杂或特殊的元素你仍然可以回退到CSS或XPathpage.locator(“cssbutton.special”).click() page.locator(“xpath//button[contains(class, ‘special’)]”).click() # 或者更简洁的写法Playwright会自动推断 page.locator(“button.special”).click()3.3 自动等待让脚本健壮的核心Playwright的绝大多数操作如click,fill,goto都内置了智能等待。它会等待一系列相关条件达成后才执行例如导航page.goto(url)等待页面触发load事件。点击locator.click()等待元素变得可交互可见、启用、稳定。填充locator.fill()等待元素可编辑。你通常不需要写显式的sleep。但有时你需要等待特定条件这时可以使用page.wait_for_*方法或更通用的expect断言。# 等待页面导航到一个新URL page.wait_for_url(“**/dashboard”) # 等待某个元素出现 page.wait_for_selector(“text‘操作成功’”, state“visible”) # 使用expect断言进行等待更现代的方式 from playwright.sync_api import expect expect(page.locator(“text‘操作成功’“)).to_be_visible()注意事项虽然内置等待很强大但在网络极慢或前端框架如React, Vue动态渲染特别复杂时默认超时30秒可能不够。你可以通过timeout参数调整单个操作的超时或者修改全局默认超时# 设置单个操作的超时 page.click(“button”, timeout10000) # 10秒 # 设置页面级别的默认超时 page.set_default_timeout(60000) # 60秒 # 设置浏览器上下文级别的默认超时 context.set_default_timeout(60000)3.4 处理弹窗、框架与多页面现代Web应用充满了弹窗、iframe和多标签页Playwright处理这些场景非常优雅。处理弹窗对话框Playwright会自动监听并“接受”或“驳回”JavaScript触发的对话框alert,confirm,prompt。你需要在实际操作触发弹窗前设置监听器。# 在点击可能触发confirm对话框的按钮之前 page.on(“dialog”, lambda dialog: dialog.accept()) # 自动接受所有对话框 # 或者更精细的控制 def handle_dialog(dialog): print(f“对话框消息: {dialog.message}”) if dialog.type “confirm”: dialog.accept() # 点击“确定” else: dialog.dismiss() # 点击“取消” page.on(“dialog”, handle_dialog) page.click(“button#delete”) # 点击删除按钮触发确认框处理iframe要操作iframe内的元素必须先获取到该iframe的Frame对象。# 通过iframe的name属性或URL定位 iframe page.frame(name“chat-widget”) # 或者 page.frame(url“**/widget.html”) # 然后在iframe的上下文中定位元素 iframe.locator(“input”).fill(“Hello from iframe”)处理多标签页# 点击一个会打开新标签页的链接通常带有 target“_blank” with page.expect_popup() as popup_info: page.click(“a[target‘_blank’]”) new_page popup_info.value # 获取新页面的Page对象 # 现在可以操作新页面了 new_page.bring_to_front() # 切换到新标签页 print(new_page.title())4. 实操过程与核心环节实现理论讲得再多不如动手实践。这一章我们将通过一个完整的、贴近实际需求的案例串联起Playwright的核心功能。假设我们需要完成一个任务自动登录一个示例网站在仪表盘中找到特定数据并导出为CSV文件。这个任务涵盖了导航、表单填写、等待、元素定位、文件下载等多个核心环节。4.1 案例目标与脚本骨架我们的目标网站假设为https://demo.testfire.net(一个经典的银行演示网站常用于测试)。任务分解如下导航到登录页面。填写用户名和密码并登录。等待登录成功导航到账户概览页。定位交易记录表格。模拟点击“导出为CSV”按钮。处理文件下载并保存到指定位置。首先搭建脚本的同步API骨架from playwright.sync_api import sync_playwright, expect import time def run(): with sync_playwright() as p: # 1. 启动浏览器 browser p.chromium.launch(headlessFalse, slow_mo1000) # 慢速模式方便观察 context browser.new_context() page context.new_page() try: # 2. 执行核心步骤 login_to_site(page) navigate_and_locate_data(page) download_export_file(page, context) print(“任务执行成功”) except Exception as e: print(f“执行过程中出现错误: {e}”) finally: # 3. 清理资源 time.sleep(5) # 最后观察一下页面状态 browser.close() if __name__ “__main__”: run()4.2 实现登录与导航接下来我们实现login_to_site函数。这里的关键是稳健的元素定位和表单交互。def login_to_site(page): “”“登录到示例网站”“” # 导航到登录页 page.goto(“https://demo.testfire.net/login.jsp”) # 使用get_by_*定位器填写表单。优先使用最稳定的属性。 # 查看网页源码发现用户名输入框有 name‘uid’ username_input page.get_by_label(“Username”) # 优先尝试通过Label定位 if not username_input.count(): # 如果找不到label回退到name username_input page.locator(“input[name‘uid’]”) username_input.fill(“admin”) # 密码输入框 password_input page.get_by_label(“Password”) if not password_input.count(): password_input page.locator(“input[name‘passw’]”) password_input.fill(“admin”) # 定位并点击登录按钮。优先使用Role定位。 login_button page.get_by_role(“button”, name“Login”) if not login_button.count(): # 如果按钮没有明确的role和name使用其他属性 login_button page.locator(“input[name‘btnSubmit’]”) # 该网站登录按钮是input类型 login_button.click() # 等待登录成功。通常可以通过URL变化或页面出现特定元素来判断。 # 方案一等待URL包含特定路径 page.wait_for_url(“**/bank/main.jsp”) # 方案二等待登录后特有的元素出现更可靠 # expect(page.locator(“text‘Welcome’“)).to_be_visible() print(“登录成功”)实操心得在实际项目中登录往往是脚本最脆弱的环节。除了上述定位策略还有几个技巧处理验证码如果遇到简单验证码可以考虑使用OCR库如pytesseract进行识别但成功率有限。对于复杂验证码可能需要寻求绕过方案或使用商业打码服务但这已超出自动化工具本身的范围。使用Cookie持久化会话对于需要频繁登录的网站可以在首次成功登录后保存Cookie下次启动时直接加载避免重复登录。# 登录成功后保存上下文状态包含Cookie context.storage_state(path“auth_state.json”) # 下次启动时用保存的状态创建上下文 context browser.new_context(storage_state“auth_state.json”) page context.new_page() page.goto(“https://example.com/dashboard”) # 可能已处于登录状态4.3 定位数据与触发导出登录后我们进入仪表盘假设我们需要导出最近的交易记录。def navigate_and_locate_data(page): “”“导航到数据页面并定位表格”“” # 假设网站上有一个“View Transactions”的链接 # 使用精确文本匹配避免点到其他同名链接 page.locator(“a”, has_text“View Transactions”).click() # 等待交易表格加载出来。这里假设表格有一个id或者独特的标题。 # 使用expect断言进行等待代码更清晰。 from playwright.sync_api import expect transactions_table page.locator(“table#transactionsTable”) expect(transactions_table).to_be_visible() # 我们可以先读取一些数据用于验证 # 获取表格第一行第一列的内容示例 first_cell transactions_table.locator(“tbody tr:nth-child(1) td:nth-child(1)”).text_content() print(f“第一笔交易描述: {first_cell}”) # 定位导出按钮并点击。假设按钮的文本是“Export CSV” export_button page.get_by_role(“button”, name“Export CSV”) # 在点击前先设置好文件下载的监听和处理见下一节4.4 处理文件下载Playwright处理文件下载非常方便。我们需要在点击下载按钮之前启动下载监听。def download_export_file(page, context): “”“处理文件下载并保存到本地”“” import os # 在点击下载按钮前设置下载行为。 # 默认情况下文件会下载到临时目录我们可以指定下载路径。 # 但更通用的方法是监听‘download’事件。 # 启动下载监听 with page.expect_download() as download_info: # 触发下载的操作点击导出按钮 page.get_by_role(“button”, name“Export CSV”).click() # 获取Download对象 download download_info.value # 建议等待下载完成 # download.path() 会返回一个临时文件路径但我们可以将其移动到指定位置 suggested_filename download.suggested_filename # 获取浏览器建议的文件名如“transactions_202310.csv” if not suggested_filename: suggested_filename “downloaded_file.csv” # 定义保存路径 save_path os.path.join(os.getcwd(), “downloads”, suggested_filename) os.makedirs(os.path.dirname(save_path), exist_okTrue) # 确保目录存在 # 将下载的文件保存到我们指定的路径 download.save_as(save_path) print(f“文件已下载并保存至: {save_path}”) # 可选读取CSV文件内容进行简单验证 # import pandas as pd # df pd.read_csv(save_path) # print(df.head())将以上所有函数整合到主函数run()的try块中一个完整的自动化脚本就完成了。这个脚本演示了从登录到下载的完整流程涵盖了定位、等待、交互、事件处理等核心操作。5. 常见问题与排查技巧实录即使按照最佳实践编写脚本在实际运行中你依然会遇到各种问题。这一章我汇总了这些年使用Playwright时踩过的坑和总结出的排查技巧希望能帮你快速定位问题。5.1 元素定位失败最常遇到的问题症状脚本报错TimeoutError: Timeout 30000ms exceeded.通常伴随waiting for selector “...”的信息。排查步骤手动验证选择器在浏览器的开发者工具F12的Console中使用$$(“你的选择器”)(CSS) 或$x(“你的XPath”)(XPath) 来测试你的选择器是否能找到元素。如果返回空数组说明选择器写错了。检查iframe目标元素是否在iframe里面如果是你必须先获取到对应的Frame对象再在里面定位。检查动态内容元素是否是JavaScript动态加载的你可能需要更长的等待时间或者等待一个更稳定的“祖先元素”出现后再定位其子元素。使用page.pause()进行交互式调试在脚本中插入page.pause()运行脚本时浏览器会暂停并打开Playwright Inspector。你可以查看当前的页面快照、测试选择器并单步执行命令。这是最强大的调试手段。尝试更稳健的定位器放弃脆弱的XPath改用page.get_by_role(),page.get_by_text(),page.get_by_label()。这些定位器基于可访问性树通常更稳定。查看页面状态在出错前打印页面内容print(page.content()[:2000])或截图page.screenshot(path“debug.png”)看看页面是否如你预期那样加载了。5.2 异步操作与等待问题症状脚本执行顺序错乱或者点击/输入没反应。解决方案牢记Playwright操作是异步的即使在同步API中click(),fill(),goto()内部也是异步操作它们会返回一个Promise在同步API中被自动处理。这意味着你不能假设一个操作完成后DOM会立刻更新。永远依赖Playwright的内置等待或显式使用expect。避免使用time.sleep()这是最后的手段。用page.wait_for_selector(),page.wait_for_function()或expect(locator).to_be_visible()代替。处理网络请求后的等待如果你触发了一个会发送网络请求的操作如表单提交然后需要等待新内容最好等待一个网络请求完成或特定URL。# 点击提交按钮后等待某个特定的API请求完成 with page.expect_response(“**/api/submit”) as response_info: page.click(“button#submit”) response response_info.value print(f“API响应状态: {response.status}”) # 或者等待导航到一个新页面 with page.expect_navigation(): page.click(“a#next-page”)5.3 浏览器启动与环境问题问题1playwright install下载速度极慢或失败。解决如前所述设置PLAYWRIGHT_DOWNLOAD_HOST环境变量使用国内镜像源。备选手动下载浏览器包。从Playwright的GitHub Releases页面找到对应的浏览器版本压缩包解压到Playwright的浏览器缓存目录可通过playwright install --dry-run查看路径。问题2在Docker或CI/CD环境中运行失败提示找不到浏览器。解决确保在Dockerfile中运行了playwright install且安装了必要的系统依赖。Playwright提供了官方Docker镜像mcr.microsoft.com/playwright/python它预装了所有依赖和浏览器是最省事的选择。FROM mcr.microsoft.com/playwright/python:v1.40.0-noble COPY . /app WORKDIR /app RUN pip install -r requirements.txt CMD [“python”, “your_script.py”]问题3在无头模式下运行但某些网站检测到自动化工具并屏蔽。解决尝试添加启动参数来隐藏自动化特征。browser p.chromium.launch( headlessTrue, args[ ‘--disable-blink-featuresAutomationControlled’, ‘--start-maximized’ # 有些网站会检查窗口尺寸 ] ) # 此外可以覆盖navigator.webdriver属性 context browser.new_context( viewport{‘width’: 1920, ‘height’: 1080}, user_agent‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...’ ) page context.new_page() # 注入JS覆盖webdriver属性注意此方法不一定总是有效 page.add_init_script(“”” Object.defineProperty(navigator, ‘webdriver’, { get: () undefined }); “””)5.4 性能优化与资源管理问题脚本运行一段时间后变慢或内存占用过高。策略1及时关闭页面和上下文不再使用的Page和Context对象调用.close()方法释放资源。page1.close() context1.close()策略2复用浏览器实例如果有多项独立任务尽量复用同一个Browser对象只创建新的Context这比反复启动/关闭浏览器快得多。策略3禁用不必要的资源加载如果不需要图片、样式、字体等来执行你的操作可以拦截它们以加速页面加载。def route_handler(route): # 阻止图片、样式表、字体等资源的加载 if route.request.resource_type in [“image”, “stylesheet”, “font”, “media”]: route.abort() else: route.continue_() context browser.new_context() page context.new_page() page.route(“**/*”, route_handler) # 应用路由规则策略4使用异步API处理高并发当需要同时处理数十上百个页面时同步API会成为瓶颈。此时应切换到异步API (async_playwright)并利用asyncio.gather()来并发执行任务可以极大提升吞吐量。6. 进阶技巧与模式推荐当你熟练掌握了基础操作后下面这些进阶技巧和设计模式能让你的Playwright脚本更加健壮、可维护和强大。6.1 使用POM模式组织大型项目对于复杂的自动化测试或爬虫项目强烈建议使用页面对象模型。POM将每个页面的定位器和操作封装成一个类使业务逻辑与页面细节分离。# base_page.py - 基础页面类 class BasePage: def __init__(self, page): self.page page self.timeout 5000 def navigate(self, url): self.page.goto(url) def get_title(self): return self.page.title() # login_page.py - 登录页面 from base_page import BasePage class LoginPage(BasePage): # 定位器作为类属性 USERNAME_INPUT “input[name‘uid’]” PASSWORD_INPUT “input[name‘passw’]” LOGIN_BUTTON “input[name‘btnSubmit’]” def __init__(self, page): super().__init__(page) def login(self, username, password): self.page.locator(self.USERNAME_INPUT).fill(username) self.page.locator(self.PASSWORD_INPUT).fill(password) self.page.locator(self.LOGIN_BUTTON).click() # 可以在这里添加等待登录成功的逻辑 self.page.wait_for_url(“**/main.jsp”) # main_script.py - 主脚本 from playwright.sync_api import sync_playwright from login_page import LoginPage with sync_playwright() as p: browser p.chromium.launch() page browser.new_page() login_page LoginPage(page) # 创建页面对象 login_page.navigate(“https://demo.testfire.net/login.jsp”) login_page.login(“admin”, “admin”) # ... 后续使用其他页面对象6.2 网络请求拦截与模拟Playwright可以监听和修改任何网络请求与响应这对于性能优化、模拟API数据或测试错误场景极其有用。# 拦截请求并修改 await page.route(“**/api/user”, lambda route: route.fulfill( status200, content_type“application/json”, bodyjson.dumps({“name”: “Mock User”, “id”: 123}) # 返回模拟数据 )) # 监听所有响应并检查特定请求 page.on(“response”, lambda response: if “/api/data” in response.url: print(f“响应状态: {response.status}”) # print(await response.json()) # 异步API中可读取响应体 ) # 阻塞某些请求如广告、追踪器 page.route(“**/*.{png,jpg,jpeg,svg}”, lambda route: route.abort())6.3 与测试框架集成Playwright本身自带一个强大的测试运行器playwright test但它的Python版本也完全可以与流行的测试框架如pytest无缝集成。# test_demo.py import pytest from playwright.sync_api import Page, expect pytest.fixture(scope“function”) def page(browser): # browser是另一个fixture负责启动浏览器 context browser.new_context() page context.new_page() yield page context.close() def test_login_success(page: Page): “”“测试成功登录”“” page.goto(“https://demo.testfire.net/login.jsp”) page.locator(“input[name‘uid’]”).fill(“admin”) page.locator(“input[name‘passw’]”).fill(“admin”) page.locator(“input[name‘btnSubmit’]”).click() expect(page).to_have_url(“**/bank/main.jsp”) expect(page.locator(“text‘Welcome’“)).to_be_visible()你可以使用pytest --headed在浏览器中观看测试执行或者pytest --browser chromium --browser firefox进行跨浏览器测试。结合pytest-xdist插件还可以实现并行测试大幅缩短测试套件的总运行时间。6.4 录制与代码生成快速原型工具对于完全陌生的网站手动编写所有定位器可能很耗时。Playwright提供了一个强大的Codegen工具可以录制你的浏览器操作并生成对应代码。启动命令playwright codegen https://demo.testfire.net这会打开一个浏览器和一个代码生成器窗口。你在浏览器中的所有操作点击、输入、导航都会实时转换成Python或其他语言代码。这是快速编写脚本初稿、学习API用法的绝佳方式。注意事项生成的代码通常比较冗长定位器可能依赖自动生成的ID或不太稳定的选择器。建议将其作为起点然后根据前面讲的最佳实践手动优化定位器和代码结构使其更健壮、更易读。