使用reqwest+select实现简单网页爬虫

这篇文章是一个简单的Rust爬虫示例,我将爬取该网站的图书信息,包括书名、价格和库存状态。使用reqwest库发送HTTP请求,select库解析HTML内容。代码结构清晰,包含错误处理和基本的数据提取逻辑。

适合Rust爬虫新手学习网络请求、HTML解析和数据提取的基本流程。通过这个示例,你可以了解如何构建一个完整的爬虫工作流。

下面是一个简单、技术成熟且易于理解的 Rust 爬虫教程。我们将创建一个爬取图书信息(标题、价格)的爬虫,目标网站是专为爬虫练习设计的 books.toscrape.com

项目准备

1、创建新项目:

bash 复制代码
cargo new book_scraper
cd book_scraper

2、添加依赖 (Cargo.toml):

toml 复制代码
[dependencies]
reqwest = { version = "0.11", features = ["blocking"] } # 同步HTTP客户端
scraper = "0.13"           # HTML解析库
select = "0.5"             # CSS选择器库
anyhow = "1.0"             # 简化错误处理

完整代码 (src/main.rs)

rust 复制代码
use anyhow::{Context, Result};
use select::document::Document;
use select::predicate::{Class, Name};

fn main() -> Result<()> {
    // 目标网站
    let url = "http://books.toscrape.com/";
    
    // 发送HTTP GET请求
    let response = reqwest::blocking::get(url)
        .with_context(|| format!("Failed to fetch {}", url))?;
    
    // 确保请求成功
    if !response.status().is_success() {
        anyhow::bail!("Request failed with status: {}", response.status());
    }
    
    // 读取HTML内容
    let html_content = response.text()
        .with_context(|| "Failed to read response body")?;
    
    // 解析HTML文档
    let document = Document::from(html_content.as_str());
    
    println!("开始爬取: {}", url);
    println!("{:-^50}", "图书列表");
    
    // 查找所有图书条目
    for book in document.find(Class("product_pod")) {
        // 提取标题
        let title = book.find(Name("h3")).next()
            .and_then(|h3| h3.find(Name("a")).next())
            .map(|a| a.text())
            .unwrap_or_else(|| "未知标题".to_string());
        
        // 提取价格
        let price = book.find(Class("price_color")).next()
            .map(|p| p.text())
            .unwrap_or_else(|| "未知价格".to_string());
        
        // 提取库存状态
        let stock = book.find(Class("instock")).next()
            .map(|s| s.text().trim().to_string())
            .unwrap_or_else(|| "未知库存".to_string());
        
        println!("书名: {}", title);
        println!("价格: {}", price);
        println!("库存: {}", stock);
        println!("{}", "-".repeat(40));
    }
    
    println!("爬取完成!共找到 {} 本书", document.find(Class("product_pod")).count());
    Ok(())
}

代码解析

1、HTTP请求 (reqwest):

rust 复制代码
let response = reqwest::blocking::get(url)?;

使用同步方式获取网页内容,适合简单爬虫

2、错误处理 (anyhow):

rust 复制代码
.with_context(|| "错误描述")?;

提供友好的错误信息,自动处理错误传播

3、HTML解析 (select):

rust 复制代码
let document = Document::from(html_content.as_str());

将HTML内容加载到可查询的文档结构中

4、元素查找:

rust 复制代码
document.find(Class("product_pod"))

使用CSS选择器查找图书容器

5、数据提取:

rust 复制代码
book.find(Name("h3")).next().map(|h3| h3.text())

链式调用安全提取元素文本内容

运行结果示例

plaintext 复制代码
开始爬取: http://books.toscrape.com/
------------------图书列表------------------
书名: A Light in the Attic
价格: £51.77
库存: In stock
----------------------------------------
书名: Tipping the Velvet
价格: £53.74
库存: In stock
----------------------------------------
... (其他图书) ...
书名: The Black Maria
价格: £22.60
库存: In stock
----------------------------------------
爬取完成!共找到 20 本书

进阶功能添加

1、保存到CSV文件

添加依赖:

toml 复制代码
csv = "1.1"

修改代码:

rust 复制代码
use csv::Writer;

// 在main函数开头添加:
let mut wtr = Writer::from_path("books.csv")?;
wtr.write_record(&["书名", "价格", "库存"])?;

// 在循环内替换打印语句为:
wtr.write_record(&[&title, &price, &stock])?;

// 在main函数结尾添加:
wtr.flush()?;
println!("数据已保存到 books.csv");
2、自动翻页
rust 复制代码
let mut page = 1;
let mut total_books = 0;

loop {
    let url = format!("http://books.toscrape.com/catalogue/page-{}.html", page);
    
    // ...爬取逻辑...
    
    // 检查是否有下一页
    if document.find(Class("next")).next().is_none() {
        break;
    }
    
    page += 1;
    // 添加延时防止请求过快
    std::thread::sleep(std::time::Duration::from_secs(1));
}

注意事项

1、尊重网站规则:

  • 添加请求延时(至少1秒)
  • 检查robots.txt(本例中http://books.toscrape.com/robots.txt允许爬取)
rust 复制代码
std::thread::sleep(std::time::Duration::from_secs(1));

2、处理异常:

  • 网络错误
  • HTML结构变化
rust 复制代码
.unwrap_or_else(|| "未知".to_string());

3、用户代理设置:

rust 复制代码
let client = reqwest::blocking::Client::builder()
    .user_agent("Mozilla/5.0 (学习用爬虫)")
    .build()?;
client.get(url).send()?;

这个爬虫包含了爬虫的核心流程:请求 → 解析 → 提取 → 存储,使用成熟稳定的库实现,适合 Rust 新手学习。可根据需求扩展多线程、代理支持等高级功能。

到此就爬取完成!程序统计了找到的图书数量并显示结果。实际应用中,你可以添加CSV保存功能将数据存储到文件,或实现自动翻页爬取全站内容。注意在实际使用时添加请求延时,遵守robots.txt规则,并完善错误处理机制。这个基础爬虫可以扩展为更复杂的项目,如添加代理支持、并发处理或用户登录功能。希望这个示例能帮助你入门Rust爬虫开发!

相关推荐
Kapaseker6 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
A0微声z2 天前
Kotlin Multiplatform (KMP) 中使用 Protobuf
kotlin
alexhilton3 天前
使用FunctionGemma进行设备端函数调用
android·kotlin·android jetpack
lhDream3 天前
Kotlin 开发者必看!JetBrains 开源 LLM 框架 Koog 快速上手指南(含示例)
kotlin
RdoZam3 天前
Android-封装基类Activity\Fragment,从0到1记录
android·kotlin
Kapaseker3 天前
研究表明,开发者对Kotlin集合的了解不到 20%
android·kotlin
花酒锄作田4 天前
Gin 框架中的规范响应格式设计与实现
golang·gin
郑州光合科技余经理4 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
feifeigo1234 天前
matlab画图工具
开发语言·matlab
dustcell.4 天前
haproxy七层代理
java·开发语言·前端