Warum Rust der neue König des Parsings ist
\n\nWenn Sie jemals einen Parser in Python geschrieben haben, kennen Sie diesen Moment: Das Skript läuft, die Daten werden geladen, aber plötzlich – Speicherfehler, oder der Garbage Collector beschließt, im ungünstigsten Moment eine „Pause“ einzulegen. Kommt Ihnen bekannt vor? Stellen Sie sich nun vor, Ihr Parser fliegt mit C-Geschwindigkeit, frisst keinen unnötigen Speicher und ist dabei sicher. Willkommen in der Welt des Parsings in Rust.
\n\nRust ist nicht nur eine modische Sprache. Für Datenscraping-Aufgaben bietet es das, was Python niemals bieten kann: vollständige Kontrolle über den Speicher, keine Gemeinkosten durch einen Garbage Collector und unglaubliche Geschwindigkeit. Gleichzeitig ist das Ökosystem für die Arbeit mit HTTP und HTML bereits ausgereift genug. In diesem Artikel zeige ich Ihnen, wie Sie einen echten Parser in Rust erstellen, den Sie für Preisüberwachung, Content-Sammlung oder SEO-Analyse nutzen können.
\n\nWas wir brauchen: Rust-Werkzeuge für das Parsing
\n\nFür Web Scraping in Rust gibt es drei Säulen:
\n\n- \n
- reqwest – HTTP-Client. Kann Websites besuchen, unterstützt Asynchronität und Proxys. \n
- scraper (oder select.rs) – Bibliothek zum Parsen von HTML. Ermöglicht die Verwendung von CSS-Selektoren. \n
- tokio – Asynchrone Laufzeitumgebung. Ohne sie wird Ihr Parser bei jeder Anfrage langsamer. \n
Lassen Sie uns ein neues Projekt erstellen und die Abhängigkeiten installieren:
\n\ncargo new rust_parser\ncd rust_parser\ncargo add reqwest --features json\ncargo add scraper tokio\n\nFertig. Jetzt haben wir alles, um einen Parser in Rust in 10 Minuten zu schreiben.
\n\nErster Parser: Überschriften von Habr abrufen
\n\nBeginnen wir einfach: Wir parsen die Artikelüberschriften von Habr. Das ist ein Klassiker des Genres, wie „Hallo, Welt!“ für Scraping in Rust.
\n\nuse scraper::{Html, Selector};\nuse reqwest;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n // 1. GET-Anfrage senden\n let resp = reqwest::get("https://habr.com/ru/articles/")\n .await?\n .text()\n .await?;\n\n // 2. HTML parsen\n let document = Html::parse_document(&resp);\n\n // 3. Selektor für Überschriften erstellen\n let selector = Selector::parse("h2.article-title a").unwrap();\n\n // 4. Iterieren und ausgeben\n for element in document.select(&selector) {\n let title = element.text().collect::<Vec<_>>().join("");\n println!("{}", title.trim());\n }\n\n Ok(())\n}\n\nWas passiert hier? Wir verwenden reqwest für die asynchrone Anfrage, scraper zum Parsen des HTML und den CSS-Selektor h2.article-title a, um alle Links in den Überschriften zu finden. Starten Sie cargo run – und Sie sehen die Artikelliste. Ziemlich schnell, oder?
Asynchrones Parsing: 100 Seiten in einer Sekunde laden
\n\nDas Hauptmerkmal des Parsings in Rust ist die Asynchronität. In Python würden Sie asyncio und aiohttp verwenden, aber Rust mit tokio bietet noch mehr Kontrolle. Lassen Sie uns mehrere Seiten parallel parsen.
use scraper::{Html, Selector};\nuse reqwest;\nuse tokio::task;\n\nasync fn fetch_page(url: &str) -> Result<String, reqwest::Error> {\n let resp = reqwest::get(url).await?.text().await?;\n Ok(resp)\n}\n\nfn parse_titles(html: &str) -> Vec<String> {\n let document = Html::parse_document(html);\n let selector = Selector::parse("h2.article-title a").unwrap();\n document\n .select(&selector)\n .map(|el| el.text().collect::<Vec<_>>().join("").trim().to_string())\n .collect()\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n let urls: Vec<String> = (1..=5)\n .map(|i| format!("https://habr.com/ru/articles/page{}/", i))\n .coll