Rust

2025年07月11日更新 6 人订阅
原价: ¥ 2 限时优惠
专栏简介 Rust编程语言之错误处理 Rust 语言之 flod Rust编程语言之Cargo、Crates.io详解 Rust编程语言之枚举与模式匹配 Rust语言 - 接口设计的建议之受约束(Constrained) Rust编程语言之无畏并发 Rust语言 - 接口设计的建议之灵活(flexible) Rust语言 - 接口设计的建议之显而易见(Obvious) Rust语言 - 接口设计的建议之不意外(unsurprising) Rust 实战:构建实用的 CLI 工具 HTTPie Rust编程语言学习之高级特性 Rust内存管理揭秘:深度剖析指针与智能指针 解决Rust中数组和切片的编译时大小问题 《Rust编程之道》学习笔记一 Rust Async 异步编程 简易教程 使用 Async Rust 构建简单的 P2P 节点 Rust编程语言入门之模式匹配 Rust async 编程 Rust编程语言之编写自动化测试 Rust编程语言之函数式语言特性:迭代器和闭包 《Rust编程之道》学习笔记二 Rust Tips 比较数值 使用 Rust 开发一个微型游戏 Rust编程初探:深入理解Struct结构体 深入理解Rust中的内存管理:栈、堆与静态内存详解 深入理解 Rust 结构体:经典结构体、元组结构体和单元结构体的实现 深入掌握 Rust 结构体:从模板到实例化的完整指南 深入理解Rust中的结构体:逻辑与数据结合的实战示例 深入理解 Rust 枚举:从基础到实践 掌握Rust字符串的精髓:String与&str的最佳实践 全面解析 Rust 模块系统:实战案例与应用技巧 Rust 中的 HashMap 实战指南:理解与优化技巧 掌握Rust模式匹配:从基础语法到实际应用 Rust 中的面向对象编程:特性与实现指南 深入理解 Rust 的 Pin 和 Unpin:理论与实践解析 Rust Trait 与 Go Interface:从设计到实战的深度对比 从零开始:用 Rust 和 Axum 打造高效 Web 应用 Rust 错误处理详解:掌握 anyhow、thiserror 和 snafu Rust 如何优雅实现冒泡排序 链表倒数 K 节点怎么删?Python/Go/Rust 实战 用 Rust 玩转数据存储:JSON 文件持久化实战 Rust实战:打造高效字符串分割函数 如何高效学习一门技术:从知到行的飞轮效应 Rust 编程入门:Struct 让代码更优雅 Rust 编程:零基础入门高性能开发 用 Rust 写个猜数游戏,编程小白也能上手! Rust 入门教程:变量到数据类型,轻松掌握! 深入浅出 Rust:函数、控制流与所有权核心特性解析 从零开始:用 Rust 和 Axum 打造高效 Web 服务 Rust 集合类型解析:Vector、String、HashMap 深入浅出Rust:泛型、Trait与生命周期的硬核指南 Rust实战:博物馆门票限流系统设计与实现 用 Rust 打造高性能图片处理服务器:从零开始实现类似 Thumbor 的功能 Rust 编程入门实战:从零开始抓取网页并转换为 Markdown 深入浅出 Rust:高效处理二进制数据的 Bytes 与 BytesMut 实战 Rust智能指针:解锁内存管理的进阶之道 用 Rust 打造命令行利器:从零到一实现 mini-grep 解锁Rust代码组织:轻松掌握Package、Crate与Module Rust 所有权:从内存管理到生产力释放 深入解析 Rust 的面向对象编程:特性、实现与设计模式 Rust + Protobuf:从零打造高效键值存储项目 bacon 点燃 Rust:比 cargo-watch 更爽的开发体验 用 Rust 打造微型游戏:从零开始的 Flappy Dragon 开发之旅 函数式编程的Rust之旅:闭包与迭代器的深入解析与实践 探索Rust编程之道:从设计哲学到内存安全的学习笔记 精读《Rust编程之道》:吃透语言精要,彻底搞懂所有权与借用 Rust 避坑指南:搞定数值比较,别再让 0.1 + 0.2 != 0.3 困扰你! 告别 Vec!掌握 Rust bytes 库,解锁零拷贝的真正威力 告别竞态条件:基于 Axum 和 Serde 的 Rust 并发状态管理最佳实践 Rust 异步编程实践:从 Tokio 基础到阻塞任务处理模式 Rust 网络编程实战:用 Tokio 手写一个迷你 TCP 反向代理 (minginx) 保姆级教程:Zsh + Oh My Zsh 终极配置,让你的 Ubuntu 终端效率倍增 不止于后端:Rust 在 Web 开发中的崛起之路 (2024数据解读) Rust核心利器:枚举(Enum)与模式匹配(Match),告别空指针,写出优雅健壮的代码

