Парсинг HTML в Rust: обзор инструментов и практические примеры
Rust — современный системный язык программирования, который всё чаще используется для создания высокопроизводительных веб-скраперов и парсеров. Благодаря строгой системе типов, безопасности памяти и асинхронным возможностям, Rust идеально подходит для задач, где требуется быстро и надёжно извлекать данные из HTML-документов. В этой статье мы рассмотрим ключевые библиотеки для парсинга HTML на Rust, их особенности и приведём рабочие примеры кода.
Основные библиотеки для парсинга HTML в Rust
Экосистема Rust предлагает несколько зрелых решений для работы с HTML. Наиболее популярными являются:
- scraper — высокоуровневая библиотека с поддержкой CSS-селекторов, построенная поверх
select.rsиhtml5ever. - select.rs — библиотека для извлечения данных из DOM с помощью CSS-селекторов.
- html5ever — низкоуровневый парсер, соответствующий стандарту HTML5 (используется внутри Servo).
- quick-xml — быстрый XML/HTML парсер на основе потокового чтения, подходит для простых задач.
Для большинства задач веб-скрапинга рекомендуется scraper, так как он предоставляет удобный API и активную поддержку сообщества.
Установка и настройка
Добавьте зависимость в файл Cargo.toml:
[dependencies]
scraper = "0.20"
reqwest = { version = "0.12", features = ["blocking"] }
Библиотека reqwest используется для HTTP-запросов. Если вы предпочитаете асинхронный подход, используйте reqwest с tokio.
Пример 1: Извлечение заголовков страницы
Напишем простой парсер, который получает заголовок <title> и все заголовки <h1> с веб-страницы:
use scraper::{Html, Selector};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let html = r#"
<html>
<head><title>Пример страницы</title></head>
<body>
<h1>Первый заголовок</h1>
<h1>Второй заголовок</h1>
</body>
</html>
"#;
let document = Html::parse_document(html);
// Извлекаем title
let title_selector = Selector::parse("title").unwrap();
if let Some(title_element) = document.select(&title_selector).next() {
let title_text = title_element.text().collect::().join("");
println!("Заголовок страницы: {}", title_text);
}
// Извлекаем все h1
let h1_selector = Selector::parse("h1").unwrap();
for element in document.select(&h1_selector) {
let text = element.text().collect::().join("");
println!("H1: {}", text);
}
Ok(())
}
В этом примере мы парсим статический HTML. Для реальных сайтов HTML обычно получают через HTTP-запрос.
Пример 2: Парсинг реального сайта с HTTP-запросом
Теперь используем reqwest для загрузки страницы и извлечения ссылок:
use scraper::{Html, Selector};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let response = reqwest::blocking::get("https://www.rust-lang.org")?;
let html = response.text()?;
let document = Html::parse_document(&html);
let link_selector = Selector::parse("a[href]").unwrap();
println!("Ссылки на странице:");
for link in document.select(&link_selector) {
if let Some(href) = link.value().attr("href") {
let text = link.text().collect::();
println!(" Текст: '{}' -> URL: {}", text.trim(), href);
}
}
Ok(())
}
Обратите внимание на метод attr("href") — он позволяет получать значения атрибутов элементов.
Пример 3: Работа с атрибутами и вложенными селекторами
Часто требуется извлечь элементы по более сложным правилам. Например, получить все изображения с классом product-image и их атрибут src:
use scraper::{Html, Selector};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let html = r#"
<div class="products" Также в библиотеке
Combine для Swift: полное руководство с примерами
Rate limiting в C++: библиотеки и инструменты для контроля запросов
Тестирование в Kotlin: фреймворки и инструменты для качественного кода