用于网络抓取的最佳 Rust HTML 解析器

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 封禁,并能有效扩展规模!

 

类似文章