用 Rust 實現 web 信息抓取

假設你想從一個網站上獲得一些信息,比如股票價格、最新的廣告等信息。最簡單的方法是調用一個 API 請求所需的信息,如果該網站有免費 API 的話。如果沒有的話,還有第二種選擇:網頁抓取。

你不需要連接到一個 “官方” 資源,你可以使用一個機器人來抓取網站的內容並解析以找到你需要的東西。

在本文中,你將學習如何使用 Rust 實現 web 抓取。你將使用兩個 Rust 庫,requestwest 和 scraper,從 IMDb 中抓取前 100 部電影列表。

創建項目

首先,你需要創建一個基本的 Rust 項目,並添加將使用的所有依賴。這最好用 Cargo 完成。生成一個 Rust 二進制項目,運行:

cargo new web_scraper

接下來,將所需的庫添加到依賴中。對於這個項目,將使用 requestwest 和 scraper。在你最喜歡的代碼編輯器中打開 web_scraper 文件夾並打開 cargo.toml 文件。在文件的最後,添加庫:

[dependencies]
reqwest = {version = "0.11", features = ["blocking"]}
scraper = "0.12.0"

現在你可以移動到 src/main.rs,開始編寫 web 信息抓取程序。

獲取網站的 HTML

抓取頁面通常包括獲取頁面的 HTML 代碼,然後解析它以找到所需的信息。因此,需要在 Rust 程序中使用 IMDb 頁面的代碼。要做到這一點,首先需要了解瀏覽器是如何工作的,因爲它們是與網頁交互的常用方式。

HTTP 有各種不同類型的請求,例如 GET(用於獲取資源的內容) 和 POST(用於向服務器發送信息)。要在 Rust 程序中獲得 IMDb 網頁的代碼,您需要通過向 IMDb 發送 HTTP get 請求來模擬瀏覽器的行爲。

在 Rust 中,你可以使用 reqwest crate。這個常用的 Rust 庫提供了 HTTP 客戶端的特性。它可以做很多普通瀏覽器可以做的事情,比如打開頁面、登錄和存儲 cookie。

要請求頁面的代碼,可以使用 requestwest::blocking::get 方法:

fn main() {
    let response = reqwest::blocking::get("https://www.imdb.com/search/title/?groups=top_100&sort=user_rating,desc&count=100")
        .unwrap()
        .text()
        .unwrap();
}

response 現在將包含請求頁面的完整 HTML 代碼。

從 HTML 中提取信息

web 抓取項目中最難的部分通常是從 HTML 文檔中獲取你需要的特定信息。爲了達到這個目的,Rust 中一個常用的工具是 scraper 庫。它的工作原理是將 HTML 文檔解析爲樹狀結構。可以使用 CSS 選擇器查詢感興趣的元素。

第一步是使用庫解析你獲取的整個 HTML:

let document = scraper::Html::parse_document(&response);

接下來,找到並選擇需要的部件。要做到這一點,需要檢查網站的代碼,並找到唯一標識這些條目的 CSS 選擇器集合。

最簡單的方法是通過瀏覽器,找到你需要的元素,然後檢查該元素的代碼:

對於 IMDb,你需要的元素是電影的名稱。當你檢查元素時,你會看到它被包裝在一個 標籤中:

<a href="/title/tt0111161/?ref_=adv_li_tt">The Shawshank Redemption</a>

不幸的是,這個標籤不是唯一的。由於頁面上有很多 標籤,將它們全部抓取不是一個明智的主意,因爲其中大多數都不是你需要的條目。相反,找到電影標題唯一的標記,然後導航到該標記內的 < a>。在這種情況下,你可以選擇 lister-item-header 類:

<h3>
    <span>1.</span>
    <a href="/title/tt0111161/?ref_=adv_li_tt">The Shawshank Redemption</a>
    <span>(1994)</span>
</h3>

現在需要使用 scraper::Selector::parse 方法創建一個查詢。

使用 h3.lister-item-header>a 選擇器。它表示找到了 標記,該標記的父標記爲 < h3 > 標記,該標記屬於一個 lister-item-header 類。使用方式如下:

let title_selector = scraper::Selector::parse("h3.lister-item-header>a").unwrap();

現在可以使用 select 方法將此查詢應用於已解析的文檔。爲了獲得電影的實際標題而不是 HTML 元素,你需要將每個 HTML 元素映射到它內部的 HTML:

let titles = document.select(&title_selector).map(|x| x.inner_html());

titles 現在是一個迭代器,包含所有前 100 個標題的名稱。

你現在要做的就是把這些名字打印出來。要做到這一點,首先標題列表進行 zip。然後在得到的迭代器上調用 for_each 方法,它將在單獨的一行上打印迭代器中的每一項:

titles.zip(1..101).for_each(|(item, number)| println!("{}. {}", number, item));

現在,你的 web 信息抓取器完成了。如果你保存文件並使用 cargo run 運行它,你將獲得前 100 部電影的列表:

1. The Shawshank Redemption
2. The Godfather
3. The Dark Knight
4. The Lord of the Rings: The Return of the King
5. Schindler's List
6. The Godfather: Part II
7. 12 Angry Men
8. Pulp Fiction
9. Inception
10. The Lord of the Rings: The Two Towers
...

總結

在本教程中,你學習瞭如何使用 Rust 創建一個簡單的 web 信息抓取器。根據你的需要,有許多方法可以升級這個 web 信息抓取器。

然而,有時使用 CSS 選擇器是不夠的。您可能需要更高級的解決方案來模擬真實瀏覽器所採取的操作。在這種情況下,你可以使用 thirtyfour 庫,用於更強大的 web 抓取操作。

本文翻譯自:

https://www.scrapingbee.com/blog/web-scraping-rust/

coding 到燈火闌珊 專注於技術分享,包括 Rust、Golang、分佈式架構、雲原生等。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/wt45NGWchB-mrZvUEI_MXA