Python 爬虫解析库 parsel 教程

当我们写 Python 爬虫时,解析 HTML/XML 绕不开选择工具。如果觉得 BeautifulSoup 速度慢,Scrapy 自带 Selector 又不想搭框架麻烦?那 Scrapy 底层「抠出来」的轻量级独立库 parsel 绝对是你的菜!支持 XPath+CSS 混合写、内置正则、API 极简、性能炸场,从入门到落地 5分钟就能上手。

1. 快速了解 parsel

它不是新东西,是 Scrapy 官方拆分出来的核心解析库,完美继承了 Scrapy Selector 的所有优势,还不用装整个 Scrapy,是日常小爬虫、数据清洗 HTML 切片工具的首选。

核心优势再列一遍更直观:

  • ✅ **双引擎解析:lxml 做底层,支持 XPath 1.0 和 CSS Selector (甚至支持 CSS→XPath 自动转换)
  • ✅ **三提取模式:纯 XPath、纯 CSS、CSS+XPath 混合链、原生正则随意切
  • ✅ **极简安全 API:get()/getall()/get(default=) 替代了复杂的错误处理
  • ✅ **兼容 Scrapy:代码直接迁移 Scrapy 里零改动直接用,练手→生产无缝衔接

2. 一键安装

只需要 pip 一条命令,自动拉取依赖的 lxml 库:

pip install parsel

3. 从 0 到 1 跑通

3.1 构建选择器实例

拿到要解析的内容后,直接用 parsel.Selector(text=) 包装就行,默认解析 HTML 时会自动补全闭合标签、处理编码问题(lxml 做的脏活累活):

from parsel import Selector

# 模拟一段要解析的电商/新闻类 demo HTML
demo_html = """
<html>
    <body>
        <h1 id="main-title">Parsel 轻量级解析实战</h1>
        <ul id="content-list">
            <li class="content-item item-0">first item</li>
            <li class="content-item item-1"><a href="link2.html">second item</a></li>
            <li class="content-item item-0 active"><a href="link3.html"><span>third item</span></a></li>
            <li class="content-item item-1 active"><a href="link4.html">fourth item</a></li>
            <li class="content-item item-0">fifth item</li>
        </ul>
        <div data-id="12345" class="dynamic-tag">
            <span>隐藏的产品ID是:</span>12345
        </div>
    </body>
</html>
"""

# 初始化 Selector
sel = Selector(text=demo_html)

3.2 先用最友好的 CSS Selector 定位

喜欢前端或者刚学爬虫的同学优先选 CSS 语法,简单直观,写法和前端样式完全一致。

# 定位所有带 active 类的 li
active_items = sel.css('.content-item.active')
# 只取第一个(注意 get() 是 parsel 独创的,替代了 Scrapy 旧的 extract_first()
print(active_items.get())

# 定位 ID 为 main-title 的标题,取文本
# ::text 是 parsel 对 CSS 的扩展,专门取节点文本
title = sel.css('#main-title::text').get()
print("标题是:", title)

3.3 进阶用 XPath 搞复杂操作

如果遇到多层嵌套、兄弟节点、祖先节点定位,或者 CSS 搞不定的,直接上 XPath 1.0,lxml 原生支持:

# 定位第三个内容列表里带 active 类的 li 下的 a 标签的 href
href_xpath = sel.xpath('//ul[@id="content-list"]/li[contains(@class, "active") and contains(@class, "item-0")]/a/@href').get()
print("XPath 提取的链接:", href_xpath)

# 取所有 li 的文本(不管嵌套多少层 span/a 用 //text()
all_raw_text = sel.xpath('//ul[@id="content-list"]//text()').getall()
print("所有原始文本列表:", all_raw_text)

4. 核心提取方法大揭秘

不管用 CSS 还是 XPath 定位,最后提取都是用这几个通用方法:

方法作用
.get()提取第一个匹配结果,匹配不到返回 None
.get(default=xxx)提取第一个匹配结果,匹配不到返回自定义的默认值,再也不怕报错!
.getall()提取所有匹配结果,返回一个列表,匹配不到返回空列表

