Python Playwright自动化:Locator自动等待实战
网页自动化最容易被三件事拖住:chromedriver 版本必须和本机 Chrome 严丝合缝,浏览器一自动升级,脚本第二天就抛 SessionNotCreatedException;页面还没渲染完就 find_element,直接报 NoSuchElementException,只能满屏写 time.sleep(3);再换到 Firefox、Edge,驱动环境又得重来一遍。Playwright 由微软团队维护,Python 版 API 设计得比较干净,刚好把这三点一起处理掉:自带浏览器内核(一条命令下载,版本天然匹配)、操作自带自动等待(不必手写 sleep)、一套 API 通吃 Chromium / Firefox / WebKit。
一、环境准备:两条命令
Playwright 支持 Python 3.8 及以上,同样建议用虚拟环境隔离依赖:
python -m venv .venv
source .venv/bin/activate # Windows 用 .venv\Scripts\activate
# 第一步:装 Python 包
pip install playwright
# 第二步:下载浏览器内核(约 100~300MB,只需执行一次)
playwright install chromium
第二步是新手最容易卡住的地方:它下载的是 Playwright 自带的浏览器,不是你桌面上的 Chrome,所以不存在版本对不上的问题。国内下载慢可以走镜像加速:
# 使用国内镜像加速下载
PLAYWRIGHT_DOWNLOAD_HOST=https://cdn.npmmirror.com/binaries/playwright playwright install chromium
装完先跑一段最小验证代码:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto("https://example.com")
print("页面标题:", page.title())
browser.close()
能打印出 Example Domain,就说明环境和浏览器内核都没问题。
二、最小可运行示例:打开页面并截图
Playwright 提供同步和异步两套 API,日常脚本用同步版最省心。下面的代码打开 python.org 并截取整页长图:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
# headless=True 表示无界面运行;调试时改成 False 能看到真实浏览器
browser = p.chromium.launch(headless=True)
page = browser.new_page(viewport={"width": 1280, "height": 800})
page.goto("https://www.python.org/")
# full_page=True 会截取整页长图,不只是可视区域
page.screenshot(path="python_org.png", full_page=True)
browser.close()
对比 Selenium 同样的功能,这里少了驱动配置、少了显式等待,代码块从十几行压到十行以内。
三、三个核心概念:Locator、自动等待、上下文
1. Locator:定位元素的新写法
Playwright 推荐用 locator() 拿到元素句柄,后续操作都基于它,下面几种写法等价:
# 下面几种写法等价,选你顺手的
page.locator("button#submit").click()
page.locator("text=登录").click()
page.get_by_role("button", name="登录").click() # 按语义角色,最稳
page.get_by_placeholder("请输入用户名").fill("admin")
page.get_by_text("下一步").click()
Locator 是惰性的:写 page.locator(...) 时并不会真的去查 DOM,只有执行 .click()、.fill() 时才查找,并且会自动等到元素可见、可点击、不被遮挡。这就是“自动等待”的实现基础。
2. 自动等待:和 time.sleep 说再见
官方断言工具 expect 会轮询等待,而不是一次性判断,比 assert page.title() == ... 这类写法可靠得多:
from playwright.sync_api import expect
# 断言元素可见(默认超时 5 秒,期间反复重试)
expect(page.get_by_text("提交成功")).to_be_visible(timeout=10000)
# 断言 URL 变化
expect(page).to_have_url("https://example.com/dashboard")
需要改全局超时可以直接设置默认值:
page.set_default_timeout(15000) # 所有操作默认最多等 15 秒
3. 上下文:多开互不干扰的会话
context 相当于一个独立的浏览器会话,拥有独立的 Cookie 和缓存,比开多个 browser 省资源,也可以用来模拟移动端和中文环境:
with sync_playwright() as p:
browser = p.chromium.launch()
# 模拟移动端 + 中文环境
ctx = browser.new_context(
viewport={"width": 390, "height": 844},
user_agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15",
locale="zh-CN",
)
page = ctx.new_page()
page.goto("https://example.com")
ctx.close() # 关上下文,Cookie 一起清掉
browser.close()
四、进阶操作:表单、上传下载与请求拦截
1. 填表单与键盘鼠标
page.get_by_label("用户名").fill("zhangsan")
page.get_by_label("密码").fill("secret123")
page.get_by_label("记住我").check()
page.get_by_role("combobox").select_option("beijing")
page.keyboard.press("Enter")
2. 文件上传与下载
上传直接给本地路径,不用再操作系统文件选择框;下载必须用 expect_download 包住触发动作,否则拿不到下载对象:
# 上传:直接给本地路径,不用再找系统文件框
page.set_input_files("input", "report.xlsx")
# 下载:必须用 expect_download 包住触发动作
with page.expect_download() as dl:
page.get_by_text("导出报表").click()
download = dl.value
download.save_as("data/export.xlsx")
3. 拦截请求:屏蔽图片提速
抓数据时图片、字体往往没用,直接拦掉能明显加速:
def block_images(route):
if route.request.resource_type in ("image", "font", "media"):
route.abort()
else:
route.continue_()
page.route("**/*", block_images)
page.goto("https://news.example.com")
五、实战:带登录态抓取分页列表
下面这段完整代码把登录、断言等待、分页抓取、保存登录态串在一起,下次运行可以直接复用 auth.json,省掉重复登录:
from playwright.sync_api import sync_playwright, expect
BASE = "https://example.com"
def main():
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
ctx = browser.new_context(locale="zh-CN")
page = ctx.new_page()
page.set_default_timeout(15000)
# 1. 登录
page.goto(f"{BASE}/login")
page.get_by_label("账号").fill("your_account")
page.get_by_label("密码").fill("your_password")
page.get_by_role("button", name="登录").click()
# 等到跳转完成,不要用 sleep
expect(page).to_have_url(f"{BASE}/dashboard")
# 2. 抓多页列表
rows = []
for page_no in range(1, 4):
page.goto(f"{BASE}/items?page={page_no}")
expect(page.locator(".item-row").first).to_be_visible()
items = page.locator(".item-row").all()
for it in items:
rows.append({
"title": it.locator(".title").inner_text(),
"price": it.locator(".price").inner_text(),
})
print(f"第 {page_no} 页抓到 {len(items)} 条")
# 3. 保存登录态,下次直接复用,省掉重复登录
ctx.storage_state(path="auth.json")
print("共抓取", len(rows), "条")
browser.close()
if __name__ == "__main__":
main()
下次运行时直接加载已保存的登录态:
ctx = browser.new_context(storage_state="auth.json")
六、和 Selenium 怎么选
从驱动、等待、多浏览器、录制、网络拦截几个维度对比:
[*]浏览器驱动:Playwright 自带,一条命令安装;Selenium 需手动下载,版本必须匹配。
[*]等待机制:Playwright 操作自带自动等待;Selenium 需自己写 WebDriverWait 或 sleep。
[*]多浏览器:Playwright 一套 API 支持 Chromium/Firefox/WebKit;Selenium 每个浏览器各自适配。
[*]录制脚本:Playwright 官方支持 playwright codegen;Selenium 依赖第三方插件。
[*]网络拦截:Playwright 原生 page.route;Selenium 需借助代理。
[*]生态与资料:Playwright 较新,中文资料相对少;Selenium 十年积累,资料最多,岗位要求常见。
结论:新项目优先 Playwright,效率和稳定性都更好;老项目或需要兼容既有 Selenium 资产的继续用 Selenium,两者并不互斥。
七、常见坑与解决办法
1)playwright install 卡住或超时
换国内镜像(见第一节)。公司网络有代理时,先设置 HTTPS_PROXY 环境变量再执行安装。
2)locator.click() 超时
先确认目标是不是在 iframe 里。跨框架要先切进去再操作:
frame = page.frame_locator("iframe#login-frame")
frame.get_by_role("button", name="登录").click()
3)被网站识别为自动化
默认 headless 会带自动化特征,可以加启动参数缓解:
browser = p.chromium.launch(
headless=True,
args=["--disable-blink-features=AutomationControlled"],
)
4)抓到的中文乱码
确认 new_context(locale="zh-CN"),并且写入文件时指定编码 encoding="utf-8"。Windows 上尤其容易踩这个坑。
5)调试时看不到过程
打开录制,事后回看每一步:
ctx.tracing.start(screenshots=True, snapshots=True)
# ... 你的操作 ...
ctx.tracing.stop(path="trace.zip")
再用 playwright show-trace trace.zip 在浏览器里逐步回放。
八、总结与下一步
Playwright 的三个核心优势可以概括为:自带浏览器(告别驱动地狱)、自动等待(告别满屏 sleep)、统一 API(一套代码跑三种内核)。如果之前被 Selenium 的驱动和等待折腾过,迁移过来基本是纯收益。
下一步可以从三件事入手:用 playwright codegen https://你的目标站点 录一遍,观察官方推荐的选择器写法;把现有脚本里的 time.sleep 逐步替换成 expect(...) 断言;需要并发时改用异步 API(from playwright.async_api import async_playwright)配合 asyncio.gather。
Re: Python Playwright自动化:Locator自动等待实战
楼主这篇总结得很到位,尤其是把 Selenium 里驱动版本、显式等待、跨浏览器环境这三个坑串起来讲,读起来很有共鸣。Locator 惰性查找加自动等待这点很关键,以前满屏写 time.sleep 确实难受,expect 轮询断言这个细节也很实用。环境准备部分两步走和国内镜像加速对新手很友好,最小验证代码也能快速确认装没装好。上下文模拟移动端和中文环境这点我还没试过,感觉做多会话挺方便。想问下如果要跑 Firefox 或 WebKit,是不是也要再单独执行一次对应的浏览器内核下载?整体写得很系统,准备照着跑一遍。Re: Python Playwright自动化:Locator自动等待实战
感谢分享,这篇把 Playwright 的几个核心点讲得挺清楚。自带浏览器内核、自动等待、一套 API 通吃,确实比 Selenium 省心很多。Locator 惰性查找和 expect 轮询断言这两块很实用,以前写 time.sleep 和一次性 assert 经常踩坑。国内镜像加速那条也记下了,准备按最小示例先跑一下。想请教下,如果元素在 iframe 里,是不是要先用 frame_locator 切换进去,自动等待还能正常生效吗?另外上下文模拟移动端那段似乎没贴完,有空期待补全。Re: Python Playwright自动化:Locator自动等待实战
感谢楼主分享,这篇把 Playwright 解决驱动版本、自动等待和跨浏览器这三点讲得挺直观。环境准备那两步确实是新手最容易卡的地方,尤其要理解下载的是自带浏览器内核,不是本机 Chrome,不然容易一直纠结版本问题。locator 惰性查找配合自动等待的思路很实用,expect 轮询断言也比一次性 assert 稳,能少写很多 sleep。context 独立会话这点对多账号或模拟移动端也很友好,比反复开浏览器省资源。期待后面继续把上下文和同步、异步 API 的取舍讲完。
页:
[1]