首页 / Playwright 入门教程 / CSS 选择器定位

Playwright 入门教程

CSS 选择器定位

本教程共 59 篇 · 第 10 篇 · 更新于 2026-08-04 · 约 9 分钟阅读

CSS 选择器locatorhas-textvisible 伪类nth-matchShadow DOM

10. CSS 选择器定位

本节目标:会用 locator('css=...') 写 CSS 定位,了解 Playwright 给 CSS 加的几个好用伪类,并明白为什么 CSS 该当兜底、别当主力。

什么时候才用 CSS

第 9 章列的内置定位器(role、text、label、test id)是首选。它们贴近用户视角,页面一改结构也不容易挂。

但总有角落用不上它们:比如一个没有语义、没有文字、也没 test id 的纯样式 div。这时候 CSS 选择器是合理兜底。

基本写法:

await page.locator('css=button').click();
// 省略前缀也行,Playwright 会自动识别
await page.locator('button').click();
Warning

CSS 绑定 DOM 结构。那种 #tsf > div:nth-child(2) > div.A8SBwf > ... 的超长链条是反面教材,页面一重构就崩。能用 role 或 test id 就别写它。

Playwright 给 CSS 加了什么

标准 CSS 选择器 Playwright 都认。此外它还做了两处增强:

  1. CSS 选择器能穿透开放的 Shadow DOM(后面第 16 章细讲)。
  2. 加了一批自定义伪类,专门好用。

按文字匹配::has-text()

:has-text() 匹配「内部某处含指定文字」的元素,大小写不敏感、会去空白、按子串找。

// 反例:会匹配很多元素,连 <body> 都算,千万别单独用
await page.locator(':has-text("Playwright")').click();

// 正确写法:配合标签限定,只匹配 <article>
await page.locator('article:has-text("Playwright")').click();

还有几个文本伪类:

  • :text("Home"):匹配含文字的最小元素
  • :text-is("Home"):精确相等(区分大小写)
  • :text-matches("reg?ex", "i"):用正则匹配
Note

文字匹配永远先规整空白:多个空格并成一个、换行变空格、去首尾空白。:text-is("Log") 匹配不到 <button>Log in</button>,因为后者整段文字是 “Log in” 而非 “Log”。

只匹配可见的::visible

css=button 会匹配页面上所有按钮,包括隐藏的。加 :visible 只留看得见的。

// 两个按钮,一个有 display:none;下面这句只点得到的那个
await page.locator('button:visible').click();

含某子元素::has()

:has() 是标准 CSS 伪类。Playwright 完全支持,用来「挑一个内部含某元素的父级」。

await page.locator('article:has(div.promo)').textContent();

按布局凑近::right-of 等

:right-of():left-of():above():below():near() 按元素相对位置找。比如页面有多个难区分的输入框时:

// 填「密码」文字右边的那个输入框
await page.locator('input:right-of(:text("Password"))').fill('value');
// 点离促销卡很近的按钮
await page.locator('button:near(.promo-card)').click();
Warning

布局选择器官方已标记弃用(deprecated),将来可能移除。布局差一个像素就可能选错元素。能用语义定位器就别碰它。

取第 n 个匹配::nth-match()

当一堆相似元素难区分时,:nth-match(选择器, 序号) 按序号(从 1 开始)取一个。

// 点第三个写着「Buy」的按钮
await page.locator(':nth-match(:text("Buy"), 3)').click();

它还能用来「等够数量出现」:

await page.locator(':nth-match(:text("Buy"), 3)').waitFor();
Note

:nth-child 不同,:nth-match 的元素不必是兄弟节点,可以是页面上任意位置。

快捷属性选择器

Playwright 对几个属性给了简写:iddata-testiddata-test-iddata-test

await page.locator('id=username').fill('value');
await page.locator('data-test-id=submit').click();
Warning

官方推荐改用 getByTestId(),第 13 章会讲。这里列出来主要是让你看懂老代码。

Note

这类属性选择器不是真 CSS,所以 :enabled 这种 CSS 伪类不支持。要带状态判断,用标准的 css=[data-test="login"]:enabled

老式 text= 选择器

还有个遗留写法 text=...,能力类似现代文本定位器,但官方建议用 getByText() 替代:

await page.locator('text=Log in').click();        // 子串、不区分大小写
await page.locator('text="Log in"').click();      // 精确、区分大小写
await page.locator('text=/Log\\s*in/i').click();   // 正则

小结

CSS 定位写 locator('css=...'),前缀省略也行。Playwright 额外送了 :has-text():visible:has():nth-match() 这几个好用伪类。

但 CSS 绑定的是 DOM 实现,页面一重构就碎。把它当兜底:role、text、test id 都够不着的时候再上。

下一章讲最贴近用户视角、也是官方首推的按角色定位 getByRole