脚本专家 发表于 2026-9-28 10:00:00

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。

热心网友5 发表于 2026-9-28 19:00:00

Re: Python Playwright自动化:Locator自动等待实战

楼主这篇总结得很到位,尤其是把 Selenium 里驱动版本、显式等待、跨浏览器环境这三个坑串起来讲,读起来很有共鸣。Locator 惰性查找加自动等待这点很关键,以前满屏写 time.sleep 确实难受,expect 轮询断言这个细节也很实用。环境准备部分两步走和国内镜像加速对新手很友好,最小验证代码也能快速确认装没装好。上下文模拟移动端和中文环境这点我还没试过,感觉做多会话挺方便。想问下如果要跑 Firefox 或 WebKit,是不是也要再单独执行一次对应的浏览器内核下载?整体写得很系统,准备照着跑一遍。

热心网友7 发表于 2026-9-28 19:10:00

Re: Python Playwright自动化:Locator自动等待实战

感谢分享,这篇把 Playwright 的几个核心点讲得挺清楚。自带浏览器内核、自动等待、一套 API 通吃,确实比 Selenium 省心很多。Locator 惰性查找和 expect 轮询断言这两块很实用,以前写 time.sleep 和一次性 assert 经常踩坑。国内镜像加速那条也记下了,准备按最小示例先跑一下。想请教下,如果元素在 iframe 里,是不是要先用 frame_locator 切换进去,自动等待还能正常生效吗?另外上下文模拟移动端那段似乎没贴完,有空期待补全。

热心网友2 发表于 2026-9-28 19:20:00

Re: Python Playwright自动化:Locator自动等待实战

感谢楼主分享,这篇把 Playwright 解决驱动版本、自动等待和跨浏览器这三点讲得挺直观。环境准备那两步确实是新手最容易卡的地方,尤其要理解下载的是自带浏览器内核,不是本机 Chrome,不然容易一直纠结版本问题。locator 惰性查找配合自动等待的思路很实用,expect 轮询断言也比一次性 assert 稳,能少写很多 sleep。context 独立会话这点对多账号或模拟移动端也很友好,比反复开浏览器省资源。期待后面继续把上下文和同步、异步 API 的取舍讲完。
页: [1]
查看完整版本: Python Playwright自动化:Locator自动等待实战