Найкращі Rust HTML-парсери для вебскрапінгу

5 найкращих Rust HTML-парсерів для вебскрапінгу

Якщо ви користуєтеся Rust (як і я), вам потрібен парсер, який швидкий, безпечний і простий у роботі. На щастя, є кілька чудових бібліотек, створених саме для цього. У цьому посібнику я проведу вас через 5 найкращих HTML-парсерів для Rust, які я знайшов для вебскрапінгу. Я розповім, у чому сильні сторони кожного, де він поступається іншим і коли його варто використовувати.

Чому вам потрібен HTML-парсер у Rust

Коли ви отримуєте вебсторінку за допомогою Rust HTTP-клієнта, наприклад reqwest, ви отримуєте сирий HTML-вміст. Однак HTML непросто читати або переглядати за допомогою базових рядкових методів. HTML-парсери дають змогу вам:

  • Шукайте елементи за тегами або CSS-класами
  • Навігація між батьківсько-дочірніми зв’язками між елементами
  • Витягувати текст або значення атрибутів
  • Обробляйте пошкоджений або некоректно сформований HTML

Це особливо корисно під час вебскрапінгу реальних сайтів, які не завжди дотримуються ідеальних правил HTML.

Налаштування для тестування

Перш ніж ми перейдемо до окремих бібліотек, ось приклад асинхронної функції Rust, яку ми використовуємо, щоб отримати HTML із тестового вебсайту:

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let resp = reqwest::get("https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket/")
.await?
.text()
.await?;
println!("{}", resp);
Ok(())
}

Топ-5 найкращих Rust HTML-парсерів для вебскрапінгу

Let’s take a closer look at each HTML parser on the list. I’ll show you how they work with real-world HTML and how well they perform.

Scraper — Простий і надійний

Scraper це високорівнева бібліотека для парсингу HTML у Rust. Вона побудована на html5ever і selectors, а це означає, що вона швидка та створена для обробки неохайного HTML із реальних сайтів.

Key Features:

  • Інтерфейс високого рівня
  • Підтримка CSS-селекторів
  • Побудовано на основі реальної браузерної технології
  • HTML-дерево, подібне до DOM

Pros:

  • Дуже простий у використанні
  • Може обробляти пошкоджений або некоректний HTML
  • Активна розробка та підтримка спільноти
  • Гарна документація

Cons:

  • Споживає більше пам’яті на великих сторінках
  • Обмежена підтримка змінювання HTML

Example:

use scraper::{Html, Selector};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let resp = reqwest::get("https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket/")
.await?
.text()
.await?;
let fragment = Html::parse_document(&resp);
let selector = Selector::parse(".price").unwrap();
if let Some(element) = fragment.select(&selector).next() {
let price = element.text().collect::<String>();
println!("Price: {}", price);
}
Ok(())
}

Use Case: Найкраще підходить для більшості проєктів вебскрапінгу, особливо коли важливі простота використання та сумісність із реальними сайтами.

html5ever — швидкий і низькорівневий

Html5ever це швидкий і потужний HTML-парсер, написаний на Rust. Його створили як частину проєкту браузера Servo. Він не будує повне DOM-дерево. Натомість використовує модель токенайзера, видаючи кожен HTML-елемент як токен.

Key Features:

  • Відповідає стандарту HTML5
  • Дуже швидкий парсинг
  • Використовується в браузерних рушіях на кшталт Servo

Pros:

  • Надзвичайно швидкий і ефективний
  • Добре обробляє некоректний HTML
  • Повний контроль над HTML-структурою
  • Можливе низькорівневе налаштування

Cons:

  • Багатослівний і складніший у використанні
  • Без представлення DOM-дерева
  • Потрібно більше коду для вилучення конкретних даних

Example:

extern crate html5ever;
use html5ever::tendril::TendrilSink;
use html5ever::tokenizer::{Tokenizer, TokenSink, Token, TokenSinkResult, StartTag, EndTag, CharacterTokens, TokenizerOpts};
use html5ever::tokenizer::BufferQueue;
struct PriceFinder {
inside_price: bool,
result: String,
}
impl TokenSink for PriceFinder {
type Handle = ();
fn process_token(&mut self, token: Token, _line: u64) -> TokenSinkResult<()> {
match token {
Token::TagToken(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;
}
}
Token::CharacterTokens(t) => {
if self.inside_price {
self.result.push_str(&t);
}
}
_ => {}
}
TokenSinkResult::Continue
}
}

Use Case: Найкраще підходить для критично важливого за продуктивністю вебскрапінгу або коли вам потрібен повний контроль.

Select.rs — jQuery-Style Syntax