Rust 编程入门实战:从零开始抓取网页并转换为 Markdown

Rust编程入门实战:从零开始抓取网页并转换为MarkdownRust作为一门以性能、安全和并发著称的现代编程语言,正逐渐成为开发者的新宠。本文将通过一个简单但实用的案例,带你走进Rust编程的世界:通过HTTP请求抓取Rust官网首页内容,并将其HTML转换为Markdo

Rust 编程入门实战:从零开始抓取网页并转换为 Markdown

Rust 作为一门以性能、安全和并发著称的现代编程语言,正逐渐成为开发者的新宠。本文将通过一个简单但实用的案例,带你走进 Rust 编程的世界:通过 HTTP 请求抓取 Rust 官网首页内容,并将其 HTML 转换为 Markdown 文件保存。这不仅能帮助你快速上手 Rust 项目开发,还能让你感受 Rust 的独特魅力!无论你是编程新手还是有经验的开发者,这篇文章都将为你打开 Rust 编程的大门。

本文详细介绍了一个基于 Rust 的网页抓取项目,通过 cargo 创建项目、添加 reqwest 和 html2md 依赖,实现从 Rust 官网获取 HTML 内容并转换为 Markdown 文件保存的功能。我们将逐步解析代码,展示 Rust 的项目管理、语法特点、错误处理以及命令行参数处理等核心概念。同时,通过代码示例和运行结果,带你体验 Rust 的强类型、类型推导和宏编程等特性。文章适合 Rust 初学者以及希望快速上手 Rust 开发的开发者阅读。

需求

通过 HTTP 请求 Rust 官网首页,然后把获得的 HTML 转换成 Markdown 保存起来。

实操

首先,我们用 cargo new scrape_url 生成一个新项目。默认情况下,这条命令会生成一个可执行项目 scrape_url,入口在 src/main.rs。

~/Code via 🅒 base
➜ mcd rust  # mkdir rust cd rust

~/Code/rust via 🅒 base
➜

~/Code/rust via 🅒 base
➜ cargo new scrape_url
     Created binary (application) `scrape_url` package

~/Code/rust via 🅒 base
➜ ls
scrape_url

~/Code/rust via 🅒 base
➜ cd scrape_url

scrape_url on  master [?] via 🦀 1.70.0 via 🅒 base
➜ c

scrape_url on  master [?] via 🦀 1.70.0 via 🅒 base
➜

我们在 Cargo.toml 文件里,加入如下的依赖:

Cargo.toml 是 Rust 项目的配置管理文件,它符合 toml 的语法。我们为这个项目添加了两个依赖:reqwest 和 html2md。reqwest 是一个 HTTP 客户端,它的使用方式和 Python 下的 request 类似;html2md 顾名思义,把 HTML 文本转换成 Markdown。

[package]
name = "scrape_url"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
reqwest ={ version = "0.11.18", features = ["blocking"] }
html2md = "0.2.14"

接下来,在 src/main.rs 里,我们为 main() 函数加入以下代码:

use std::fs;

fn main() {
    let url = "https://www.rust-lang.org/";
    let output = "rust.md";

    println!("Fetching url: {}", url);
    let body = reqwest::blocking::get(url).unwrap().text().unwrap();

    println!("Converting html to markdown...");
    let md = html2md::parse_html(&body);

    fs::write(output, md.as_bytes()).unwrap();
    println!("Converted markdown has been saved in {}", output);
}

保存后,在命令行下,进入这个项目的目录,运行 cargo run,在一段略微漫长的编译后,程序开始运行,在命令行下,你会看到如下的输出:

scrape_url on  master [?] is 📦 0.1.0 via 🦀 1.70.0 via 🅒 base 
➜ cargo run           
   Compiling cfg-if v1.0.0
    ...
   Compiling hyper-tls v0.5.0
   Compiling reqwest v0.11.18
   Compiling scrape_url v0.1.0 (/Users/qiaopengjun/Code/rust/scrape_url)
    Finished dev [unoptimized + debuginfo] target(s) in 7.14s
     Running `target/debug/scrape_url`
Fetching url: https://www.rust-lang.org/
Converting html to markdown...
Converted markdown has been saved in rust.md

scrape_url on  master [?] is 📦 0.1.0 via 🦀 1.70.0 via 🅒 base took 9.1s 
➜ 

并且,在当前目录下,一个 rust.md 文件被创建出来了。打开一看,其内容就是 Rust 官网主页的内容。

从这段并不长的代码中,我们可以感受到 Rust 的一些基本特点:

