Rust中的异步编程:构建简单的网页爬虫

news2025/2/28 16:11:25

一、什么是Rust中的Futures和Async?

在Rust中,异步编程基于future(未来)的概念。一个future表示一个当前可能不可用,但将来某个时候可以获得的值。Rust中的Future特征定义了这一概念,任何实现了该特征的类型都表示一个future。

Rust中的异步编程关键元素包括:

  • asyncasync关键字标记函数或代码块,可以被中断并稍后恢复。当你标记一个函数为async时,它将返回一个Future,而不是直接返回结果。

  • awaitawait关键字用于在async函数中暂停函数的执行,直到待处理的future完成。

1.1.示例:并发地获取网页

让我们创建一个命令行程序,它并发地获取两个网页,提取它们的<title>元素,并打印第一个完成的网页的标题。我们将使用trpl crate,它提供了一个简化Rust异步编程的抽象,封装了像futurestokio这样的常见异步库。

1.2.设置项目

首先,我们需要设置一个新的Rust项目,并将trpl crate作为依赖项。

$ cargo new hello-async
$ cd hello-async
$ cargo add trpl

1.3.第一步:定义page_title函数

为了从网页中获取<title>元素,我们定义一个异步函数page_title。该函数将使用trpl::get方法发送HTTP GET请求到指定的URL并获取网页内容。然后,我们通过CSS选择器提取<title>元素。

这是page_title函数的实现:

use trpl::Html;

/// 异步函数,获取指定URL页面的<title>元素
async fn page_title(url: &str) -> Option<String> {
    // 使用trpl库的get方法获取网页内容,text()方法返回网页的文本内容
    let response_text = trpl::get(url).await.text().await;
    
    // 解析HTML并查找<title>标签,返回标题内容
    Html::parse(&response_text)
        .select_first("title")  // 使用CSS选择器查找第一个<title>元素
        .map(|title| title.inner_html())  // 如果找到了<title>,返回其内部HTML内容
}
1.3.1.解释:
  • 我们将函数标记为async,因为我们使用了异步操作,如获取URL (get(url)) 和读取响应体 (text()),这些都是异步操作。
  • get(url)text()都是异步操作,所以我们使用await等待它们完成。
  • 获取到响应后,我们解析HTML并使用select_first("title")方法查找第一个<title>元素。
  • 最后,我们返回<title>元素的内部HTML内容,即页面的标题,返回类型是Option<String>

1.4.第二步:在main函数中调用page_title

接下来,我们需要在main函数中调用page_title函数。然而,Rust不允许将main函数标记为异步函数,因此我们必须使用一个运行时来执行异步代码。我们可以使用trpl::run函数,它初始化异步运行时并运行page_title函数返回的future。

这是更新后的main函数:

fn main() {
    // 从命令行参数中获取两个URL
    let url1 = std::env::args().nth(1).expect("Please provide the first URL");
    let url2 = std::env::args().nth(2).expect("Please provide the second URL");

    // 使用trpl::run运行一个异步代码块
    trpl::run(async {
        // 创建两个异步任务,分别获取两个URL的<title>元素
        let title_fut_1 = page_title(&url1);
        let title_fut_2 = page_title(&url2);

        // 使用race函数并发地执行两个任务,返回第一个完成的结果
        let result = trpl::race(title_fut_1, title_fut_2).await;

        // 根据race函数的结果打印第一个完成的页面标题
        match result {
            trpl::Either::Left(Some(title)) => println!("The title for {} is: {}", url1, title),
            trpl::Either::Right(Some(title)) => println!("The title for {} is: {}", url2, title),
            _ => println!("Could not fetch title for one or both URLs."),
        }
    });
}

1.5.第三步:竞速两个URL

在这个示例中,我们从命令行传入两个URL,分别获取它们的标题,并返回第一个完成的网页。我们使用trpl::race函数,它返回一个值,指示哪个future先完成。

1.5.1解释:
  • 我们并发地调用page_title函数,分别创建两个future:title_fut_1title_fut_2
  • 使用trpl::race等待哪个future先完成。它返回一个值,表示哪个future完成得更早,我们可以根据这个结果处理。
  • 使用match语句打印第一个完成的页面的标题。如果某个页面没有<title>标签,我们也会处理这种情况。

1.6.运行程序

要运行该程序,您需要提供两个URL作为命令行参数。下面是运行爬虫的示例:

