当前位置: 首页 > news >正文

Rust异步爬虫实现与优化

Rust 语言在爬虫领域的应用相对较少,尽管 Rust 的 async/await 已稳定,但其与线程安全、Pin 等概念的结合仍较复杂,而爬虫高度依赖并发处理,进一步提高了开发成本。这就导致了使用Rust语言爬虫用的人很少。

在这里插入图片描述

下面是一个使用 Rust 编写的异步爬虫示例,支持并发请求、深度控制和去重功能。该爬虫使用 Tokio 作为异步运行时,Reqwest 处理 HTTP 请求,Select 解析 HTML。

use std::{collections::HashSet, sync::Arc, time::Duration};use select::{document::Document,predicate::{Name, Attr},
};
use tokio::{sync::{Mutex, Semaphore},time,
};
use url::Url;// 爬虫配置
const MAX_DEPTH: usize = 3; // 最大爬取深度
const MAX_PAGES: usize = 50; // 最大爬取页面数
const MAX_CONCURRENT_REQUESTS: usize = 10; // 最大并发请求数
const USER_AGENT: &str = "Mozilla/5.0 (compatible; AsyncCrawler/1.0)";#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {let start_url = "https://www.rust-lang.org/";println!("Starting crawl from: {}", start_url);// 共享状态let visited = Arc::new(Mutex::new(HashSet::new()));let page_count = Arc::new(Mutex::new(0));let semaphore = Arc::new(Semaphore::new(MAX_CONCURRENT_REQUESTS));// 初始 URLcrawl_page(start_url.to_string(),0,visited.clone(),page_count.clone(),semaphore.clone(),).await?;println!("Crawling completed!");Ok(())
}/// 爬取单个页面
async fn crawl_page(url: String,depth: usize,visited: Arc<Mutex<HashSet<String>>>,page_count: Arc<Mutex<usize>>,semaphore: Arc<Semaphore>,
) -> Result<(), Box<dyn std::error::Error>> {// 检查深度限制if depth > MAX_DEPTH {return Ok(());}// 检查是否已访问{let mut visited_set = visited.lock().await;if visited_set.contains(&url) {return Ok(());}visited_set.insert(url.clone());}// 获取信号量许可 (控制并发)let _permit = semaphore.acquire().await?;// 创建 HTTP 客户端let client = reqwest::Client::builder().user_agent(USER_AGENT).timeout(Duration::from_secs(5)).build()?;// 发送请求let response = match client.get(&url).send().await {Ok(res) => res,Err(e) => {eprintln!("Request failed: {} - {}", url, e);return Ok(());}};// 检查状态码if !response.status().is_success() {eprintln!("HTTP error: {} - {}", url, response.status());return Ok(());}// 获取页面内容let html = match response.text().await {Ok(html) => html,Err(e) => {eprintln!("Failed to get text: {} - {}", url, e);return Ok(());}};// 更新页面计数器let mut count = page_count.lock().await;*count += 1;println!("[{}/{}] Depth {}: {}", *count, MAX_PAGES, depth, url);// 检查页面限制if *count >= MAX_PAGES {return Ok(());}// 解析页面并提取链接let base_url = Url::parse(&url)?;let document = Document::from(html.as_str());let links: Vec<String> = document.find(Name("a")).filter_map(|a| a.attr("href")).filter_map(|href| base_url.join(href).ok()).map(|url| url.to_string()).collect();// 限制请求速率time::sleep(Duration::from_millis(100)).await;// 创建新爬取任务let mut tasks = vec![];for link in links {let visited = visited.clone();let page_count = page_count.clone();let semaphore = semaphore.clone();tasks.push(tokio::spawn(async move {crawl_page(link, depth + 1, visited, page_count, semaphore).await}));}// 等待所有任务完成for task in tasks {let _ = task.await;}Ok(())
}

功能说明

1、异步并发

  • 使用 Tokio 的异步任务 (tokio::spawn)
  • 通过信号量 (Semaphore) 限制最大并发请求数

2、爬取控制

  • MAX_DEPTH:限制爬取深度
  • MAX_PAGES:限制最大页面数
  • 请求超时设置 (5 秒)
  • 请求间延迟 (100ms)

3、智能解析

  • 使用 url 库处理相对/绝对路径
  • 通过 select 库解析 HTML 并提取链接
  • 只处理 <a> 标签的 href 属性

4、状态管理

  • 使用 Mutex 保护共享状态
  • 使用 HashSet 记录已访问 URL
  • 原子计数器跟踪已爬取页面数

使用说明

1、添加依赖到 Cargo.toml

[dependencies]
tokio = { version = "1.0", features = ["full"] }
reqwest = "0.11"
select = "0.6"
url = "2.4"

2、可配置参数:

// 在代码顶部修改这些常量:
const MAX_DEPTH: usize = 3;      // 最大爬取深度
const MAX_PAGES: usize = 50;     // 最大爬取页面数
const MAX_CONCURRENT_REQUESTS: usize = 10; // 并发请求数
const USER_AGENT: &str = "..."; // 自定义 User-Agent

3、运行:

cargo run

这个爬虫框架提供了基础功能,我们可以根据具体需求扩展其功能。建议在实际使用时添加适当的日志记录、错误处理和遵守目标网站的爬取政策。

http://www.lryc.cn/news/580135.html

相关文章:

  • 全星 QMS:制造业全面质量管理的数字化全能平台
  • 鸿蒙系统(HarmonyOS)应用开发之手势锁屏密码锁(PatternLock)
  • Jenkins-Publish HTML reports插件
  • 接口测试之postman
  • ZigBee通信技术全解析:从协议栈到底层实现,全方位解读物联网核心无线技术
  • 区块链技术核心组件及应用架构的全面解析
  • 7.4_面试_JAVA_
  • 【PyTorch】PyTorch预训练模型缓存位置迁移,也可拓展应用于其他文件的迁移
  • 基于PHP+MySQL实现(Web)英语学习与测试平台
  • 408第三季part2 - 计算机网络 - 计算机网络基本概念
  • 金融平衡术:创新与合规的突围之路
  • Spark从入门到实战:安装与使用全攻略
  • 使用 DigitalPlat 免费搭配 Cloudflare Tunnel 实现飞牛系统、服务及 SSH 内网穿透教程
  • Java SE--方法的使用
  • Kotlin中优雅的一行行读取文本文件
  • 缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级
  • 【笔记】PyCharm 2025.2 EAP 创建 Poetry 和 Hatch 环境的踩坑实录与反馈
  • 三体融合实战:Django+讯飞星火+Colossal-AI的企业级AI系统架构
  • Android WebView 性能优化指南
  • 《Java修仙传:从凡胎到码帝》第三章:缩进之劫与函数峰试炼
  • React Ref使用
  • React中的useState 和useEffect
  • 指环王英文版魔戒再现 Part 1 Chapter 01
  • 力扣 hot100 Day34
  • [Linux]内核态与用户态详解
  • java web5(黑马)
  • Vue内置指令
  • 一、react18+项目初始化(vite)
  • 支付宝小程序关键词排名实战攻略,从0到1的突破之路
  • 八股学习(三)---MySQL