Лучшие HTML-парсеры на Rust для веб-скрейпинга

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

Если вы используете Rust, как и я, вам нужен парсер, который быстрый, безопасный и удобный в работе. К счастью, есть несколько отличных библиотек, созданных именно для этого. В этом руководстве я рассмотрю 5 лучших HTML-парсеров на Rust, которые я нашёл для веб-скрейпинга. Я расскажу, чем хорош каждый из них, где у него слабые места и когда его стоит использовать.

Зачем нужен HTML-парсер на Rust

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

  • Ищите элементы по тегам или CSS-классам
  • Переход по связям между родительскими и дочерними элементами
  • Извлекать текст или значения атрибутов
  • Обрабатывайте битый или некорректно сформированный HTML

Это особенно полезно при скрапинге реальных сайтов, которые не всегда следуют идеальным правилам HTML.

Настройка для тестирования

Прежде чем перейти к отдельным библиотекам, вот пример асинхронной функции на Rust, которую мы используем для получения HTML с тестового сайта:

#[tokio::main]
async fn главная() -> Result<(), Box<dyn std::error::Error>> {
пусть resp = reqwest::получить("https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket/")
.ожидайте?
.текст()
.ожидайте?;
println!("{}", resp);
Ok(())
}

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

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

Плюсы:

  • Очень прост в использовании
  • Может обрабатывать невалидный или поврежденный HTML
  • Активная разработка и поддержка сообщества
  • Хорошая документация

Конс:

  • Потребляет больше памяти на больших страницах
  • Ограниченная поддержка изменения HTML

Пример:

использовать scraper::{Html, Selector};
#[tokio::main]
async fn главная() -> Result<(), Box<dyn std::error::Error>> {
пусть resp = reqwest::получить("https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket/")
.ожидайте?
.текст()
.ожидайте?;
пусть fragment = Html::parse_document(&resp);
пусть selector = Selector::разбор(".price").unwrap();
если пусть Some(element) = fragment.select(&selector).next() {
пусть price = element.текст().collect::<String>();
println!("Price: {}", price);
}
Ok(())
}

Use Case: Лучше всего подходит для большинства проектов веб-скрейпинга, особенно когда важны простота использования и совместимость в реальных условиях.

html5ever — быстрый и низкоуровневый

Html5ever Это быстрый и мощный HTML-парсер, написанный на Rust. Он был создан в рамках проекта браузера Servo. Он не строит полноценное DOM-дерево. Вместо этого он использует модель токенизации, выдавая каждый HTML-элемент в виде токена.

Key Features:

  • Соответствует стандарту HTML5
  • Очень быстрая обработка
  • Используется в браузерных движках вроде Servo

Плюсы:

  • Чрезвычайно быстрый и эффективный
  • Хорошо обрабатывает некорректный HTML
  • Полный контроль над структурой HTML
  • Возможна низкоуровневая настройка

Конс:

  • Многословный и сложнее в использовании
  • Нет представления в виде DOM-дерева
  • Требует больше кода для извлечения конкретных данных

Пример:

extern crate html5ever;
использовать html5ever::tendril::TendrilSink;
использовать html5ever::tokenizer::{Tokenizer, TokenSink, Token, TokenSinkResult, StartTag, EndTag, CharacterTokens, TokenizerOpts};
использовать html5ever::tokenizer::BufferQueue;
struct PriceFinder {
inside_price: bool, результат: String,
}
impl TokenSink для PriceFinder {
type Handle = ();
fn process_token(&mut себя, token: Token, _line: u64) -> TokenSinkResult<()> {
match token {
Token::TagToken(tag) => {
если tag.kind == StartTag && tag.name.as_ref() == "p" {
для attr в tag.attrs {
если attr.name.local.as_ref() == "class" && attr.value.as_ref() == "цена" {
себя.inside_price = true;
}
}
} else если tag.kind == EndTag && tag.name.as_ref() == "p" {
себя.inside_price = false;
}
}
Token::CharacterTokens(t) => {
если себя.inside_price {
себя.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

Плюсы:

  • Синтаксис в стиле jQuery
  • Прост для новичков
  • Может экспортировать данные в нескольких форматах
  • Встроенные функции для поиска и редактирования узлов

Конс:

  • Использует больше памяти, чем другие инструменты
  • Медленнее некоторых альтернатив на больших документах

Пример:

использовать select::document::Document;
использовать select::predicate::{Class, Name};
#[tokio::main]
async fn главная() -> Result<(), Box<dyn std::error::Error>> {
пусть resp = reqwest::получить("https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket/")
.ожидайте?
.текст()
.ожидайте?;
пусть документ = Document::с сайта(resp.as_str());
если пусть Some(price_node) = document.find(Name("p").и(Class("цена"))).next() {
println!("Price: {}", price_node.текст());
}
Ok(())
}

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

Kuchiki — дерево, похожее на DOM

Kuchiki (по-японски «гнилое дерево») — это удобный и интуитивно понятный HTML-парсер, который строит DOM-дерево, как настоящий браузер. Он основан на html5ever, но предоставляет древовидный интерфейс с CSS-селекторами.

Key Features:

  • Поддержка дерева DOM
  • CSS-селекторы
  • Позволяет обходить и модифицировать

Плюсы:

  • Очень прост в понимании
  • Полная древовидная структура
  • Хорошая поддержка селекторов
  • Отлично подходит для обхода сложного HTML

Конс:

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

Пример:

использовать kuchiki::parse_html;
использовать kuchiki::traits::*;
#[tokio::main]
async fn главная() -> Result<(), Box<dyn std::error::Error>> {
пусть resp = reqwest::получить("https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket/")
.ожидайте?
.текст()
.ожидайте?;
пусть документ = parse_html().one(resp);
если пусть 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 → затем разобрать текст

Плюсы:

  • Сверхлегкий
  • Эффективен для больших текстовых файлов
  • Полезно, когда нужен только обычный текст
  • Прост в использовании

Конс:

  • Не является полноценным HTML-парсером
  • Не поддерживает CSS-селекторы или DOM
  • Для обработки HTML требуются другие crates

Пример:

использовать pulldown_cmark::{Parser, Event};
использовать html2md;
#[tokio::main]
async fn главная() -> Result<(), Box<dyn std::error::Error>> {
пусть resp = reqwest::получить("https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket/")
.ожидайте?
.текст()
.ожидайте?;
пусть markdown = html2md::parse_html(&resp);
пусть parser = Parser::новый(&markdown);
для event в parser {
если пусть Event::Text(text) = event {
если 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-парсер

Заключение

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 может удивить вас своей скоростью. Какой бы парсер вы ни выбрали, используйте его вместе с надежные прокси или API для скрапинга например, Bright Data или ScrapingBee. Это поможет вашим скрейперам избегать IP-блокировок и эффективно масштабироваться!

 

Похожие записи