kp-003

HTML/DOM 与 CSS 选择器思维

入门 ≈ 25 分钟 HTMLDOMCSS选择器解析

前置知识

本文基于模型知识整理,建议核对官方文档(见 参考资料)。

一句话定义

HTML 是描述页面结构的标记语言,浏览器将其解析为 DOM 树;CSS 选择器是在这棵树上「指哪打哪」的定位语法,也是爬虫抽取字段的基本功。

为什么重要

字段抽取的本质是「在树上找节点、读文本/属性」。选择器写得准,解析代码就短;选择器思维不牢,面对嵌套页面会反复试错。它同时是 kp-007(BS4 解析)、kp-008(XPath 进阶)、kp-012(渲染后抓取)的共同地基。

前置知识

kp-002(知道响应体里是 HTML)。

核心概念

  • 元素/标签/属性:<a href="/p/1" class="title"> 中 a 是标签,href/class 是属性。
  • DOM 树:浏览器把 HTML 解析成以节点为单位的树;解析库(BS4/lxml)构建的是同构的树。
  • 语义化定位锚点:class、id、data-* 属性是定位首选锚点。
  • CSS 选择器:tag、.class、#id、A B(后代)、A > B(直接子代)、[attr=值]、:nth-child(n)。

原理与机制

选择器从右往左匹配(浏览器优化思路,解析库同理):先找出所有候选节点,再向上验证祖先链。理解这一点能解释「.list .item 与 .list > .item 为什么结果不同」。

html
└── body
    └── div.product-list          ← .list
        ├── div.product           ← .item(直接子代)
        │   ├── a.title
        │   └── span.price
        └── section.recommend     ← 间接子代
            └── div.product       ← 用 > 时不会被选中

定位字段的标准工作流(在浏览器 DevTools 中完成):

  1. 右键目标内容 → 检查,定位到元素。
  2. 在 Elements 面板回溯到「最小重复单元」(一条列表项)。
  3. 找出该单元稳定的 class/结构特征,写出选择器。
  4. 在 Console 用 document.querySelectorAll(sel).length 验证命中数量。

实例或案例

在练习站 books.toscrape.com 上抽取书名与价格:

# 选择器:article.product_pod(每本书)→ h3 a(书名)→ p.price_color(价格)
# 验证:document.querySelectorAll("article.product_pod").length → 20

每个 article.product_pod 就是一个可循环单元,字段从单元内部相对定位——这是所有列表页解析的通用模式。

常见误区

  • 误区一:把「渲染后的 DOM」当成「源码里的 HTML」。 DevTools 看到的是 JS 执行后的树;若源码(右键查看源代码)里没有该节点,说明内容是动态渲染的,需走 kp-012 的路线。判断方法:Ctrl+U 查看源码搜索关键字。
  • 误区二:选择器依赖绝对路径。 body > div:nth-child(3) > div > ul > li 一改版就断;优先选语义 class 与 data-* 属性。
  • 误区三:用正则解析 HTML。 HTML 不是正则可稳定解析的语言,嵌套与属性变化会制造无数边角案例;交给解析库。

自测题

  1. .list .item 与 .list > .item 的区别?

答:前者匹配任意深度的后代,后者只匹配直接子元素;后者更抗「嵌套区域内出现同名 class」的干扰。

  1. 如何快速判断内容是否为服务端渲染?

答:查看页面源代码(而非 DevTools Elements)搜索目标文本;搜到=服务端渲染,搜不到=前端渲染。

  1. 为什么优先用 class/data-* 而不是 nth-child?

答:语义锚点表达「是什么」,位置锚点表达「排第几」;页面增删节点后前者仍成立。

与其他知识点的关系

kp-007 把选择器变成 Python 调用;kp-008 扩展到 XPath 的文本函数与轴;kp-012 处理「源码里没有」的那一半世界。

延伸阅读

MDN「CSS 选择器」参考页;Chrome DevTools 官方文档的 Elements 面板一节。

相关知识点

学习进度