介绍
传统爬虫的工作流程通常始于编写代码来定义采集规则,随后程序会向目标网页发送请求以获取原始数据,接着对返回的页面内容进行解析并提取所需信息,若存在多页数据则需自动处理分页逻辑以遍历所有目标页面,最终将清洗整理后的结构化数据存储到数据库中,从而完成从网页到数据的全自动化采集过程。
-
发送 HTTP 请求获取网页资源。
-
解析 HTML 内容提取有用信息。
-
将数据存储到本地或数据库。
-
处理提取到的链接加入待抓取队列。
关于解析
程序会先用解析器把 HTML 构建成一棵文档树,比如 div,a 这些标签会形成层级关系;然后再通过 CSS 选择器、XPath 或正则表达式,从这棵树里定位目标节点,取出其中的文本、链接、图片地址、属性值等有用信息。
常见方式大致有三类:
| 方式 | 适合做什么 | 注意点 |
|---|---|---|
| CSS 选择器 | 按 class、id、标签、属性快速定位元素 |
写法短、直观,适合结构稳定的页面 |
| XPath | 按层级、父节点、文本内容精确定位 | 表达能力更强,但语法稍复杂 |
| 正则表达式 | 从纯文本里抠手机号、编号、JSON 片段等 | 不适合直接解析完整 HTML,容易因页面变化失效 |
DOM 树
写爬虫时,很多人会被 DOM 树吓到,觉得它属于前端知识。其实对爬虫来说,你不需要掌握 DOM API、事件冒泡、浏览器渲染机制,也不需要理解 JavaScript 如何操作页面。你只需要学会一件事:把 HTML 看成一棵有层级关系的树,然后顺着树找到目标数据。
HTML 被解析后,会形成一棵文档树。每个标签都是一个节点,标签之间通过嵌套形成父子、兄弟、祖先、后代关系。属性挂在标签上,文本则藏在标签内部。理解了这些,你写 CSS 选择器或 XPath 时就不会只是"照着抄",而是能真正判断该往哪里定位。
<div class="news-list">
<article class="news-item">
<h2>
<a href="/news/123">标题</a>
</h2>
<span class="time">2026-10-13</span>
</article>
</div>
你需要知道:
-
div是外层容器 -
article是div的子节点 -
h2和span是同级 -
a是h2的子节点 -
文本
"标题"属于a -
href="/news/123"是a的属性
CSS Selector
CSS Selector 原本是前端用来给页面元素设置样式的,但在爬虫里,它几乎是最直观的 HTML 定位方式。你不需要写复杂的语法,只要像读句子一样,把元素的层级、类名、标签、属性串起来,就能精准找到目标数据。
先看几个常见的写法:
| 选择器 | 含义 |
|---|---|
.news-item |
找 class 包含 news-item 的元素 |
.news-item h2 a |
在 .news-item 下面,找 h2 里面的 a |
article.news-item |
找标签是 article、且 class 包含 news-item 的元素 |
a[href] |
找带有 href 属性的 a 标签 |
span.time |
找标签是 span、且 class 包含 time 的元素 |
重点理解 .news-item h2 a
这个选择器可以拆成三段:
.news-item h2 a
↓ ↓ ↓
外层容器 中间层 目标元素
它表示:
先找到 class 为
news-item的元素,再在这个元素内部,找到h2标签,最后找到h2里面的a标签。
结合前面的 HTML:
html
<div class="news-list">
<article class="news-item">
<h2>
<a href="/news/123">标题</a>
</h2>
<span class="time">2026-10-13</span>
</article>
</div>
.news-item h2 a 会命中:
html
<a href="/news/123">标题</a>
这里的空格表示"后代关系",也就是说,a 不一定要直接紧挨着 h2,只要它在 h2 内部就可以了。
和 XPath 相比,CSS Selector 更短、更直观,适合结构清晰的页面;XPath 表达能力更强,比如可以按文本内容定位、向上找父节点,但语法会更复杂一些。日常写爬虫,先掌握 CSS Selector 已经能解决大部分问题。
XPath
如果说 CSS Selector 是直观的"视觉定位",那 XPath 就是一把功能更强的"手术刀"。它原本是为 XML 文档设计的查询语言,但在 HTML 解析中同样强大。XPath 的最大优势在于表达能力极强------它不仅能向下查找,还能向上回溯、按文本内容筛选、甚至进行逻辑运算。当 CSS Selector 搞不定的复杂场景(比如"查找包含特定文字的按钮"或"定位父级元素")时,XPath 往往是最佳解法。
核心语法速查表
你不需要背诵复杂的规范,掌握下面这几招就能覆盖 90% 的爬虫需求:
| 语法 | 含义 | 示例 |
|---|---|---|
//tag |
在整个文档中查找所有该标签 | //a 查找所有链接 |
/ |
选取直接子节点 | //div/a 查找 div 下的直接链接 |
// |
选取后代节点(子孙均可) | //div//a 查找 div 内所有层级的链接 |
[@attr='val'] |
属性筛选 | //div[@id='main'] |
text() |
获取标签内的文本内容 | //h2/text() |
contains() |
模糊匹配(属性或文本) | //div[contains(@class, 'news')] |
结合实例理解
回到我们熟悉的 HTML 结构:
html
<div class="news-list">
<article class="news-item">
<h2>
<a href="/news/123">标题</a>
</h2>
<span class="time">2026-10-13</span>
</article>
</div>
1. 基础定位
html
//article[@class='news-item']//a
这句话的意思是:先在全文找到 class 为 news-item 的 article 标签,然后在它内部任意层级找到 a 标签。
2. 提取文本
html
//article[@class='news-item']/h2/a/text()
这会直接提取出文本内容:标题。注意这里用了单斜杠 /,表示严格的父子层级关系。
3. 模糊匹配(实战神器)
html
//span[contains(@class, 'time')]
很多网站为了反爬,class 名会写成 time-wrapper 或 time-box。用 contains 就能轻松命中包含 time 字符的 class。
什么时候必须用 XPath?
虽然 CSS Selector 写起来更顺手,但在以下场景 XPath 是无可替代的:
-
按文本查找 :比如
//button[text()='下一页'],CSS 做不到。 -
向上查找父节点 :比如
//a[@href='...']/../..,找到链接的祖父节点。 -
复杂的逻辑组合 :比如
//div[@id='list' and @data-type='news']。
正则表达式
正则表达式(Regular Expression)在爬虫里的定位非常明确:它不适合直接解析 HTML 结构,但极其擅长处理已经提取出来的文本片段。
为什么不用正则解析 HTML?
HTML 是树状嵌套结构,而正则是基于线性文本匹配的。用正则硬扒整个 HTML 会遇到很多坑:
-
标签嵌套层级复杂,正则难以准确匹配开闭标签;
-
属性顺序不固定,
class="a" id="b"和id="b" class="a"会让同一套正则失效; -
页面稍有改动,正则就可能全盘崩溃。
所以业界共识是:结构问题交给 DOM(CSS/XPath),文本问题交给正则。
正确的使用姿势
典型的爬虫解析流程是这样的:
HTML 源码
↓ CSS/XPath 定位节点
拿到目标文本(如 "发布时间:2026年10月13日 15:30")
↓ 正则进一步清洗
提取纯数据(如 "2026-10-13 15:30")
常用正则模式速查
表格
| 需求 | 正则写法 | 匹配示例 |
|---|---|---|
| 4位数字 | \d{4} |
2026 |
| 1~2位数字 | \d{1,2} |
1 ~ 12 |
| 中文日期 | \d{4}年\d{1,2}月\d{1,2}日 |
2026年10月13日 |
| 手机号 | 1\d{10} |
13812345678 |
| 邮箱 | \w+@\w+\.\w+ |
test@example.com |
| 非贪婪匹配 | .*? |
匹配最短内容 |
实战示例
假设你已经用 CSS Selector 拿到了这段文本:
html
发布时间:2026年10月13日 15:30
用正则提取日期和时间:
html
import re
text = "发布时间:2026年10月13日 15:30"
match = re.search(r"(\d{4}年\d{1,2}月\d{1,2}日)\s*(\d{1,2}:\d{2})", text)
if match:
date = match.group(1) # "2026年10月13日"
time = match.group(2) # "15:30"
一句话总结
CSS/XPath 负责"找到在哪里",正则负责"抠出是什么"。 两者配合,才是爬虫解析的最佳实践。
解析的产物:先拿到"节点",再提炼出"数据"
很多初学者会以为爬虫解析的目标就是"把文字抠出来",其实不是。解析器(lxml、parsel、BeautifulSoup)真正返回给你的,是两个层次的东西:一个是节点对象 ,另一个是你最终想要的业务数据。理解这两个层次,才算真正打通了"解析 → 入库"的链路。
第一类:节点对象(中间产物)
当你用 CSS Selector 或 XPath 定位时,命中的往往不是一个字符串,而是一个节点对象。比如你定位到:
<a href="/news/123">周杰伦发布新歌</a>
解析器返回的是一个代表这个 `` 标签的对象。这个对象的价值在于它是"活的"------你还能继续从它身上取各种信息:
| 你想拿的东西 | 从节点上怎么取 |
|---|---|
| 文本内容 | 周杰伦发布新歌 |
| 属性值 | href="/news/123" |
| 子节点 | 该标签内部的嵌套标签 |
| 父节点 | 它的上一层容器 |
| 整段 HTML | 连标签一起的原始结构 |
换句话说,节点对象是一个"可继续深挖的入口"。定位这一步只是把范围缩小到某个节点,真正的字段提取是在这个节点上二次操作完成的。这也是为什么 XPath 里有 text()、@href 这些函数------它们就是挂在节点对象上的取值手段。
第二类:业务数据(最终产物)
节点只是手段,你真正要的是把它拆成一个个有语义的字段。一条新闻里常见的业务数据包括:
文本 title = "周杰伦发布新歌" 链接 url = "/news/123" 图片 image = "https:1.jpg" 时间 publishTime = "2026-10-13" 作者 author = "某记者" 正文 content = "……" 属性 data-id = "123"
这一步做的事情其实就是:从节点对象上,把文本、属性、子节点分别取出来,再做去空格、补全域名、格式清洗等处理,把"网页上的一块结构"翻译成"程序里的一段干净数据"。
最后:整理成结构化对象
零散字段单独放着没法用,爬虫的收尾工作是把它们归拢成一个结构化对象------通常是字典或对象,一行对应一条数据:
javascript
{
"title": "周杰伦发布新歌",
"url": "https://xxx.com/news/123",
"publishTime": "2026-10-13T15:30:00+08:00",
"author": "某记者",
"content": "......"
}
到这一步,解析就彻底完成了:HTML 树 → 定位到节点 → 从节点取字段 → 清洗 → 装进结构化对象。这个对象就是要送去"入库"的东西------存进 MySQL、MongoDB 还是导出成 CSV,都以它为标准形态。