Por que Rust é o novo rei do parsing?
\n\nSe você já escreveu um parser em Python, conhece aquele momento: o script funciona, os dados são baixados, mas de repente — erro de memória, ou o coletor de lixo decide "descansar" no momento mais inoportuno. Familiar? Agora imagine que seu parser voa na velocidade de C, não consome memória extra e ainda é seguro. Bem-vindo ao mundo do parsing em Rust.
\n\nRust não é apenas uma linguagem da moda. Para tarefas de raspagem de dados, ele oferece o que Python nunca conseguirá: controle total sobre a memória, custos zero com coletor de lixo e velocidade incrível. Além disso, o ecossistema para trabalhar com HTTP e HTML já está bastante maduro. Neste artigo, mostrarei como construir um parser real em Rust que você pode usar para monitoramento de preços, coleta de conteúdo ou análise de SEO.
\n\nO que vamos precisar: ferramentas Rust para parsing
\n\nPara web scraping em Rust, existem três pilares:
\n\n- \n
- reqwest — cliente HTTP. Capaz de navegar em sites, suporta assincronia e proxies. \n
- scraper (ou select.rs) — biblioteca para parsing de HTML. Permite usar seletores CSS. \n
- tokio — runtime assíncrono. Sem ele, seu parser vai travar em cada requisição. \n
Vamos criar um novo projeto e instalar as dependências:
\n\ncargo new rust_parser\ncd rust_parser\ncargo add reqwest --features json\ncargo add scraper tokio\n\nPronto. Agora temos tudo para escrever um parser em Rust em 10 minutos.
\n\nPrimeiro parser: obtendo títulos do Habr
\n\nVamos começar com algo simples: extrair os títulos dos artigos do Habr. Isso é um clássico, como um "Hello, World!" para raspagem em Rust.
\n\nuse scraper::{Html, Selector};\nuse reqwest;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n // 1. Enviar requisição GET\n let resp = reqwest::get("https://habr.com/ru/articles/")\n .await?\n .text()\n .await?;\n\n // 2. Fazer parsing do HTML\n let document = Html::parse_document(&resp);\n\n // 3. Criar seletor para os títulos\n let selector = Selector::parse("h2.article-title a").unwrap();\n\n // 4. Iterar e exibir\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\nO que está acontecendo aqui? Usamos reqwest para a requisição assíncrona, scraper para analisar o HTML e o seletor CSS h2.article-title a para encontrar todos os links nos títulos. Execute cargo run — e vemos a lista de artigos. Rápido, não é?
Parsing assíncrono: baixando 100 páginas por segundo
\n\nA principal vantagem do parsing em Rust é a assincronia. Em Python, você usaria asyncio e aiohttp, mas Rust com tokio oferece ainda mais controle. Vamos extrair várias páginas em paralelo.
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