多執行緒連結檢查器

讓我們運用所學的新知識,建立多執行緒連結檢查工具。該工具應從網頁開始,檢查頁面上的連結是否有效,並應以遞迴方式檢查相同網域中的其他頁面,直到驗證完所有頁面為止。

為此,您需要使用 HTTP 用戶端,例如 reqwest。請建立新的 Cargo 專案,並使用以下指令,將 reqwest 設為依附元件:

cargo new link-checker
cd link-checker
cargo add --features blocking,rustls-tls reqwest

如果 cargo add 失敗並顯示 error: no such subcommand,請手動編輯 Cargo.toml 檔案。請新增下列依附元件。

您也需要設法找出連結。為此,我們可以使用 scraper

cargo add scraper

最後,我們需要處理錯誤的方法。為此,我們會使用 thiserror

cargo add thiserror

cargo add 呼叫會更新 Cargo.toml 檔案,如下所示:

[package]
name = "link-checker"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
reqwest = { version = "0.11.12", features = ["blocking", "rustls-tls"] }
scraper = "0.13.0"
thiserror = "1.0.37"

您現在可以下載起始網頁。請以小型網站嘗試這項操作,例如 https://www.google.org/

src/main.rs 檔案應如下所示:

use reqwest::blocking::Client;
use reqwest::Url;
use scraper::{Html, Selector};
use thiserror::Error;

#[derive(Error, Debug)]
enum Error {
    #[error("request error: {0}")]
    ReqwestError(#[from] reqwest::Error),
    #[error("bad http response: {0}")]
    BadResponse(String),
}

#[derive(Debug)]
struct CrawlCommand {
    url: Url,
    extract_links: bool,
}

fn visit_page(client: &Client, command: &CrawlCommand) -> Result<Vec<Url>, Error> {
    println!("Checking {:#}", command.url);
    let response = client.get(command.url.clone()).send()?;
    if !response.status().is_success() {
        return Err(Error::BadResponse(response.status().to_string()));
    }

    let mut link_urls = Vec::new();
    if !command.extract_links {
        return Ok(link_urls);
    }

    let base_url = response.url().to_owned();
    let body_text = response.text()?;
    let document = Html::parse_document(&body_text);

    let selector = Selector::parse("a").unwrap();
    let href_values = document
        .select(&selector)
        .filter_map(|element| element.value().attr("href"));
    for href in href_values {
        match base_url.join(href) {
            Ok(link_url) => {
                link_urls.push(link_url);
            }
            Err(err) => {
                println!("On {base_url:#}: ignored unparsable {href:?}: {err}");
            }
        }
    }
    Ok(link_urls)
}

fn main() {
    let client = Client::new();
    let start_url = Url::parse("https://www.google.org").unwrap();
    let crawl_command = CrawlCommand{ url: start_url, extract_links: true };
    match visit_page(&client, &crawl_command) {
        Ok(links) => println!("Links: {links:#?}"),
        Err(err) => println!("Could not extract links: {err:#}"),
    }
}

使用以下指令,在 src/main.rs 中執行程式碼:

cargo run

工作

  • 使用執行緒同時檢查連結:將要檢查的網址傳送到管道,讓幾個執行緒同時檢查網址。
  • 拓展這項功能,以遞迴方式擷取 www.google.org 網域中所有頁面上的連結。將頁面上限設為 100 個左右,以免遭到網站封鎖。