CSS 选择器详细教程:原理、语法、方向/"轴"与实战
面向:爬虫与自动化同学(Python 为主),从原理到实战。重点讲"方向/轴"的概念与 CSS 的能力边界。
1. CSS 选择器是什么?
CSS 选择器 是一套用来在 HTML 文档树中定位元素的规则语法。浏览器使用它来应用样式,而爬虫/自动化用它来定位元素。
优势:
- 语法简洁、易读
- 浏览器原生支持,执行速度快
- 适合大量"按 class/id/层级"的定位需求
2. 原理:DOM 树与匹配规则
HTML 会被解析成 DOM 树,CSS 选择器就是在树上做"匹配"。
核心思想:
- 通过 标签、class、id、属性 等条件筛选节点
- 通过 组合器(combinator) 表达节点之间的方向关系
3. CSS 基础语法速览
3.1 基本选择
css
#id
.class
TagName
3.2 属性选择器
css
[a]
[a="v"]
[a^="v"] /* 以 v 开头 */
[a$="v"] /* 以 v 结尾 */
[a*="v"] /* 包含 v */
3.3 组合器(方向关系)
css
A B /* 后代 */
A > B /* 子元素 */
A + B /* 相邻兄弟 */
A ~ B /* 后续兄弟 */
3.4 伪类与伪元素
css
:first-child
:nth-child(2)
:nth-of-type(3)
:not(.disabled)
4. CSS 的"轴/方向"概念(重点)
XPath 有"轴(axis)"概念,比如 parent::、ancestor::、following-sibling::。CSS 没有"轴"这个术语,但组合器 + 伪类可以表达常见方向关系。
4.1 CSS 可表达的方向(类轴)
| XPath 轴概念 | CSS 对应方式 | 示例 |
|---|---|---|
| 子元素(child) | > |
div > span |
| 后代(descendant) | 空格 | div span |
| 右侧兄弟(following-sibling) | + 或 ~ |
h2 + p / h2 ~ p |
4.2 CSS 不擅长/不支持的方向
- 向上找父级/祖先 (XPath 的
parent::、ancestor::) - 向左找前置兄弟 (XPath 的
preceding-sibling::)
这就是为什么复杂定位场景通常更推荐 XPath。
4.3 :has()(现代 CSS 的"反向选择")
CSS4 的 :has() 允许根据子元素反推父元素:
css
div:has(> span.author)
注意:
- 浏览器支持度较新;有些解析器/引擎不支持
- 在 Selenium 中是否可用取决于浏览器对
:has()的支持情况
5. 实战一:BeautifulSoup(bs4)里的 CSS 选择器
5.1 基本用法
python
from bs4 import BeautifulSoup
html = """<div class='post'><h2>标题</h2></div>"""
soup = BeautifulSoup(html, "lxml")
# 返回列表
items = soup.select("div.post > h2")
# 返回单个(第一个)
item = soup.select_one("div.post > h2")
print(item.get_text())
5.2 属性与伪类
python
# 属性选择
links = soup.select("a[href^='https']")
# nth-child
items = soup.select("ul > li:nth-child(2)")
5.3 bs4 注意点
select()返回 列表select_one()返回 单个元素或 None- 伪类支持有限(通常可用的是
:nth-child、:not等常用伪类)
6. 实战二:Selenium 中使用 CSS 选择器
6.1 基本用法
python
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com")
# 多元素
items = driver.find_elements(By.CSS_SELECTOR, "div.item")
# 单元素
item = driver.find_element(By.CSS_SELECTOR, "div.item")
注意:Selenium 正确写法是
By.CSS_SELECTOR,不是By.CSS。
6.2 在元素内继续定位
python
card = driver.find_element(By.CSS_SELECTOR, "div.card")
name = card.find_element(By.CSS_SELECTOR, ".name")
6.3 搭配显式等待
python
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 10)
items = wait.until(
EC.presence_of_all_elements_located((By.CSS_SELECTOR, "div.item"))
)
6.4 "方向/轴"场景示例
python
# 找标题后面的第一个段落(相邻兄弟)
content = driver.find_element(By.CSS_SELECTOR, "h2.title + p")
# 找标题后面所有段落(后续兄弟)
contents = driver.find_elements(By.CSS_SELECTOR, "h2.title ~ p")
7. 其他常见使用场景
-
Scrapy + CSS
pythonresponse.css("a::attr(href)").getall() -
Playwright
pythonpage.locator("div.item") -
浏览器控制台
jsdocument.querySelectorAll("div.item") -
前端样式规则调试(原始用途)
8. CSS 与 XPath 的取舍建议
| 需求 | 推荐 |
|---|---|
| 简单、稳定的 class/id 定位 | CSS |
| 需要向上找父级/祖先 | XPath |
| 需要复杂文本过滤 | XPath |
| 需要跨层级灵活定位 | XPath |
9. 常见坑与最佳实践
9.1 常见坑
#id只能匹配一个元素(理论上),但 HTML 可能不规范- 位置伪类
:nth-child是从 1 开始 :nth-child(n)以 所有子节点为基准,不是同标签
9.2 稳健性建议
- 优先使用唯一
id/data-* - 避免过长层级
- 尽量用结构关系(
>、+、~)替代下标
10. 一页速查表
css
/* 基本 */
#id
.class
Tag
/* 属性 */
[a]
[a="v"]
[a^="v"]
[a$="v"]
[a*="v"]
/* 方向 */
A B
A > B
A + B
A ~ B
/* 伪类 */
:first-child
:nth-child(2)
:not(.disabled)
11. 小结
- CSS 选择器是 DOM 树上的匹配规则,语法简洁、性能好
- "轴/方向"在 CSS 中用组合器 表达,但不支持向上
- bs4 / Selenium / Scrapy / Playwright 都天然支持 CSS
- 复杂定位建议结合 XPath 一起使用