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.pswp.cn/diannao/89731.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/89731.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Electron 安全最佳实践:构建安全的桌面应用

Electron 是一个流行的框架&#xff0c;允许开发者使用 Web 技术&#xff08;HTML、CSS、JavaScript&#xff09;构建跨平台桌面应用。许多知名应用&#xff0c;如 VS Code、Slack 和 Discord&#xff0c;都基于 Electron 开发。然而&#xff0c;由于其结合了 Node.js&#xff…

MySQL 事务详解:从基础操作到隔离级别与 MVCC 原理

前言 首先从概念上进行理解什么是事务&#xff0c;以及事务的4大属性&#xff0c;知道是什么还要知道为什么&#xff1f; 事务是如何进行操作的&#xff0c;最后在谈事务的隔离性、隔离级别&#xff08;最重要但是也很难理解&#xff09;&#xff0c;理解隔离级别体现在哪里 …

【Unity 编辑器工具开发:GUILayout 与 EditorGUILayout 对比分析】

Unity 编辑器工具开发&#xff1a;GUILayout 与 EditorGUILayout 对比分析 一、核心区别对比 方面GUILayoutEditorGUILayout区别命名空间UnityEngineUnityEditorEditorGUILayout 仅限编辑器环境适用范围游戏运行时 编辑器工具仅限编辑器工具运行时禁用 EditorGUILayout渲染管…

[附源码+数据库+毕业论文]基于Spring+MyBatis+MySQL+Maven+jsp实现的个人财务管理系统,推荐!

摘 要 随着软件信息技术的兴起&#xff0c;许多手工作业也升级为软件管理数据&#xff0c;本次针对个人财务数据的管理&#xff0c;开发一款个人财务管理系统&#xff0c;该系统可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间很长&#xff0c;数据存在错误不能及…

Compose入门3 - 高仿小红书 界面

使用compose 实现一个小红书UI 界面&#xff0c;主要是为了锻炼 使用compose布局的能力 demo地址&#xff1a;https://github.com/PangHaHa12138/ComposeDemo 先上demo 截图 下面是完整的compose代码 package com.example.test001import android.annotation.SuppressLint imp…

mybatis-plus json字段使用typeHandler自动转换为List

mybatis-plus json字段使用typeHandler自动转换为List mybatis-plus json字段使用typeHandler自动转换为List 一、实现思路 1.配置mybatis配置&#xff0c;注入handlermybatis-plus:typeHandlersPackage: com.power.common.core.handler 2.字段顶部增加注解TableField(typeHand…

(C++)学生管理系统(测试2版)(map数组的应用)(string应用)(引用)(C++教学)(C++项目)

1. 头文件与命名空间 #include <iostream> // 输入输出流库&#xff0c;提供cin/cout等基本I/O功能 #include <map> // 映射容器库&#xff0c;提供map数据结构&#xff08;键值对集合&#xff09; #include <string> // 字符串库&#xff0c;…

使用assembly解决jar包超大,实现依赖包、前端资源外置部署

成果物需要部署到用户内网的童鞋应该都遇到过该问题&#xff1a;引入的maven依赖越来越多&#xff0c;jar包越来越大&#xff0c;我之间甚至见过一两个G的依赖&#xff0c;想改个代码换到现场测试&#xff0c;包传到现场要一二十分钟&#xff0c;真正实现了改代码两分钟分钟&am…

基于PHP+MySQL实现(Web)英语学习与测试平台

数据库课设&#xff1a;英语学习与测试平台 运行环境要求 PHP7.1 基于 thinkPHP6.0、Layui、Xadmin 开发 主要功能 公共模块 登录注册个人信息修改密码修改 教师模块 文章查看发布班级管理测试查看发布批改历史成绩查看 学生模块 文章查看参与测试查看成绩 管理员模块…

WinForm中Settings.settings和app.config修改后信息不同步到exe.config问题

在 WinForms 项目中&#xff0c;Settings.settings 和 app.config/exe.config 的关系确实容易让人困惑。以下是问题的根本原因和解决方案&#xff1a; 问题本质 设计时文件&#xff1a;app.config&#xff08;源码中的配置文件&#xff09;运行时文件&#xff1a;bin/Debug/Yo…

