Parsing in Rust: faster than Python and without a garbage collector

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why Rust is the New King of Parsing?

\n\n

If you've ever written a parser in Python, you know this moment: the script works, data is being downloaded, but suddenly — a memory error, or the garbage collector decides to "take a break" at the most inopportune moment. Sound familiar? Now imagine your parser flying at the speed of C, not consuming extra memory, and being safe at the same time. Welcome to the world of parsing in Rust.

\n\n

Rust is not just a trendy language. For data scraping tasks, it gives you what Python can never provide: full control over memory, zero overhead from a garbage collector, and incredible speed. Moreover, the ecosystem for working with HTTP and HTML is already mature enough. In this article, I'll show you how to build a real parser in Rust that you can use for price monitoring, content collection, or SEO analysis.

\n\n

What We Need: Rust Tooling for Parsing

\n\n

For web scraping in Rust, there are three pillars:

\n\n
    \n
  • reqwest — an HTTP client. It can browse websites, supports asynchrony and proxies.
  • \n
  • scraper (or select.rs) — a library for parsing HTML. It allows using CSS selectors.
  • \n
  • tokio — an asynchronous runtime. Without it, your parser will slow down on every request.
  • \n
\n\n

Let's create a new project and install the dependencies:

\n\n
cargo new rust_parser\ncd rust_parser\ncargo add reqwest --features json\ncargo add scraper tokio
\n\n

Done. Now we have everything to write a parser in Rust in 10 minutes.

\n\n

First Parser: Getting Headlines from Habr

\n\n

Let's start simple: we'll parse article headlines from Habr. This is a classic, like "Hello, World!" for Rust scraping.

\n\n
use scraper::{Html, Selector};\nuse reqwest;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    // 1. Send a GET request\n    let resp = reqwest::get("https://habr.com/ru/articles/")\n        .await?\n        .text()\n        .await?;\n\n    // 2. Parse the HTML\n    let document = Html::parse_document(&resp);\n\n    // 3. Create a selector for headlines\n    let selector = Selector::parse("h2.article-title a").unwrap();\n\n    // 4. Iterate and output\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\n

What's happening here? We use reqwest for an asynchronous request, scraper for HTML parsing, and the CSS selector h2.article-title a to find all links in headlines. Run cargo run — and we see a list of articles. Fast, isn't it?

\n\n

Asynchronous Parsing: Downloading 100 Pages in a Second

\n\n

The main feature of parsing in Rust is asynchrony. In Python, you would use asyncio and aiohttp, but Rust with tokio gives even more control. Let's parse several pages in parallel.

\n\n
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                

Blogs

Book Recommendations