在本教程中,您将深入了解 Rust 网络爬取。具体来说,您将学到:
- 为什么 Rust 是一种高效爬取 Web 的优秀语言。
- Rust 中最好的爬取库是什么。
- 如何从头开始构建 Rust 爬虫。
让我们深入了解一下!
你能用 Rust 执行网页爬取吗?
当然可以,而且 Rust 还是一个非常有效的选择!
Rust 是一种静态类型的编程语言,以其注重安全性、性能和并发性而闻名。近年来,它因其高效率而受到了广泛关注。这使得它成为各种应用程序的优秀选择,包括网络爬虫。
具体而言,Rust提供了用于在线数据爬取任务的有用功能。例如,其可靠的并发模型允许同时执行多个网络请求。这就是为什么Rust是一种适用于高效地从多个网站上爬取大量数据的通用语言。
此外,Rust生态系统还包括HTTP客户端和HTML解析库,简化了网页检索和数据提取过程。让我们来探索一些最受欢迎的库!
最好的 Rust 网页爬取库
以下是一些最好的 Rust 网页爬取库的列表:
- reqwest:一个强大的 Rust HTTP 客户端库。它提供了一个简单高效的 API 来发出 HTTP 请求。多亏了它,您可以下载网页、处理 cookie 和自定义标头,使其成为在爬取项目中获取 Web 内容的重要库。
- scraper:一个简化 HTML 解析和数据提取的 Rust 库。它使您能够通过 CSS 选择器通过内置方法遍历和操作 HTML 文档。
- rust-headless-chrome:一个 Rust 库,它公开了一个高级 API,用于通过 DevTools 协议控制 Chrome 的无头实例。您可以将其视为 Rust 中的 Puppeteer。这是处理涉及需要 JavaScript 渲染或浏览器自动化的动态页面的爬取任务的不可或缺的工具。
先决条件
要遵循此网页爬取 Rust 教程,您需要在计算机上安装 Rust。在终端中启动以下命令以验证:
rustc --version
如果结果如下,那么你就可以开始了:
rustc 1.73.0 (cc66ad468 2023-10-03)
如果以错误结束,则需要安装 Rust。 从官方网站下载安装程序并按照说明进行操作。这将设置:
您知道需要一个 IDE 来使用 Rust 进行编码。安装了 rust-analyzer 扩展 的 Visual Studio Code 是一款免费且可靠的解决方案。
用 Rust 构建网络爬虫
在本分步部分中,您将了解如何构建 Rust 网络爬取工具。您将编写一个自动化脚本,该脚本可以自动从Quotes scraping sandbox检索数据。 Rust 网络爬取过程的目标是:
- 从页面中选择引用 HTML 元素。
- 从中提取数据。
- 将收集的数据转换为易于探索的格式,例如 CSV。
截至撰写本文时,目标站点如下所示:
请按照以下说明学习如何在 Rust 中执行网页爬取!
第 1 步:在 Rust 中建立一个项目
是时候初始化 Rust 网络爬取项目了。在要放置项目的文件夹中打开终端,然后运行以下命令:
cargo new rust_web_scraper
将出现 rust_web_scraper 目录。打开它你会看到:
- toml:manifest 文件,包含项目的依赖项。
- src/:包含 Rust 文件的文件夹。
在 IDE 中打开 rust_web_scraper 文件夹。您会注意到 src 目录中的 main.rs 文件包含:
fn main() { println!("Hello, world!"); }
这就是最简单的 Rust 脚本的样子。 main() 函数代表任何 Rust 应用程序的入口点。
完美,验证您的脚本是否有效!
在 IDE 的终端中,执行以下命令来编译 Rust 应用程序:
cargo build
这应该在目标文件夹中创建一个二进制文件。
您现在可以使用以下命令运行二进制可执行文件:
cargo run
如果一切按预期进行,它应该在终端中打印:
Hello, World!
太好了,您现在有了一个可以运行的 Rust 项目!现在,您的爬取工具只需打印“Hello, World!”但它很快就会包含一些爬取逻辑。
第2步:安装项目的依赖项
在安装任何软件包之前,您需要确定哪些 Rust 网络爬取库最适合您的目标。为此,请在浏览器中打开目标站点。右键单击并选择“Inspect”选项以打开 DevTools。然后,到达“网络”选项卡,重新加载页面,然后查看“Fetch/XHR”部分。
如您所见,网页不执行任何AJAX 请求。换句话说,它不会在客户端上动态检索数据。因此,它是一个静态页面,其 HTML 内容已包含所有数据。
因此,您不需要具有无头浏览器功能的库来对其执行网页爬取。您仍然可以使用 Headless Chrome,但这只会带来性能开销。这就是为什么你应该这样做:
- reqwest:作为 HTTP 客户端检索与目标页面关联的 HTML 文档。
- scraper:作为 HTML 解析器来解析 HTML 内容并从中检索数据。
将 reqwest 和 scraper 添加到项目的依赖项中:
cargo add scraper reqwest --features "reqwest/blocking"
该命令将相应地更新 Cargo.toml 文件。
做得好!您现在已经具备了在 Rust 中执行数据爬取所需的一切!
第三步:下载目标页面
使用reqwest的阻塞HTTP客户端下载目标页面的HTML文档:
let response = reqwest::blocking::get("https://quotes.toscrape.com/"); let html = response.unwrap().text().unwrap();
在后台,get() 对作为参数传递的 URL 执行同步 HTTP GET 请求。脚本执行将停止,直到服务器响应。
要从响应中以字符串形式提取原始 HTML,您需要调用 unwrap() 两次以:
- 解开存储在响应变量中的 Result 对象。如果请求成功,您将收到服务器返回的响应。否则,这将导致 Rust 恐慌。
- 解开其文本内容以访问 HTML 内容。
第四步:解析HTML文档
现在您已将目标页面的 HTML 内容存储在字符串中,您可以将其提供给 scraper:
let document = scraper::Html::parse_document(&html)
文档对象公开了 HTML 解析器公开的数据提取 API。
您的 main.rs 文件现在应如下所示:
fn main() { // download the HTML document associated with the target page let response = reqwest::blocking::get("https://quotes.toscrape.com/"); // extract the raw HTML from the response let html = response.unwrap().text().unwrap(); // parse the HTML content let document = scraper::Html::parse_document(&html); // scraping logic... }
第五步:检查页面内容
是时候分析目标 URL 的结构以设计有效的数据检索策略了。
在浏览器中打开要爬取的报价。您将看到报价列表。右键单击这些元素之一并选择“检查”。您的浏览器的开发工具将打开如下:
在那里,您可以注意到每个报价卡都是一个 .quote HTML 节点,其中包含:
- 存储引用文本的 .text 元素。
- 包含作者姓名的 .author 元素。
- 许多 .tag 元素,每个元素显示一个标签。
以上注释包含选择所需 DOM 元素所需的所有CSS 选择器。现在您需要在代码中使用它们来执行 Rust 网页爬取!
第 6 步:实施数据爬取
开始之前,您需要一个自定义数据结构来存储爬取的数据。在 main.rs 文件顶部添加以下行以定义新的 struct:
struct Quote { quote: Option<String>, author: Option<String>, tags: Vec<String>, }
由于页面包含多个引号,因此在 main() 中实例化 Vec 的 Quote 对象:
let mut quotes: Vec<Quote> = Vec::new();
在脚本末尾,引号将包含所有爬取的数据。
了解如何填充该数组!
在 scraper 中定义 CSS Selector 对象,如下所示:
let html_quote_selector = scraper::Selector::parse(".quote").unwrap();
接下来,使用 select() 方法应用于页面并检索所需的 HTML 元素:
let html_quotes = document.select(&html_quote_selector);
您现在可以迭代每个报价元素并从中提取感兴趣的数据:
for html_quote in html_quotes { let quote_selector = scraper::Selector::parse(".text").unwrap(); let quote = html_quote .select("e_selector) .next() .map(|element| element.text().collect::<String>()); let author_selector = scraper::Selector::parse(".author").unwrap(); let author = html_quote .select(&author_selector) .next() .map(|element| element.text().collect::<String>()); let tags_selector = scraper::Selector::parse(".tag").unwrap(); let tags = html_quote .select(&tags_selector) .map(|element| element.text().collect::<String>()) .collect(); // create a new quote object with the scraped data // and store it in the list let quote_obj = Quote { quote, author, tags, }; quotes.push(quote_obj); }
前几行应用了上面看到的相同方法。他们定义了一个 CSS 选择器对象并将其应用到 quote HTML 元素上。由于 select() 返回多个元素,因此您可以使用 next() 获取第一个元素。然后,text() 方法使您可以访问其文本内容。
获取爬取数据后,只需填充新的 Quote 对象并将其添加到数组中即可。
极好的!您刚刚学习了如何在 Rust 中执行网页爬取!
第七步:添加爬取逻辑
您刚刚从单个页面中提取了数据,但不要忘记报价列表由多个页面组成。特别是,每个页面都有一个“下一步 →”按钮,其中包含指向下一页的链接:
除最后一页外,所有页面均如此:
要执行网络爬行并从每个页面爬取数据,您需要:
- 刮一页。
- 查看页面上是否存在“下一步→”元素。
- 如果是这样,请跟踪其 URL 并为新页面重复该循环。否则,停止爬取逻辑。
在 Rust 中实现上述算法如下:
// the URL of the first page to scrape let mut url = "https://quotes.toscrape.com/".to_owned(); loop { // download the HTML document associated with the target page let response = reqwest::blocking::get(url); // parsing and scraping logic... // select the "Next ->" element let next_page_element_selector = scraper::Selector::parse(".next").unwrap(); let next_page_element = document.select(&next_page_element_selector).next(); // if this is not the last page if next_page_element.is_some() { // retrieve the relative URL to the next page let next_page_link_selector = scraper::Selector::parse("a").unwrap(); let partial_url = next_page_element .unwrap() .select(&next_page_link_selector) .next() .and_then(|a| a.value().attr("href")) .map(str::to_owned) .unwrap(); // the next page to scrape url = format!("https://quotes.toscrape.com{partial_url}"); } else { break; } }
Rust 数据爬取逻辑已完成!
步骤 8:将提取的数据导出到 CSV
爬取的数据现在存储在 Rust 对象中。您需要将其转换为不同的格式,以便团队的其他成员更容易阅读和使用。了解如何将收集的数据导出到 CSV 文件。
您可以创建一个 CSV 文件并使用普通 Rust 进行填充,但是 CSV 库让一切变得更加简单。安装它:
货物添加 csv
您现在只需几行代码即可将报价转换为 CSV 文件:
// initialize the CSV output file let path = std::path::Path::new("quotes.csv"); let mut writer = csv::Writer::from_path(path).unwrap(); // append the header to the CSV writer.write_record(&["quote", "author", "tags"]).unwrap(); // populate the CSV file for quote_obj in quotes { // if the "quote" and "author" fields are not None if let (Some(quote), Some(author)) = (quote_obj.quote, quote_obj.author) { let tags = quote_obj.tags.join("; "); writer.write_record(&[quote, author, tags]).unwrap(); } } // free up the writer resources writer.flush().unwrap(); The above snippet initializes a CSV file with the header row. Then, it iterates over the array of quotes, converts each element to records in CSV formats, and appends it to the output file.
第 9 步:将它们放在一起
以下是 Rust 爬虫的完整代码:
// define a custom data structure // where to store the scraped data struct Quote { quote: Option<String>, author: Option<String>, tags: Vec<String>, } fn main() { // initialize the vector that will store the objects // with the scraped quotes let mut quotes: Vec<Quote> = Vec::new(); // the URL of the first page to scrape let mut url = "https://quotes.toscrape.com/".to_owned(); loop { // download the HTML document associated with the target page let response = reqwest::blocking::get(url); // extract the raw HTML from the response let html = response.unwrap().text().unwrap(); // parse the HTML content let document = scraper::Html::parse_document(&html); // select all quote HTML elements on the page let html_quote_selector = scraper::Selector::parse(".quote").unwrap(); let html_quotes = document.select(&html_quote_selector); // iterate over each HTML quote to extract data for html_quote in html_quotes { // data scraping logic let quote_selector = scraper::Selector::parse(".text").unwrap(); let quote = html_quote .select("e_selector) .next() .map(|element| element.text().collect::<String>()); let author_selector = scraper::Selector::parse(".author").unwrap(); let author = html_quote .select(&author_selector) .next() .map(|element| element.text().collect::<String>()); let tags_selector = scraper::Selector::parse(".tag").unwrap(); let tags = html_quote .select(&tags_selector) .map(|element| element.text().collect::<String>()) .collect(); // create a new quote object with the scraped data // and store it in the list let quote_obj = Quote { quote, author, tags, }; quotes.push(quote_obj); } // select the "Next ->" element let next_page_element_selector = scraper::Selector::parse(".next").unwrap(); let next_page_element = document.select(&next_page_element_selector).next(); // if this is not the last page if next_page_element.is_some() { // retrieve the relative URL to the next page let next_page_link_selector = scraper::Selector::parse("a").unwrap(); let partial_url = next_page_element .unwrap() .select(&next_page_link_selector) .next() .and_then(|a| a.value().attr("href")) .map(str::to_owned) .unwrap(); // the next page to scrape url = format!("https://quotes.toscrape.com{partial_url}"); } else { break; } } // initialize the CSV output file let path = std::path::Path::new("quotes.csv"); let mut writer = csv::Writer::from_path(path).unwrap(); // append the header to the CSV writer.write_record(&["quote", "author", "tags"]).unwrap(); // populate the CSV file for quote_obj in quotes { // if the "quote" and "author" fields are not None if let (Some(quote), Some(author)) = (quote_obj.quote, quote_obj.author) { let tags = quote_obj.tags.join("; "); writer.write_record(&[quote, author, tags]).unwrap(); } } // free up the writer resources writer.flush().unwrap(); }
只需大约 100 行代码,您就可以在 Rust 中构建一个数据爬取器!
使用以下命令编译应用程序:
cargo build
然后,运行它:
cargo run
当 Rust 爬取器从每个页面检索数据时,请保持耐心。当它终止时,quotes.csv 文件将出现在项目的根文件夹中。打开它,您应该看到以下数据:
瞧!您从在线页面中的非结构化数据开始,现在将其保存在一个方便的 CSV 文件中!
结 论
在本指南中,您了解到为什么Rust是一种用于网络爬虫的高效语言。您还有机会探索一些最好的爬取库。然后,您学习了如何使用它们创建一个能够从真实目标提取数据的Rust爬虫。正如您所见,使用Rust进行网页爬取非常简单,并且只需要几行代码。
主要问题由反机器人和反爬虫系统构成,这些系统被网站采用以保护其数据。这些解决方案可以检测并阻止您的爬取脚本。幸运的是,Bright Data为您提供了一套解决方案:
- Web Scraper IDE:用于构建网络爬取工具的云 IDE,可以自动绕过和避免任何阻止。
- Scraping Browser:基于云的可控浏览器,提供 JavaScript 渲染功能,同时为您处理验证码、浏览器指纹识别、自动重试等。它与最流行的自动化浏览器库集成,例如 Playwright 和 Puppeteer。
- Web Unlocker:一种解锁 API,可以无缝返回任何页面的原始 HTML,规避任何反爬取措施。
如果您不想完全处理网络爬取但仍对在线数据感兴趣,请探索 Bright Data 的即用型数据集!