【公司环境下发布个人NPM包完整教程】

&#x1f3e2; 公司环境下发布个人NPM包完整教程 创建时间: 2025年7月2日 适用场景: 公司电脑&#xff0c;需要临时切换个人账户发布npm包 &#x1f3af; 教程概述 场景说明 环境: 公司电脑&#xff0c;已配置公司npm账户目标: 临时使用个人账户发布npm包&#xff0c;发布后恢复…

渗透测试中 phpinfo() 的信息利用分析

在渗透测试中&#xff0c;phpinfo() 是一个非常常见却极具价值的信息泄露点。这个函数的本意是向开发者展示当前 PHP 环境的详细配置情况&#xff0c;包括编译选项、扩展模块、环境变量、系统信息、目录路径等。然而一旦该页面被暴露到互联网上&#xff0c;攻击者便可以借此收集…

《如何在 Spring 中实现 MQ 消息的自动重连:监听与发送双通道策略》

大家好&#xff0c;我是G探险者&#xff01;&#x1f4cc; 背景场景在高可用分布式系统中&#xff0c;我们经常面临&#xff1a;MQ 集群重启 → 消息监听中断MQ 网络短暂抖动 → 发送端连接失败一端恢复正常&#xff0c;另一端仍处于挂死状态如果你只配置了“连接工厂层”的重连…

OpenCV 安装使用教程

一、OpenCV 简介 OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开源计算机视觉和机器学习软件库&#xff0c;提供了超过 2500 个优化的算法&#xff0c;用于实时图像处理、视频分析、对象识别、人脸检测、机器学习等任务。 Python 提供了对 Open…

【SNN脉冲神经网络3】HH神经元软件仿真

本篇文章主要核心目的在于研究明白HH神经元的数学模型&#xff0c;并且验证其正确性。因此&#xff0c;在本篇文章中只会使用numpy函数库用于构建神经元&#xff0c;以及matplotlib用于绘图。1.导入对应的库import numpy as np import matplotlib.pyplot as plt import re impo…

「日拱一码」014 Python常用库——Pandas

目录 数据结构 pandas.Series &#xff1a;一维数组&#xff0c;类似于数组&#xff0c;但索引可以是任意类型&#xff0c;而不仅仅是整数 pandas.DataFrame &#xff1a;二维表格型数据结构&#xff0c;类似于 Excel 表格&#xff0c;每列可以是不同的数据类型 数据读取与写…

狂命爆肝21天,共51K字的JAVA学习笔记奉上,JAVA从入门到精通一文搞定,一文在手JAVA无忧

背景知识 Java 相关概念 JavaSE (Java Standard Edition): 基础版&#xff0c;用于开发桌面应用程序。JavaEE (Java Enterprise Edition): 企业版&#xff0c;用于开发企业级应用程序。JavaME (Java Micro Edition): 微型版&#xff0c;用于开发嵌入式系统和移动设备应用程序…

Dijkstra 算法#图论

Dijkstra 算法 算法前提&#xff1a;在没有负边的情况下使用。算法思路&#xff1a;将结点分成已确定最短路长度的点集 S 和未确定最短路长度的点集 T&#xff0c;每次从 T 集合中选取最短路长度最小的结点移到 S 集合中&#xff0c;并对其出边执行更新操作 从T集合中&#x…

开源与闭源大模型的生态与技术对比:以百度文心4.5开源为视角

技术对比&#xff1a;开源与闭源大模型的优劣势 性能对比&#xff1a;算力效率与场景适配的博弈 在模型性能的竞技场上&#xff0c;开源与闭源大模型呈现出明显的差异化特征。以百度文心4.5开源系列为例&#xff0c;其47B参数的混合专家&#xff08;MoE&#xff09;模型在飞桨…

企业电商解决方案哪家好?ZKmall模块商城全渠道支持 + 定制化服务更省心

在数字化浪潮席卷各行各业的当下&#xff0c;企业要想拓展市场、提升竞争力&#xff0c;搭建专属电商平台已经成了绕不开的选择。但市场上的电商解决方案五花八门&#xff0c;怎么才能挑到真正适合自己的&#xff1f;其实道理很简单&#xff1a;能同时搞定全渠道支持和定制化服…