这就是今天要聊的主角——微软出品的 Playwright,一个让 Web 自动化测试从"体力活"变成"技术活"的现代框架。
01.
一、浏览器自动化的前世今生
要理解 Playwright 带来的变革,得先说说这个领域的老前辈们。
Selenium 诞生于 2004 年,靠着 WebDriver 协议一路打天下,至今仍是市场占有率最高的选择。它的原理是通过浏览器驱动(ChromeDriver、GeckoDriver)作为中间层,向浏览器发送指令。这个架构在当时很合理,但问题也很明显:
协议开销大。每次操作都要经过驱动中转,延迟累积起来很可观。
版本强耦合。Chrome 升级了,ChromeDriver 必须跟着升,否则各种奇奇怪怪的兼容问题。
等待机制反人类。开发者需要手动处理隐式等待、显式等待,写出来的代码又臭又长。
我见过最夸张的 Selenium 用例,光等待逻辑就占了 60% 的代码量。测试用例本身反而被淹没了。
Cypress 在 2015 年杀出来,带来了"开发者体验优先"的理念。它的自动等待、智能断言确实让人眼前一亮。但问题也很致命:只支持 Chromium 内核,多标签页操作像噩梦一样,文件下载更是老大难。
直到 2020 年,微软扔出了 Playwright 这个王炸。
团队背景很有意思——核心成员来自 Puppeteer(Google 开发的 Chrome 自动化库)。他们不满足于 Puppeteer 只支持 Chromium 的局限,决心做真正跨浏览器的自动化框架。靠着对浏览器内核的深刻理解,Playwright 从第一天起就走上了不同的技术路线。
02.
二、Playwright 的核心优势
1.1 自动等待:告别所有 sleep
这是 Playwright 最核心的特性,没有之一。
我们之前写 Selenium,最痛苦的就是等待。页面加载要等、接口返回要等、动画完成要等。不写等待,脚本分分钟报错;写了等待,要么等太久拖慢速度,要么等不够还是报错。
Playwright 的做法是:所有操作都内置自动等待机制。
当你执行 page.click("#submit") 时,Playwright 会在底层自动完成这些检查:
元素是否已经挂载到 DOM 树(Attached)
元素是否可见,没有被 display:none 或 visibility:hidden 隐藏(Visible)
元素的位置是否稳定,没有还在晃动的动画(Stable)
元素是否可交互,没有被 disabled 属性禁用(Enabled)
只有当这些条件全部满足,Playwright 才会执行点击操作。如果 30 秒内元素始终不满足条件,才会抛出超时错误。
我第一次看到这个机制时,心里是存疑的:"真的什么都不用管?"结果我专门写了一个测试,故意让按钮延迟 5 秒才出现,跑了一下午,脚本居然真的乖乖等够了 5 秒才点击。测试通过的那一刻,我直接对 Playwright 路转粉。
1.2 跨浏览器统一 API
Selenium 给人最深的印象就是"同一个功能,不同浏览器写法可能不一样"。Chromium 有 Chromium 的特性,Firefox 有 Firefox 的坑,Safari 更是独树一帜。维护多浏览器测试时,那个酸爽谁用谁知道。
Playwright 统一了所有浏览器的 API。无论你用 Chromium、Firefox 还是 WebKit,代码写法完全一致:
PYTHON# Chromiumbrowser = p.chromium.launch()# Firefoxbrowser = p.firefox.launch()# WebKitbrowser = p.webkit.launch()
我之前做过一个电商项目,需要在 Chrome、Firefox、Safari 上跑兼容性测试。用 Selenium 时,三套代码各自为政,光维护浏览器兼容性就占了我 30% 的时间。迁移到 Playwright 后,一套代码跑三个浏览器,测试用例从 900 行砍到 600 行,代码可读性反而更高了。
1.3 语义化定位器
Selenium 的元素定位一直是个痛点。ID 可能变、Class 可能变、XPath 写出来像天书一样难维护。我见过最长的 XPath 定位器://div[@class='container']//form[@id='login-form']//div[contains(@class,'field')]//input[@name='username'],看到这种代码我都替原作者捏把汗。
Playwright 官方推荐了一套语义化定位器,核心原则是"用功能定位,而不是用结构定位":
PYTHON# 根据角色定位,最推荐page.get_by_role("button", name="登录").click()# 根据标签文本定位page.get_by_text("用户名").fill("admin")# 根据输入框的 label 定位page.get_by_label("密码").fill("123456")# 根据占位符定位page.get_by_placeholder("请输入手机号").fill("13800138000")# 根据 alt 文本定位(图片)page.get_by_alt_text("验证码图片").click()
这些定位器的共同特点是:它们描述的是"功能"而非"结构"。只要页面上这个按钮的文字还是"登录",只要这个输入框的 label 还是"用户名",无论前端怎么重构,代码都不用改。
我之前维护一个 Vue 项目,前端团队喜欢频繁改结构。用了 Playwright 的语义化定位器后,每次改版需要调整的测试代码从平均 15 处降到了 2-3 处。我的头发保住了。
1.4 上下文隔离:测试隔离的正确姿势
Playwright 的架构里有一个核心概念叫"Browser Context"。每个 Context 相当于一个全新的浏览器配置文件——独立的 Cookie、独立的 LocalStorage、独立的会话。
这带来了一个巨大的好处:测试隔离。
用 Selenium 时,如果测试之间没有做好数据清理,很容易互相影响。A 测试登录了用户甲,B 测试可能继承了这个登录状态,导致奇怪的 Bug。
Playwright 的做法是,每个测试创建一个全新的 Context,测试结束后直接销毁。零污染,零干扰。
PYTHON# 每个测试都这样创建全新的上下文context = browser.new_context()page = context.new_page()# ... 执行测试 ...context.close() # 测试结束,干净利落
进阶用法是"一次登录,到处复用"。你可以先手动登录一次,把认证状态保存下来,后续所有测试直接加载这个状态,省掉重复登录的麻烦:
PYTHON# 保存登录状态context = browser.new_context()page = context.new_page()# ... 手动登录 ...context.storage_state(path="auth.json")# 后续测试直接加载context = browser.new_context(storage_state="auth.json")page = context.new_page() # 已经处于登录状态
这个功能在做爬虫时也特别有用。登录一次,后续所有请求都带着登录态,不用每次都走登录流程,效率提升明显。
03.
三、Playwright 基础操作
3.1 安装与快速上手
Playwright 的安装比我用过的任何自动化框架都简单。三条命令搞定:
BASHpip install playwrightplaywright install # 安装浏览器驱动
如果你在国内,网络不太好,可以指定国内镜像加速:
BASHplaywright install --force --with-deps chromium
默认安装 Chromium、Firefox、WebKit 三种浏览器。如果你只需要一种,可以指定:
BASHplaywright install chromium
第一个脚本简单到令人发指:
PYTHONfrom playwright.sync_api import sync_playwrightwith sync_playwright() as p: browser = p.chromium.launch() # 启动浏览器 page = browser.new_page() # 创建新页面 page.goto("https://example.com") # 访问网址 print(page.title()) # 打印标题 browser.close() # 关闭浏览器
如果你用的是 asyncio 风格的异步代码,Playwright 也支持:
PYTHONimport asynciofrom playwright.async_api import async_playwrightasync 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,上手更简单。等对框架熟悉了,再根据项目需求决定是否切换到异步。
3.2 页面导航与等待
页面导航是所有自动化操作的基础。Playwright 提供了完善的导航 API:
PYTHON# 基本跳转page.goto("https://example.com")# 等待页面加载完成再继续page.goto("https://example.com", wait_until="networkidle")# 支持的等待策略:# - load: 默认,等待 load 事件# - domcontentloaded: 等待 DOMContentLoaded 事件# - networkidle: 等待网络空闲(无活动请求超过 500ms)# 前进、后退、刷新page.go_back()page.go_forward()page.reload()
wait_until 参数是我最常用的一个。默认的 load 策略有时候页面还在渲染后续内容,自动化脚本就开始点击了,很容易出问题。我一般用 networkidle,虽然会慢一点,但稳定性好很多。
3.3 元素交互
Playwright 的元素交互 API 设计得很优雅,基本覆盖了所有常用场景:
PYTHON# 点击操作page.get_by_role("button", name="提交").click()page.get_by_text("删除").dblclick() # 双击page.get_by_text("复制").click(button="right") # 右键# 输入操作page.get_by_label("用户名").fill("admin") # 填入内容(自动清空)page.get_by_label("搜索").type("hello", delay=100) # 模拟逐字输入# 选择操作page.select_option("#city", value="beijing") # 通过 value 选择page.select_option("#city", label="北京") # 通过显示文本选择page.select_option("#city", index=2) # 通过索引选择# 勾选/取消勾选page.get_by_role("checkbox", name="同意协议").check()page.get_by_role("checkbox", name="同意协议").uncheck()# 文件上传page.set_input_files("#upload", "path/to/file.pdf")# 键盘操作page.keyboard.press("Enter")page.keyboard.press("Control+A")page.keyboard.press("Control+C")page.keyboard.type("hello world")
我最喜欢的是 fill 和 type 的区别。fill 直接设置值,适合不需要触发输入事件的场景;type 模拟逐字输入,会触发 input 事件,适合需要前端做实时校验的场景。根据业务需求选对方法,能省很多调试时间。
3.4 获取元素信息
自动化不仅要操作元素,有时候还需要"看"页面:
PYTHON# 获取文本内容title = page.locator("h1").inner_text()# 获取元素属性href = page.get_by_role("link").get_attribute("href")# 获取输入框的值value = page.locator("#search").input_value()# 判断元素状态is_visible = page.get_by_role("button").is_visible()is_enabled = page.get_by_role("button").is_enabled()is_checked = page.get_by_role("checkbox").is_checked()
这些方法返回的都是 Python 原生类型,可以直接用在断言里。
04.
四、Playwright 高级功能
4.1 网络拦截与 Mock
这是 Playwright 最强大的功能之一,也是我做爬虫时的杀手锏。
你可以拦截浏览器的所有网络请求,修改请求头、伪造响应、阻止某些资源加载:
PYTHON# 拦截图片请求,加快页面加载page.route("**/*.{png,jpg,jpeg,gif}", lambda route: route.abort())# 拦截特定 API,返回假数据def handle_api(route): route.fulfill( status=200, content_type="application/json", body='{"code": 0, "data": {"name": "测试数据"}}' )page.route("**/api/user/info", handle_api)# 修改请求头def modify_headers(route): headers = route.request.headers headers["X-Custom-Header"] = "custom-value" route.continue_(headers=headers)page.route("**/*", modify_headers)
测试场景里,这个功能可以用来 Mock 后端接口。前端页面还没接后端?没关系,自己构造假数据,照样跑测试。我之前做一个数据看板项目,后端接口延期了两周,我就是靠 Mock 数据把前端测试跑完了,一点没耽误进度。
爬虫场景里,这个功能更香。有些网站的接口返回 JSON 数据,比解析 HTML 方便 10 倍不止。我爬某电商平台的商品评论,就是直接拦截接口请求,拿到的全是结构化的 JSON,解析效率比 XPath 快了 5 倍。
4.2 等待机制详解
虽然 Playwright 做了自动等待,但有些场景你还是需要手动控制:
PYTHON# 等待元素出现page.wait_for_selector("#result", timeout=5000)# 等待元素可见page.wait_for_selector("#result", state="visible")# 等待元素消失(加载完成)page.wait_for_selector("#loading", state="hidden")# 等待网络空闲page.wait_for_load_state("networkidle")# 等待函数返回 Truepage.wait_for_function("() => document.querySelector('.count').textContent === '100'")# 固定等待(不推荐,但调试时可以用)page.wait_for_timeout(2000)
记住一个原则:能用自动等待解决的问题,千万别用固定等待。time.sleep() 在 Playwright 里是禁止使用的,它会阻塞事件循环,导致不可预期的行为。如果必须等待,用 wait_for_timeout,它内部是异步实现,不会出问题。
4.3 截图与录屏
PYTHON# 页面截图page.screenshot(path="screenshot.png")# 整页截图page.screenshot(path="full_page.png", full_page=True)# 元素截图page.locator(".chart").screenshot(path="chart.png")# 录屏(需要开启上下文录制)context = browser.new_context(record_video_dir="videos/")page = context.new_page()# ... 操作 ...context.close() # 关闭时自动保存视频
我经常用截图功能做测试报告。每个失败的用例自动截一张图,测试报告里直接展示问题页面,比纯文字描述直观多了。
4.4 处理弹窗与新窗口
PYTHON# 处理 alert/confirm/promptdef handle_dialog(dialog): print(f"弹窗类型: {dialog.type}, 内容: {dialog.message}") dialog.accept() # 确认 # dialog.dismiss() # 取消page.on("dialog", handle_dialog)page.click("#show-alert")# 处理新窗口/标签页with page.context.expect_page() as new_page_info: page.click("a[target='_blank']")new_page = new_page_info.valueawait new_page.wait_for_load_state()print(await new_page.title())
这个功能做自动化测试时特别有用。点一个链接跳转到新页面,直接拿到新页面的引用,继续操作,比 Selenium 的 switch_to.window() 简洁多了。
4.5 移动端模拟
Playwright 原生支持移动端模拟,不用额外安装什么:
PYTHON# 模拟 iPhone 12context = browser.new_context( viewport={"width": 390, "height": 844}, user_agent="Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) ...", locale="zh-CN", geolocation={"longitude": 116.28, "latitude": 40.05}, # 经纬度 permissions=["geolocation"] # 权限)# 或者直接用内置的设备预设from playwright.sync_api import devicescontext = browser.new_context(**devices["iPhone 12"])
这个功能做响应式测试特别方便。同一个测试用例,分别用桌面端和移动端跑一遍,验证不同设备上的表现。
05.
五、实战案例:端到端测试
接下来看一个完整的测试案例,演示如何用 Playwright 编写可靠的端到端测试。
场景:测试一个用户登录流程,包含正确登录、错误密码、账户锁定三种情况。
PYTHONfrom playwright.sync_api import sync_playwright, expectdef test_login_success(): """正确登录场景""" with sync_playwright() as p: browser = p.chromium.launch() context = browser.new_context() page = context.new_page() # 访问登录页 page.goto("https://demo.playwright.test/login") # 填写表单 page.get_by_label("用户名").fill("testuser") page.get_by_label("密码").fill("correct_password") # 点击登录 page.get_by_role("button", name="登录").click() # 验证跳转到首页 expect(page).to_have_url("https://demo.playwright.test/dashboard") # 验证用户名显示 expect(page.get_by_text("欢迎,testuser")).to_be_visible() context.close()def test_login_wrong_password(): """密码错误场景""" with sync_playwright() as p: browser = p.chromium.launch() context = browser.new_context() page = context.new_page() page.goto("https://demo.playwright.test/login") page.get_by_label("用户名").fill("testuser") page.get_by_label("密码").fill("wrong_password") page.get_by_role("button", name="登录").click() # 验证错误提示出现 expect(page.get_by_text("用户名或密码错误")).to_be_visible() # 验证还在登录页 expect(page).to_have_url("https://demo.playwright.test/login") context.close()def test_account_locked(): """账户锁定场景""" with sync_playwright() as p: browser = p.chromium.launch() context = browser.new_context() page = context.new_page() page.goto("https://demo.playwright.test/login") # 模拟连续 5 次输错密码 for _ in range(5): page.get_by_label("用户名").fill("locked_user") page.get_by_label("密码").fill("wrong_password") page.get_by_role("button", name="登录").click() page.wait_for_timeout(500) # 短暂等待 # 第 6 次输入正确密码 page.get_by_label("用户名").fill("locked_user") page.get_by_label("密码").fill("correct_password") page.get_by_role("button", name="登录").click() # 验证账户锁定提示 expect(page.get_by_text("账户已被锁定")).to_be_visible() context.close()
这个测试用例有几个亮点:
使用 expect 断言库,内置自动重试机制。断言会等待条件满足再验证,不用担心时序问题。
使用语义化定位器。get_by_label("用户名") 比 CSS 选择器 .form-control input:nth-child(1) 好维护多了。
测试隔离。每个测试都是全新的 Context,完全独立运行,互不干扰。
断言明确。expect(page).to_have_url(...) 比 assert "dashboard" in page.url 可读性好很多,失败时也更容易定位问题。
06.
六、实战案例:爬虫开发
说完测试,再聊一个 Playwright 的另一个主战场——爬虫。
场景:爬取某电商平台的商品列表,提取商品名称、价格、销量。
传统做法是用 requests 库请求页面,然后解析 HTML。但现代网站大量使用 JavaScript 渲染,requests 拿到的可能是一个空壳 Div。Selenium 虽然能执行 JS,但速度慢、占用高、反爬能力弱。
Playwright 兼顾了两边的优点:原生支持 JS 渲染,速度比 Selenium 快 2-3 倍,反爬能力更强。
PYTHONfrom playwright.sync_api import sync_playwrightimport jsondef scrape_products(keyword, max_pages=5): """爬取电商平台商品列表""" results = [] with sync_playwright() as p: browser = p.chromium.launch(headless=True) # 无头模式 context = browser.new_context( user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" ) # 拦截 API 请求,直接拿 JSON 数据 api_data = [] def handle_response(response): if "/api/product/list" in response.url and response.status == 200: try: data = response.json() api_data.append(data) except: pass page = context.new_page() page.on("response", handle_response) for page_num in range(1, max_pages + 1): print(f"正在爬取第 {page_num} 页...") # 访问搜索页 page.goto( f"https://search.example.com/?keyword={keyword}&page={page_num}", wait_until="networkidle" ) # 等待商品列表加载 page.wait_for_selector(".product-item", timeout=10000) # 如果拦截到了 API 数据,直接解析 JSON if api_data: for item in api_data[-1].get("data", {}).get("list", []): results.append({ "name": item.get("title"), "price": item.get("price"), "sales": item.get("sales"), }) api_data.clear() # 清空,准备接收下一页数据 else: # 否则用传统方式解析 DOM products = page.locator(".product-item").all() for product in products: name = product.locator(".product-title").inner_text() price = product.locator(".product-price").inner_text() sales = product.locator(".sales-count").inner_text() results.append({ "name": name, "price": price, "sales": sales, }) context.close() return results# 运行爬虫if __name__ == "__main__": products = scrape_products("Python书籍", max_pages=3) with open("products.json", "w", encoding="utf-8") as f: json.dump(products, f, ensure_ascii=False, indent=2) print(f"共爬取 {len(products)} 条商品数据")
这个爬虫有两个技巧值得说说:
拦截 API 请求。现代网站大量使用 AJAX 加载数据,直接拦截接口拿 JSON,比解析 HTML 省事多了。数据规整,字段清晰,解析效率还高。
无头模式 + User-Agent 伪装。headless=True 让浏览器在后台运行,不弹出窗口。配合自定义 User-Agent,伪装成正常浏览器访问。
说实话,我之前用 Selenium 爬数据,速度慢不说,还动不动就被反爬检测拦了。换 Playwright 后,同样的网站,检测拦截率降低了 70%。不是说 Playwright 不会被检测,而是它的底层通信机制更接近真实浏览器,给反爬系统制造了更大的识别难度。
07.
七、调试与工具链
Playwright 自带了一套强大的调试工具,这是我用过最贴心的设计。
7.1 Playwright Inspector
启动命令:
BASHplaywright open https://example.com
这会打开一个可视化窗口,你可以:
检查页面元素,生成定位器
逐步执行测试代码
查看每个操作的实际参数
设置断点,单步调试
我刚学 Playwright 时,Inspector 是我的主要学习工具。看到它生成的代码,再对照自己的写法,进步很快。
7.2 Trace Viewer
Trace Viewer 是 Playwright 最强大的调试功能。当测试失败时,Playwright 可以生成一个追踪文件,包含测试执行的全过程:
每个操作的屏幕截图
每个时刻的 DOM 快照
所有网络请求和响应
控制台日志
你可以离线回放整个测试过程,精准定位问题出在哪一步。
PYTHONfrom playwright.sync_api import sync_playwrightwith sync_playwright() as p: browser = p.chromium.launch() context = browser.new_context() # 开始录制 context.tracing.start(screenshots=True, snapshots=True) page = context.new_page() # ... 测试代码 ... # 停止录制,保存到文件 context.tracing.stop(path="trace.zip")# 打开追踪文件查看# playwright show-trace trace.zip
7.3 代码生成器
如果你不知道某个操作的代码怎么写,可以用代码生成器:
BASHplaywright codegen https://example.com
运行命令后会自动打开浏览器,你手动操作一遍,Playwright 会实时生成对应的 Python 代码。生成的代码质量很高,用的是语义化定位器,有合理的等待逻辑。生成完后直接复制到你的项目里,稍作调整就能用。
我之前让实习生用这个工具生成基础脚本,他第一天就写出了 100 多行可用的自动化代码,第二天就开始理解框架设计了。这个工具对新手真的太友好了。
08.
八、实战:价格监控系统
最后看一个综合案例,用 Playwright 做个实用的价格监控系统。
场景:监控某电商平台的商品价格,价格低于阈值时发送通知。
PYTHONfrom playwright.sync_api import sync_playwrightimport jsonimport timeimport requestsCONFIG_FILE = "config.json"def load_config(): """加载配置文件""" try: with open(CONFIG_FILE, "r") as f: return json.load(f) except FileNotFoundError: return {"products": []}def save_config(config): """保存配置文件""" with open(CONFIG_FILE, "w") as f: json.dump(config, f, ensure_ascii=False, indent=2)def get_current_price(page, url): """获取商品当前价格""" page.goto(url, wait_until="networkidle") # 有些价格可能在弹窗里,需要关闭 try: page.get_by_role("button", name="关闭").click(timeout=2000) except: pass # 等待价格元素出现 page.wait_for_selector(".price", timeout=10000) # 获取价格文本并清洗 price_text = page.locator(".price").inner_text() price = float(price_text.replace("¥", "").replace(",", "")) return pricedef send_notification(title, message): """发送通知(这里用 PushPlus 示例)""" token = "your_token_here" url = f"http://www.pushplus.plus/send?token={token}&title={title}&content={message}" requests.get(url)def monitor_products(): """监控商品价格""" config = load_config() products = config.get("products", []) if not products: print("没有配置监控商品") return with sync_playwright() as p: browser = p.chromium.launch(headless=True) context = browser.new_context() for product in products: name = product["name"] url = product["url"] threshold = product["threshold"] last_price = product.get("last_price") print(f"正在检查: {name}") try: page = context.new_page() current_price = get_current_price(page, url) print(f" 当前价格: ¥{current_price}, 阈值: ¥{threshold}") # 价格低于阈值,发送通知 if current_price <= threshold: msg = f"{name} 当前价格 ¥{current_price},低于阈值 ¥{threshold}!" print(f" 🚨 {msg}") send_notification("价格监控提醒", msg) # 更新最后价格 product["last_price"] = current_price product["last_check"] = time.strftime("%Y-%m-%d %H:%M:%S") page.close() except Exception as e: print(f" ❌ 检查失败: {str(e)}") context.close() save_config(config)# 初始化配置def init_config(): """初始化配置文件""" config = { "products": [ { "name": "iPhone 15 Pro", "url": "https://item.example.com/iphone15", "threshold": 7000, "last_price": None, "last_check": None }, { "name": "MacBook Air M3", "url": "https://item.example.com/macbook-air", "threshold": 8000, "last_price": None, "last_check": None } ] } save_config(config) print("配置文件已创建")if __name__ == "__main__": import sys if len(sys.argv) > 1 and sys.argv[1] == "init": init_config() else: monitor_products()
运行方式:
BASH# 首次运行,初始化配置python price_monitor.py init# 定期执行监控python price_monitor.py
配合系统的定时任务(Windows 任务计划程序、Linux cron),就能实现自动价格监控。我自己跑这个脚本三个月,抓到过两次 iPhone 降价通知,省了将近 1000 块。
09.
九、总结
用了两年多 Playwright,我的感受是:这是一个让自动化测试回归本质的工具。
它的自动等待机制消灭了 90% 的时序问题,语义化定位器让代码好维护了,统一的 API 让跨浏览器测试不再是噩梦。配套的调试工具(Inspector、Trace Viewer)又让问题定位变得异常简单。
对于测试工程师来说,Playwright 绝对是 2026 年最值得投资的新技能。市场数据已经说明一切:75% 的新项目开始采用 Playwright,GitHub Stars 突破 7.8 万,周下载量 1350 万次。这些数字背后,是整个行业对 Playwright 技术路线的认可。
对于爬虫开发者来说,Playwright 的 JS 渲染能力和低检测率,让它在现代网站爬取场景里几乎是必选方案。同样的数据,用 requests 可能拿不到,用 Playwright 一把梭。
如果你是 Selenium 老用户,我的建议是:不要急着迁移旧项目,先在新项目里用起来。等对框架熟悉了,再评估迁移成本。如果你是自动化测试的新手,直接从 Playwright 开始,少走三年弯路。
工具在进化,理念也在进步。Playwright 代表的,是"让测试更简单、让自动化更可靠"这个方向的正确探索。
📌 更多Python技术干货,关注"Python与AI智能研习社"~