$ cargo run -- https://www.rust-lang.org https://www.example.com

输出将显示第一个完成加载的页面的标题:

The title for https://www.rust-lang.org is: Rust Programming Language

1.7.理解Rust中的Async和Futures

在这个示例中,async关键字将函数转换为返回Future的函数,Future代表一个将在未来某个时刻可用的值。这是Rust中异步编程的基本概念。

  • 懒惰的Futures:在Rust中,futures是懒惰的,意味着它们不会在创建时立即执行,而是直到使用await显式等待它们时才会执行。这使得Rust能够优化异步任务并避免不必要的计算。

  • 状态机和执行器:每个async函数都被Rust编译器转换为一个状态机。这些状态机允许程序在await点暂停执行,并在未来某个时刻恢复。异步任务的执行由执行器管理,例如trpl::run函数所提供的执行器。

  • 并发性:通过让两个URL并发执行,我们利用了异步编程的优势,使程序比顺序执行更高效。

二、结论

我们成功地构建了一个简单的异步网页爬虫,能够并发地获取两个网页,并打印第一个完成的网页的标题。在这个过程中,我们学习了Rust中的异步编程的基本概念,包括futures、async/await和并发性。

通过使用trpl crate并理解Rust的异步系统工作原理,你现在可以创建更复杂的异步应用程序,充分利用Rust的并发模型。祝编程愉快!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2307464.html

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

相关文章

springai系列(二)从0开始搭建和接入azure-openai实现智能问答

文章目录 前言1.从0开始搭建项目2.进入微软openai申请key3.配置application.yaml4.编写controller5.测试源码下载地址总结 前言 之前使用openai的官网的api需要科学上网&#xff0c;但是我们可以使用其他的代理间接实现使用chatgpt的相关模型&#xff0c;解决这个问题。比如:本…

Go在1.22版本修复for循环陷阱

记录 前段时间升级Go版本碰到一个大坑&#xff0c;先记录。 先上代码案例&#xff1a; func main() {testClosure() }func testClosure() {for i : 0; i < 5; i {defer func() {fmt.Println(i)}()} }在1.22之下&#xff08;不包括1.22&#xff09;版本&#xff1a; 输出的…

可视化约瑟夫生死环小游戏

这是一个基于Tkinter的图形界面应用程序&#xff0c;用于模拟约瑟夫环问题。约瑟夫环问题是一个经典的数学问题&#xff0c;描述的是N个人围成一圈&#xff0c;从第一个人开始报数&#xff0c;每数到第M个人就将其淘汰&#xff0c;然后从下一个人继续报数&#xff0c;直到剩下最…

【深入理解JWT】从认证授权到网关安全

最近的项目学习中&#xff0c;在进行登陆模块的用户信息验证这一部分又用到了JWT的一些概念和相关知识&#xff0c;特在此写了这篇文章、方便各位笔者理解JWT相关概念 目录 先来理解JWT是什么&#xff1f; 区分有状态认证和无状态认证 有状态认证 VS 无状态认证 JWT令牌的…

学习路之PHP --TP6异步执行功能 (无需安装任何框架)

学习路之PHP --异步执行功能 &#xff08;无需安装任何框架&#xff09; 简介一、工具类二、调用三、异步任务的操作四、效果&#xff1a; 简介 执行异步任务是一种很常见的需求&#xff0c;如批量发邮箱&#xff0c;短信等等执行耗时任务时&#xff0c;需要程序异步执行&…

九、数据治理架构流程

一、总体结构 《数据治理架构流程图》&#xff08;Data Governance Architecture Flowchart&#xff09; 水平结构&#xff1a;流程图采用水平组织&#xff0c;显示从数据源到数据应用的进程。 垂直结构&#xff1a;每个水平部分进一步划分为垂直列&#xff0c;代表数据治理的…

【数据结构】 最大最小堆实现优先队列 python

堆的定义 堆&#xff08;Heap&#xff09;是一种特殊的完全二叉树结构&#xff0c;通常分为最大堆和最小堆两种类型。 在最大堆中&#xff0c;父节点的值总是大于或等于其子节点的值&#xff1b; 而在最小堆中&#xff0c;父节点的值总是小于或等于其子节点的值。 堆常用于实…

51c自动驾驶~合集52

