Python 爬虫解析库 parsel 教程

如果你在写 Python 爬虫时,觉得 BeautifulSoup 解析速度不够快,又不想为了一个高效的 Selector 去搭建 Scrapy 框架,那么今天介绍的这个库就是你的「救星」——parsel
它从 Scrapy 中抽离出来,继承了强大的选择器能力,同时保持了极简和轻量,让你在几分钟内就能上手,写出清晰、高效的解析代码。

1. 什么是 parsel?

parsel 并不是一个新项目,它是 Scrapy 官方拆分出来的核心解析库,原本就是 Scrapy 框架里的 Selector
它拥有 Scrapy Selector 的全部优势,同时可以独立安装、独立使用,非常适合:

  • 写小型爬虫
  • 数据清洗
  • 不想引入整个 Scrapy 但又要高性能解析的场景

parsel 的核心亮点:

  • 双引擎解析:底层基于 lxml,支持 XPath 1.0 和 CSS Selector(甚至支持 CSS→XPath 自动转换)
  • 三种提取模式:纯 CSS、纯 XPath、CSS 与 XPath 混合链式调用,想怎么切就怎么切
  • 极简且安全的 APIget()getall()get(default=…) 替代了过去繁琐的错误处理
  • 内置正则支持:不用先把内容取出来再单独做正则,直接在选择器上调用 .re().re_first()
  • 与 Scrapy 无缝对接:练手代码可以直接搬到 Scrapy 的 parse 方法里,零改动

2. 安装

只需要一条 pip 命令,parsel 会自动安装依赖的 lxml:

pip install parsel

安装完成后,就可以在任何 Python 脚本中使用了。

3. 快速上手

我们通过一段模拟的 HTML 内容,演示 parsel 最常用的提取方式。

3.1 创建 Selector 对象

拿到 HTML 文本后,用 parsel.Selector(text=…) 包装即可。parsel 底层会借助 lxml 自动处理闭合标签、编码等问题。

from parsel import Selector

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>
"""

sel = Selector(text=demo_html)

3.2 用 CSS 选择器提取

如果你有前端基础,CSS 选择器是最友好的方式,写法和你平时写样式几乎一样。

# 选中所有带 active 类的 li
active_items = sel.css('.content-item.active')
# 取第一个匹配结果(get() 是 parsel 独有的便捷方法)
print(active_items.get())

# 提取标题文本 ::text 是 parsel 对 CSS 的扩展,专门用来获取节点内的文本
title = sel.css('#main-title::text').get()
print("标题是:", title)

3.3 用 XPath 提取

遇到复杂的嵌套关系、需要定位兄弟节点或祖先节点时,XPath 会更加灵活。

# 获取 class 包含 active 且包含 item-0 的 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 内部的文本(无论嵌套多少层)
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() 获取当前节点及所有子孙节点的文本片段(返回列表)。
# CSS 获取直接子节点的文本
direct_text = sel.css('.dynamic-tag > span::text').get()
print("直接子文本:", direct_text)

# 获取所有内部文本(列表),然后拼接成一个干净字符串
raw_list = sel.css('.dynamic-tag ::text').getall()
clean_text = ''.join(raw_list).strip()
print("拼接后的干净文本:", clean_text)

4.2 提取属性的多种写法

parsel 支持多种风格提取属性,你可以按自己的习惯选择。

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

# XPath 写法
data_id_xpath = sel.xpath('//div[@class="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. 内置正则,一步提取复杂内容

当你想从文本或属性里提取手机号、价格、编号等特定格式的内容时,可以直接在选择器上调用 .re().re_first(),不用再自己写一堆后处理逻辑。

# 提取所有 li 中 item- 后面的数字
all_item_nums = sel.css('.content-item').re(r'item-(\d)')
print("所有列表项编号:", all_item_nums)

# 提取第一个 active li 中 item- 后面的数字,未匹配时返回默认值
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 处理内部细节,代码既好读又高效。

# 在 #content-list 里面找到所有带 active 类的 li,再取内部 a 标签的 href
hrefs = sel.css('#content-list li.active').xpath('.//a/@href').getall()
print("激活项的所有链接:", hrefs)

6.2 安全处理缺失值

parsel 的 .get(default=…).re_first(default=…) 可以让你彻底告别 try-except 的麻烦,即使元素不存在也不会中断爬虫程序。

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

6.3 XPath 轴操作:定位兄弟/祖先节点

复杂页面中经常需要找「隔壁的兄弟」或「父级容器」,XPath 轴可以轻松搞定。

# 第一个 li 后面所有的兄弟 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 和 Scrapy 的 Selector 接口完全一致,练手时写的解析代码可以直接复制到 Scrapy 的爬虫里使用。

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

# 在 Scrapy 的 Spider 里(response 自带 selector,无需手动创建)
def parse(self, response):
    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. 善用内置正则 .re().re_first() 提取复杂内容;
  4. 练习代码直接迁移到 Scrapy,无缝衔接正式项目。

如果想了解更多细节,可以查阅官方文档:parsel 官方文档