5 款最适合网络抓取的 Rust HTML 解析器
如果你像我一样在用 Rust,你会希望找一个既快速、安全,又易于使用的解析器。幸运的是,市面上正好有几款专为这个目的打造的优秀库。在这篇指南里,我会带你了解我找到的 5 个最好的 Rust HTML 解析器,用于网页抓取。我会分享每个解析器各自擅长什么、短板在哪里,以及什么时候该用它。
为什么需要使用 Rust 的 HTML 解析器?
使用 Rust HTTP 客户端(如 reqwest)获取网页时,会收到原始的 HTML 内容。然而,使用基本的字符串方法来阅读或浏览 HTML 并不容易。HTML 解析器可以让你
- 使用标记或 CSS 类搜索元素
- 导航元素之间的父子关系
- 提取文本或属性值
- 处理破损或畸形的 HTML
这一点在搜索现实世界的网站时尤其有用,因为这些网站并不总是遵循完美的 HTML 规则。
测试设置
在深入研究各个库之前,下面是我们用来从测试网站获取 HTML 的异步 Rust 函数示例:
#[tokio::main]
async fn main() -> 结果<(), 盒子<动态 std::error::Error>> {
let 呼吸 = reqwest::get("https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket/")
.await?
.text()
.await?;
println!("{}",resp);
好的(())
}
最适合网络抓取的 5 款 Rust HTML 解析器
让我们仔细看看列表中的每个 HTML 解析器。我将向你展示它们是如何处理真实世界中的 HTML 的,以及它们的性能如何。
Scraper - 简单可靠
Scraper 是一个使用 Rust 的高级 HTML 解析库。它建立在 html5ever 和选择器之上,这意味着它速度极快,可处理现实世界中杂乱无章的 HTML。
主要功能
- 高级界面
- 支持 CSS 选择器
- 基于真实浏览器技术
- 类似 DOM 的 HTML 树
优点
- 非常易于使用
- 可处理破损或无效的 HTML
- 积极发展和社区支持
- 良好的文档
缺点
- 大页面占用更多内存
- 对修改 HTML 的支持有限
例如
使用 scraper::{Html, Selector};
#[tokio::main]
async fn main() -> 结果<(), 盒子<动态 std::error::Error>> {
let 呼吸 = reqwest::get("https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket/")
.await?
.text()
.await?;
let 片段 = Html::解析文档(&resp);
let 选举人 = 选择器::parse(".价格").拆开();
if let 一些(元素)=片段。遴选(选择器)。下一个() {
let price = 元素。text().collect::<String>();
println!("价格:{}", 价格);
}
好的(())
}
使用案例: 最适合大多数网络搜索项目,尤其是对易用性和现实世界兼容性要求较高的项目。
html5ever - 快速、低级