4.1 文本提取的细节

  • CSS 要用扩展语法 ::text 单独取当前节点文本,::attr(属性名) 取属性;
  • XPath 用 /text() 取当前节点直接文本,//text() 取当前节点及所有子节点的文本拼接前的列表;
# 1. 取直接子节点纯文本(CSS
direct_text = sel.css('.dynamic-tag > span::text').get()
print("直接子文本:", direct_text)

# 2. 取所有子文本的列表,然后用 join 拼接(更干净
raw_list = sel.css('.dynamic-tag ::text').getall()
clean_text = ''.join(raw_list).strip()
print("拼接后的干净文本:", clean_text)

4.2 属性提取的对比

两种写法都很常用,看哪个顺手:

# CSS 写法
data_id_css = sel.css('.dynamic-tag::attr(data-id)').get()

# XPath 写法
data_id_xpath = sel.css('.dynamic-tag/@data-id').get()

# 这里甚至可以用混合链简化:CSS 定位后接 XPath 属性
data_id_mix = sel.css('.dynamic-tag').xpath('./@data-id').get()
print("三种方式结果一致:", data_id_css == data_id_xpath == data_id_mix)

5. 内置正则表达式直接提复杂文本

有时候文本或者属性里的内容太散,比如价格、手机号、邮箱,直接用正则就行,不用先提出来再处理:

# 5.1 用 .re(正则) 提取所有匹配结果,返回列表
all_item_nums = sel.css('.content-item').re(r'item-(\d)')
print("所有列表项编号:", all_item_nums)

# 5.2 用 .re_first(正则, default=xxx) 提取第一个匹配结果,安全无报错
first_active_num = sel.css('.content-item.active').re_first(r'item-(\d)', default='无匹配')
print("第一个激活项的编号:", first_active_num)

6. 几个实用的小技巧

6.1 链式调用(混合 CSS/XPath 更爽

先写简单直观的 CSS 定位大区域,再写精准的 XPath 处理小细节,效率和可读性拉满:

# 从内容列表里,取所有带 active 类的 li 的 href
hrefs = sel.css('#content-list li.active').xpath('.//a/@href').getall()
print("激活项的所有链接:", hrefs)

6.2 处理缺失值

.get(default=).re_first(default=) 绝对是爬虫避免 KeyError/IndexError 的神器,再也不用写 try-except 了:

# 假设我们要找一个不存在的 li 的 href
missing_href = sel.css('.content-item.missing::attr(href)').get(default='https://example.com')
print("安全的默认链接:", missing_href)

6.3 定位特定元素的兄弟/祖先节点

用 XPath 轴操作,复杂定位不再愁:

# 第一个 li 的**后面所有兄弟节点
following_li = sel.css('#content-list li:first-child').xpath('./following-sibling::li').getall()
print("第一个 li 后面的所有兄弟:", len(following_li))

# 带 span 的 a 标签的**父级 li
parent_li = sel.css('#content-list li span').xpath('./ancestor::li[1]').get()
print("带 span 的父级 li:", parent_li)

7. 与 Scrapy 无缝迁移

练手用 parsel,生产直接复制粘贴代码:

# 1. 练手代码(parsel
# from parsel import Selector
# sel = Selector(text=demo_html)
# hrefs = sel.css('#content-list li.active').xpath('.//a/@href').getall()

# 2. 生产代码(Scrapy
from scrapy.selector import Selector

# 假设在 Scrapy Spider 的 parse 方法里
def parse(self, response):
    # response 自带 selector,甚至不用自己初始化!
    hrefs = response.css('#content-list li.active').xpath('.//a/@href').getall()
    for href in hrefs:
        yield response.follow(href, callback=self.parse_detail)

8. 简单总结

parsel 是轻量级、高性能、灵活度拉满的 HTML/XML 解析工具,从入门到落地只需要掌握:

  1. 会写简单的 CSS 定位,必要时加 XPath 轴
  2. 记住 .get()/.get(default=)/.getall() 三个提取方法
  3. 内置正则直接提复杂内容
  4. 练手→生产无缝衔接 Scrapy

想深入了解的同学可以看官方文档:parsel 官方文档