用 Rust 抓取網頁數據

在本文中,我們將學習通過使用 Rust 進行網頁數據抓取,文末有完整代碼。

我們將使用 Rust reqwest 和 scraper 這兩個流行的庫來抓取網站數據,我們將稍微討論一下這些庫。在本文的最後,你將對 Rust 的工作原理以及如何將其用於網絡數據抓取有一個基本的概念。

設置先決條件

首先,我們創建一個 rust 項目:

cargo new rust_tutorial

然後我們必須安裝兩個 Rust 庫,這兩個庫將在本教程中使用。

將它們添加到 Cargo.toml 文件中:

[dependencies]
reqwest = "0.10.8"
scraper = "0.12.0"
tokio = { version = "1.25.0", features = ["full"] }

 0.10.8 和 0.12.0 都是庫的最新版本。現在終於可以在項目文件 src/main.rs 中訪問它們了。

抓取什麼?

我們將從 “books.toscrape.com” 網站頁面上抓取每本書的書名和價格。

這個過程很簡單。首先,我們將檢查 chrome 以確定這些元素在 DOM 中的確切位置,然後我們將使用 scraper 庫來解析它們。

抓取單個圖書數據

讓我們一步一步地抓取書名和價格。首先,必須確定 DOM 元素的位置。

正如你在上面所看到的,圖書標題是存儲在標題屬性內的一個標籤。現在讓我們看看價格存儲在哪裏。

價格存儲在類 price_color 的標記下。現在,讓我們用 rust 編寫代碼並提取數據。

第一步是在 src/main.rs 中導入所有相關庫。

use reqwest::Client;
use scraper::{Html, Selector};

使用 reqwest,我們將建立到主機網站的 HTTP 連接,使用 scraper 庫,我們將解析 HTML 內容,我們將通過 reqwest 庫發出 GET 請求。

現在,我們必須創建一個客戶端,用於使用 reqwest 發送連接請求。

let client = Client::new();

最後,我們將使用上面創建的客戶端將 GET 請求發送到目標 URL。

let res = client.get("http://books.toscrape.com/").send().await?;

這裏我們使用 mut 修飾符將值綁定到變量。這提高了代碼的可讀性,一旦你在將來改變了這個值,你可能不得不改變代碼的其他部分。

因此,一旦請求發送,你將得到 HTML 格式的響應。但是你必須使用. text().unwrap() 從 res 變量中提取 HTML 字符串。

let body = res.text().await?;

這裏 res.text().unwrap() 將返回一個 HTML 字符串,我們將該字符串存儲在 body 變量中。

現在,我們有了一個字符串,通過它可以提取我們想要的所有數據。在使用 scraper 庫之前,我們必須使用 Html::parse_document 將此字符串轉換爲 scraper::Html 對象。

let document = Html::parse_document(&body);

現在,這個對象可以用於選擇元素和導航到所需的元素。

首先,讓我們爲書名創建一個選擇器。我們將使用 Selector::parse 函數創建一個 scraper::Selector 對象。

let book_title_selector = Selector::parse("h3 > a").unwrap();

現在可以使用該對象從 HTML 文檔中選擇元素。我們已經將 h3 > a 作爲參數傳遞給解析函數。這是我們感興趣的 CSS 元素選擇器。H3 > a 意味着它將選擇所有的 a 標籤,它們是 H3 標籤的子標籤。

正如你上圖中看到的,目標 a 標記是 h3 標記的子標記。因此,我們在上面的代碼中使用了 h3 > a。

由於有很多的書,我們將使用 for 循環遍歷所有的書。

for book_title in document.select(&book_title_selector) {
    let title = book_title.text().collect::<Vec<_>>();
    println!("Title: {}", title[0]);
}

select 方法將爲我們提供與選擇器 book_title_selector 匹配的元素列表。然後遍歷該列表以找到 title 屬性並最終打印它。

這裏 Vec<_>> 表示一個動態大小的數組。它是一個向量,你可以通過它在向量中的位置訪問任何元素。

下一步也是最後一步是提取價格。

let book_price_selector = Selector::parse(".price_color").unwrap();

我們再次使用 Selector::parse 函數創建 scraper::Selector 對象。如上所述,價格存儲在 price_color 類下。我們把這個作爲 CSS 選擇器傳遞給了 parse 函數。

然後,我們將再次使用 for 循環,就像我們上面所做的那樣,遍歷所有 price 元素。

for book_price in document.select(&book_price_selector) {
    let price = book_price.text().collect::<Vec<_>>();
    println!("Price: {}", price[0]);
}

一旦你找到匹配的選擇器,它將獲得文本並打印到控制檯上。

最後,我們完成了可以從目標 URL 提取標題和價格的代碼。現在,一旦你保存這個並使用 cargo run 運行代碼,你將得到類似這樣的輸出。

Title: A Light in the ...
Title: Tipping the Velvet
Title: Soumission
Title: Sharp Objects
Title: Sapiens: A Brief History ...
Title: The Requiem Red
Title: The Dirty Little Secrets ...
Title: The Coming Woman: A ...
Title: The Boys in the ...
Title: The Black Maria
Title: Starving Hearts (Triangular Trade ...
Title: Shakespeare's Sonnets
Title: Set Me Free
Title: Scott Pilgrim's Precious Little ...
Title: Rip it Up and ...
Title: Our Band Could Be ...
Title: Olio
Title: Mesaerion: The Best Science ...
Title: Libertarianism for Beginners
Title: It's Only the Himalayas
Price: £51.77
Price: £53.74
Price: £50.10
Price: £47.82
Price: £54.23
Price: £22.65
Price: £33.34
Price: £17.93
Price: £22.60
Price: £52.15
Price: £13.99
Price: £20.66
Price: £17.46
Price: £52.29
Price: £35.02
Price: £57.25
Price: £23.88
Price: £37.59
Price: £51.33
Price: £45.17

完整代碼

你可以對代碼進行更多更改,以提取其他信息,如星級、圖書圖像等。可以使用相同的技術,首先檢查和查找元素的位置,然後使用 Selector 函數提取它們。

use reqwest::Client;
use scraper::{Html, Selector};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
   // Create a new client
    let client = Client::new();

    // Send a GET request to the website
    let res = client.get("http://books.toscrape.com/")
        .send().await?;

    // Extract the HTML from the response
    let body = res.text().await?;

    // Parse the HTML into a document
    let document = Html::parse_document(&body);

    // Create a selector for the book titles
    let book_title_selector = Selector::parse("h3 > a").unwrap();

    // Iterate over the book titles
    for book_title in document.select(&book_title_selector) {
        let title = book_title.text().collect::<Vec<_>>();
        println!("Title: {}", title[0]);
    }

    // Create a selector for the book prices
    let book_price_selector = Selector::parse(".price_color").unwrap();

    // Iterate over the book prices
    for book_price in document.select(&book_price_selector) {
        let price = book_price.text().collect::<Vec<_>>();
        println!("Price: {}", price[0]);
    }

    Ok(())
}

總結

我們學習了 Rust 如何用於網頁抓取。使用 Rust,你也可以抓取許多其他動態網站。即使在上面的代碼中,你也可以做一些更改來抓取圖像和評級。這肯定會提高你的使用 Rust 進行網絡抓取的技能。

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