Rust 爬虫:比 Python 更快,且无需垃圾回收器

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

为什么 Rust 是爬虫领域的新王者?

\n\n

如果你曾经用 Python 写过爬虫,你一定经历过这样的时刻:脚本运行着,数据下载着,但突然——内存错误,或者垃圾回收器在最不合时宜的时候“休息”一下。熟悉吗?现在想象一下,你的爬虫以 C 语言的速度运行,不消耗多余内存,而且安全可靠。欢迎来到 Rust 爬虫 的世界。

\n\n

Rust 不仅仅是一种时髦的语言。对于 数据抓取 任务,它提供了 Python 永远无法做到的东西:完全的内存控制、零垃圾回收开销以及惊人的速度。同时,用于处理 HTTP 和 HTML 的生态系统已经相当成熟。在本文中,我将向你展示如何构建一个真正的 Rust 爬虫,你可以将其用于价格监控、内容收集或 SEO 分析。

\n\n

我们需要什么:Rust 爬虫工具集

\n\n

对于 Rust 网络抓取,有三个核心工具:

\n\n
    \n
  • reqwest — HTTP 客户端。能够访问网站,支持异步和代理。
  • \n
  • scraper(或 select.rs)— 用于解析 HTML 的库。允许使用 CSS 选择器。
  • \n
  • tokio — 异步运行时。没有它,你的爬虫会在每次请求时变慢。
  • \n
\n\n

让我们创建一个新项目并安装依赖项:

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

完成。现在我们拥有了在 10 分钟内编写一个 Rust 爬虫 所需的一切。

\n\n

第一个爬虫:从 Habr 获取标题

\n\n

我们从简单的开始:抓取 Habr 上的文章标题。这是经典入门示例,就像 Rust 抓取 的“Hello, World!”。

\n\n
use scraper::{Html, Selector};\nuse reqwest;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    // 1. 发送 GET 请求\n    let resp = reqwest::get("https://habr.com/ru/articles/")\n        .await?\n        .text()\n        .await?;\n\n    // 2. 解析 HTML\n    let document = Html::parse_document(&resp);\n\n    // 3. 为标题创建选择器\n    let selector = Selector::parse("h2.article-title a").unwrap();\n\n    // 4. 迭代并输出\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

这里发生了什么?我们使用 reqwest 进行异步请求,scraper 解析 HTML,并使用 CSS 选择器 h2.article-title a 查找标题中的所有链接。运行 cargo run — 然后我们看到文章列表。很快,对吧?

\n\n

异步爬取:每秒下载 100 个页面

\n\n

Rust 爬虫 的主要特点是异步性。在 Python 中,你会使用 asyncio 和 aiohttp,但 Rust 配合 tokio 提供了更多的控制。让我们并行抓取多个页面。

\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                

博客

书籍推荐