Select.rs є ще одним потужним HTML-парсером для Rust. Його знайомий синтаксис у стилі jQuery спрощує написання та розуміння селекторів.

Key Features:

  • Підтримує запити CSS і схожі на XPath
  • Дозволяє змінювати DOM
  • Повертає чисті формати виводу, як-от JSON і YAML

Pros:

  • Синтаксис у стилі jQuery
  • Зрозумілий для початківців
  • Може експортувати дані в кількох форматах
  • Вбудовані функції для пошуку та редагування вузлів

Cons:

  • Використовує більше пам’яті, ніж інші інструменти
  • Повільніший за деякі альтернативи для великих документів

Example:

use select::document::Document;
use select::predicate::{Class, Name};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let resp = reqwest::get("https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket/")
.await?
.text()
.await?;
let document = Document::from(resp.as_str());
if let Some(price_node) = document.find(Name("p").and(Class("price"))).next() {
println!("Price: {}", price_node.text());
}
Ok(())
}

Use Case: Чудово підходить користувачам, які надають перевагу синтаксису в стилі jQuery та хочуть виводити структуровані дані.

Kuchiki — DOM-подібне дерево

Kuchiki (японською «гниле дерево») — це цікавий та інтуїтивно зрозумілий HTML-парсер, який створює DOM-дерево, як у справжньому браузері. Він побудований на html5ever, але надає деревоорієнтований інтерфейс із селекторами у стилі CSS.

Key Features:

  • Підтримка DOM-дерева
  • CSS-селектори
  • Дозволяє обходити та змінювати

Pros:

  • Дуже легко зрозуміти
  • Повна структура дерева
  • Добра підтримка селекторів
  • Чудово підходить для обходу складного HTML

Cons:

  • Більше не підтримується активно (архівовано у 2023 році)
  • Більший розмір бінарного файлу через роботу з деревом

Example:

use kuchiki::parse_html;
use kuchiki::traits::*;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let resp = reqwest::get("https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket/")
.await?
.text()
.await?;
let document = parse_html().one(resp);
if let Ok(price_node) = document.select_first("p.price") {
println!("Price: {}", price_node.text_contents());
}
Ok(())
}

Use Case: Найкраще підходить для обходу дерева та простих HTML-завдань. Його слід уникати у великих проєктах через відсутність підтримки.

Pulldown-Cmark — Markdown насамперед

Pulldown-cmark не є HTML-парсером у традиційному сенсі. Його призначено для розбору Markdown, але його можна адаптувати для деяких завдань із розбору HTML. Він добре працює разом із такими інструментами, як html2md, які перетворюють HTML на Markdown.

Key Features:

  • Pull-парсер для Markdown
  • Надзвичайно низьке споживання пам’яті
  • Може перетворювати HTML → Markdown → розбирати текст

Pros:

  • Надзвичайно легкий
  • Ефективний для великих текстових файлів
  • Корисно, коли вам потрібен лише звичайний текст
  • Простий у використанні

Cons:

  • Не є справжнім HTML-парсером
  • Не підтримує CSS-селектори або DOM
  • Потрібні інші crates для обробки HTML

Example:

use pulldown_cmark::{Parser, Event};
use html2md;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let resp = reqwest::get("https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket/")
.await?
.text()
.await?;
let markdown = html2md::parse_html(&resp);
let parser = Parser::new(&markdown);
for event in parser {
if let Event::Text(text) = event {
if text.contains("$") {
println!("Price: {}", text);
}
}
}
Ok(())
}

Use Case: Чудово підходить для перетворення HTML-вмісту на Markdown для аналізу або відображення.

Performance Benchmark

Ми протестували кожен парсер на тій самій вебсторінці та в тому самому завданні з витягання даних (визначення ціни товару). Ось їхні середні результати (менше - краще):

These tests were run using criterion.rs on a Ryzen 9 machine. Real-world results may vary.

Коли використовувати кожен HTML-парсер

Conclusion

Rust has several capable HTML parsers for web scraping. Whether you want high performance, easy syntax, or advanced control, there’s a crate for you. If you’re just getting started, Scraper is a great balance of power and simplicity. If you care about raw performance and have time to write more code, go for html5ever. For structured output and jQuery-style selection, try Select.rs.

А якщо ви перетворюєте вміст на Markdown, Pulldown-cmark може здивувати вас швидкістю. Який би парсер ви не обрали, поєднуйте його з надійні проксі or API для вебскрапінгу сервісом на кшталт Bright Data або ScrapingBee. Це допоможе вашим скраперам уникати блокувань IP і ефективно масштабуватися!

 

Схожі записи