首先,Rust 使用名为 cargo 的工具来管理项目,它类似 Node.js 的 npm、Golang 的 go,用来做依赖管理以及开发过程中的任务管理,比如编译、运行、测试、代码格式化等等。

其次,Rust 的整体语法偏 C/C++ 风格。函数体用花括号 {} 包裹,表达式之间用分号 ; 分隔,访问结构体的成员函数或者变量使用点 . 运算符,而访问命名空间(namespace)或者对象的静态函数使用双冒号 :: 运算符。如果要简化对命名空间内部的函数或者数据类型的引用,可以使用 use 关键字,比如 use std::fs。此外,可执行体的入口函数是 main()。

另外,你也很容易看到,Rust 虽然是一门强类型语言,但编译器支持类型推导,这使得写代码时的直观感受和写脚本语言差不多。

最后,Rust 支持宏编程,很多基础的功能比如 println!() 都被封装成一个宏,便于开发者写出简洁的代码。

这里例子没有展现出来,但 Rust 还具备的其它特点有:

  • Rust 的变量默认是不可变的,如果要修改变量的值,需要显式地使用 mut 关键字。
  • 除了 let / static / const / fn 等少数语句外,Rust 绝大多数代码都是表达式(expression)。所以 if / while / for / loop 都会返回一个值,函数最后一个表达式就是函数的返回值,这和函数式编程语言一致。
  • Rust 支持面向接口编程和泛型编程。
  • Rust 有非常丰富的数据类型和强大的标准库。
  • Rust 有非常丰富的控制流程,包括模式匹配(pattern match)。

<https://static001.geekbang.org/resource/image/15/cb/15e5152fe2b72794074cff40041722cb.jpg?wh=1920x1898>

如果想让错误传播,可以把所有的 unwrap() 换成 ? 操作符,并让 main() 函数返回一个 Result,如下所示:

use std::fs;

fn main() -> Result&lt;(), Box&lt;dyn std::error::Error>> {
    let url = "https://www.rust-lang.org/";
    let output = "rust.md";

    println!("Fetching url: {}", url);
    let body = reqwest::blocking::get(url)?.text()?;

    println!("Converting html to markdown...");
    let md = html2md::parse_html(&body);

    fs::write(output, md.as_bytes())?;
    println!("Converted markdown has been saved in {}", output);

    Ok(())
}

从命令行参数中获取用户提供的信息来绑定 URL 和文件名

use std::env;
use std::fs;

fn main() -> Result&lt;(), Box&lt;dyn std::error::Error>> {
    let args: Vec&lt;String> = env::args().collect();
    let url = &args[1];
    let output = &args[2];
    // let url = "https://www.rust-lang.org/";
    // let output = "rust.md";

    println!("Fetching url: {}", url);
    let body = reqwest::blocking::get(url)?.text()?;

    println!("Converting html to markdown...");
    let md = html2md::parse_html(&body);

    fs::write(output, md.as_bytes())?;
    println!("Converted markdown has been saved in {}", output);

    Ok(())
}

运行

scrape_url on  master [?] is 📦 0.1.0 via 🦀 1.70.0 via 🅒 base 
➜ cargo run https://www.rust-lang.org/ rust1.md
   Compiling scrape_url v0.1.0 (/Users/qiaopengjun/Code/rust/scrape_url)
    Finished dev [unoptimized + debuginfo] target(s) in 1.48s
     Running `target/debug/scrape_url 'https://www.rust-lang.org/' rust1.md`
Fetching url: https://www.rust-lang.org/
Converting html to markdown...
Converted markdown has been saved in rust1.md

scrape_url on  master [?] is 📦 0.1.0 via 🦀 1.70.0 via 🅒 base took 3.5s 
➜ 

总结

通过这个简单的网页抓取项目,我们初步领略了 Rust 编程的魅力:

  • Cargo 的便捷性:Rust 的包管理工具 cargo 让项目初始化、依赖管理和构建变得高效而简单。
  • 语法与特性:Rust 的 C/C++ 风格语法结合类型推导和宏编程,让代码既简洁又强大。
  • 错误处理:通过 unwrap() 和 ? 操作符,我们看到了 Rust 在错误处理上的灵活性和严谨性。
  • 扩展性:通过命令行参数的引入,代码具备了更高的灵活性和实用性。

这个项目只是 Rust 编程的冰山一角。Rust 还拥有强大的并发模型、丰富的标准库和模式匹配等特性,值得深入探索。如果你对 Rust 感兴趣,不妨继续尝试更复杂的项目,比如并发编程或构建 CLI 工具。欢迎关注我的微信公众号,获取更多 Rust 学习资源和实战案例!

参考

点赞 0
收藏 1
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论