我自己的原文哦~ https://blog.51cto.com/whaosoft/13383340 #世界模型如何推演未来的千万种可能 驾驶世界模型&#xff08;DWM&#xff09;&#xff0c;专注于预测驾驶过程中的场景演变&#xff0c;已经成为追求自动驾驶的一种有前景的范式。这些方法使自动驾驶系统能够更…

【我的 PWN 学习手札】House of Husk

House of Husk House of Husk是利用格式化输出函数如printf、vprintf在打印输出时&#xff0c;会解析格式化字符如%x、%lld从而调用不同的格式化打印方法&#xff08;函数&#xff09;。同时C语言还提供了注册自定义格式化字符的方法。注册自定义格式化字符串输出方法&#xf…

Nmap使用指南

Nmap使用指南 Nmap (网络映射器) 是一款强大的应用网络扫描和安全核查工具&#xff0c;适合于网络管理和安全专家。本文将介绍Nmap的基本使用方法&#xff0c;包括基本命令和常用功能。 1. 基本使用方式 Nmap的基本命令格式如下&#xff1a; nmap [选项] 目标地址目标地址 可…

傅里叶分析

傅里叶分析之掐死教程&#xff08;完整版&#xff09;更新于2014.06.06 要让读者在不看任何数学公式的情况下理解傅里叶分析。 傅里叶分析不仅仅是一个数学工具&#xff0c;更是一种可以彻底颠覆一个人以前世界观的思维模式。但不幸的是&#xff0c;傅里叶分析的公式看起来太复…

从零开始用react + tailwindcss + express + mongodb实现一个聊天程序(五) 实现登录功能

1.登录页面 完善登录页面 和注册差不多 直接copy signUpPage 内容 再稍微修改下 import { useState } from "react"; import { useAuthStore } from "../store/useAuthStore"; import { MessageSquare,Mail,Lock,Eye, EyeOff,Loader2} from "lucide…

【机器学习】Logistic回归#1基于Scikit-Learn的简单Logistic回归

主要参考学习资料&#xff1a; 《机器学习算法的数学解析与Python实现》莫凡 著 前置知识&#xff1a;线性代数-Python 目录 问题背景数学模型类别表示Logistic函数假设函数损失函数训练步骤 代码实现特点 问题背景 分类问题是一类预测非连续&#xff08;离散&#xff09;值的…

8.Dashboard的导入导出

分享自己的Dashboard 1. 在Dashboard settings中选择 JSON Model 2. 导入 后续请参考第三篇导入光放Dashboard&#xff0c;相近

next.js-学习2

next.js-学习2 1. https://nextjs.org/learn/dashboard-app/getting-started2. 模拟的数据3. 添加样式4. 字体&#xff0c;图片5. 创建布局和页面页面导航 1. https://nextjs.org/learn/dashboard-app/getting-started /app: Contains all the routes, components, and logic …

视频推拉流EasyDSS直播点播平台授权激活码无效,报错400的原因是什么?

在当今数字化浪潮中&#xff0c;视频推拉流 EasyDSS 视频直播点播平台宛如一颗璀璨的明珠&#xff0c;汇聚了视频直播、点播、转码、精细管理、录像、高效检索以及时移回看等一系列强大功能于一身&#xff0c;全方位构建起音视频服务生态。它既能助力音视频采集&#xff0c;精准…

【论文详解】Transformer 论文《Attention Is All You Need》能够并行计算的原因

文章目录 前言一、传统 RNN/CNN 存在的串行计算问题二、Transformer 如何实现并行计算&#xff1f;三、Transformer 的 Encoder 和 Decoder 如何并行四、结论 前言 亲爱的家人们&#xff0c;创作很不容易&#xff0c;若对您有帮助的话&#xff0c;请点赞收藏加关注哦&#xff…

Framework层JNI侧Binder

目录 一&#xff0c;Binder JNI在整个系统的位置 1.1 小结 二&#xff0c;代码分析 2.1 BBinder创建 2.2 Bpinder是在查找服务时候创建的 2.3 JNI实现 2.4 JNI层android_os_BinderProxy_transact 2.5 BPProxy实现 2&#xff09;调用IPCThreadState发送数据到Binder驱动…

Excel大文件拆分

import pandas as pddef split_excel_file(input_file, output_prefix, num_parts10):# 读取Excel文件df pd.read_excel(input_file)# 计算每部分的行数total_rows len(df)rows_per_part total_rows // num_partsremaining_rows total_rows % num_partsstart_row 0for i i…