Html5ever 是一个用 Rust 编写的快速而强大的 HTML 解析器。它是 Servo 浏览器项目的一部分。它不会构建完整的 DOM 树。相反,它使用标记符模型,将每个 HTML 元素作为一个标记符输出。
主要功能
- 符合 HTML5 标准
- 快速解析
- 用于伺服等浏览器引擎
优点
- 速度极快,效率极高
- 良好处理畸形 HTML
- 全面控制 HTML 结构
- 可进行低级定制
缺点
- 冗长且难以使用
- 无 DOM 树表示
- 需要更多代码来提取特定数据
例如
外部 条板箱 html5ever;
使用 html5ever::tendril::TendrilSink;
使用 html5ever::tokenizer::{Tokenizer, TokenSink, Token, TokenSinkResult, StartTag, EndTag, CharacterTokens, TokenizerOpts};
使用 html5ever::tokenizer::BufferQueue;
结构 价格查询 {
内部价格: bool,
结果 String,
}
内含 令牌水槽 for 价格查询 {
type 手柄 = ();
fn 过程令牌(&突变 self, token:token, _line: u64) -> 中国okenSinkResult {
match 令牌 {
令牌::标签令牌(tag) => {
if tag.kind == StartTag && tag.name.as_ref() == "p" {
for attr in tag.attrs {
if attr.name.local.as_ref() == "class" && attr.value.as_ref() == "price" {
self.inside_price = true;
}
}
} else if tag.kind == EndTag && tag.name.as_ref() == "p" {
self.inside_price = false;
}
}
令牌::角色代币(t) => {
if self.inside_price {
self.结果.push_str(&t);
}
}
_ => {}
}
令牌汇结果::继续
}
}
使用案例: 最适用于对性能要求极高的刮擦或需要完全控制的情况。
Select.rs - jQuery 风格语法
选择.rs 是 Rust 另一个功能强大的 HTML 分析器。其熟悉的 jQuery 风格语法使其易于编写和理解选择器。
主要功能
- 支持 CSS 和 XPath 类查询
- 允许修改 DOM
- 返回简洁的输出格式,如 JSON 和 YAML
优点
- jQuery 风格语法
- 适合初学者
- 可导出多种格式的数据
- 用于查找和编辑节点的内置功能
缺点
- 比其他工具占用更多内存
- 在处理大型文档时比某些替代方案慢
例如
使用 select::document::Document;
使用 select::predicate::{Class, Name};
#[tokio::main]
async fn main() -> 结果<(), 盒子<动态 std::error::Error>> {
let 呼吸 = reqwest::get("https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket/")
.await?
.text()
.await?;
let document = Document::from(resp.as_str());
if let 一些(price_node) = document.find(名称("p").和(班级("price"))).下一个() {
println!("价格:{}", price_node.text());
}
好的(())
}
使用案例: 非常适合喜欢 jQuery 风格语法并希望输出结构化数据的用户。
Kuchiki - 类 DOM 树
Kuchiki (日语的意思是“腐烂的树”)是一款有趣而直观的 HTML 解析器,它能像真正的浏览器一样创建 DOM 树。它基于 html5ever 构建,但提供了一个基于树的界面和 CSS 风格的选择器。
主要功能
- DOM 树支持
- CSS 选择器
- 允许遍历和修改
优点
- 非常容易理解
- 完整的树形结构
- 良好的选择器支持
- 非常适合遍历复杂的 HTML
缺点
- 不再主动维护(2023 年归档)
- 由于树形处理,二进制大小更大
例如
使用 kuchiki::parse_html;
使用 kuchiki::traits::*;
#[tokio::main]
async fn main() -> 结果<(), 盒子<动态 std::error::Error>> {
let 呼吸 = reqwest::get("https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket/")
.await?
.text()
.await?;
let document = parse_html().一种(resp);
if let 好的(price_node) = document.选择第一("p.price") {
println!("价格:{}", price_node.文本内容());
}
好的(())
}
使用案例: 它最适合树遍历和简单的 HTML 任务。由于缺乏支持,大型项目应避免使用。
Pulldown-Cmark — Markdown 优先
Pulldown-cmark 并不是传统意义上的 HTML 解析器。它旨在解析 Markdown,但也可用于某些 HTML 解析任务。当它与 html2md 等将 HTML 转换为 Markdown 的工具结合使用时,效果会很好。
主要功能
- 为 Markdown 拉取解析器
- 内存使用率极低
- 可以将 HTML 转换为 Markdown → 再解析文本
优点
- 重量超轻
- 高效处理大型文本文件
- 只需要纯文本时很有用
- 使用简单
缺点
- 不是真正的 HTML 解析器
- 缺乏 CSS 选择器或 DOM 支持
- 需要其他板块来处理 HTML
例如
使用 pulldown_cmark::{Parser, Event};
使用 html2md;
#[tokio::main]
async fn main() -> 结果<(), 盒子<动态 std::error::Error>> {
let 呼吸 = reqwest::get("https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket/")
.await?
.text()
.await?;
let 减价 = html2md::parse_html(&resp);
let 解析器 = 解析器::new(标记);
for 事件 in 解析器 {
if let 活动::文本(文本)= 事件 {
if 文本载有("$") {
println!("价格:{}",文本);
}
}
}
好的(())
}
使用案例: 非常适合将 HTML 内容转换为 Markdown,以便分析或显示。
性能基准
我们使用相同的网页和提取任务(提取产品价格)对每个解析器进行了测试。以下是它们的平均表现(越低越好):

这些测试是在 Ryzen 9 机器上使用 criterion.rs 进行的。实际结果可能有所不同。
何时使用每种 HTML 解析器

结论
Rust 提供了多种适用于网页抓取的 HTML 解析器。无论你想要高性能、简单语法还是高级控制,总有一款适合你。如果你刚刚入门,Scraper 可以很好地兼顾强大与简单。如果你注重原始性能,并且有时间编写更多代码,那就选择 html5ever。如果想获得结构化输出和 jQuery 风格的选择,可以试试 Select.rs。
如果你要将内容转换为 Markdown,Pulldown-cmark 的速度可能会让你大吃一惊。无论选择哪种解析器,都要将其与 可靠的代理 或 抓取API 例如 Bright Data 或 ScrapingBee。这样可以确保你的爬虫避免 IP 封禁,并能有效扩展规模!

