Verificador de Links Multi-Threads
Vamos usar nosso novo conhecimento para criar um verificador de links multi-threads. Comece em uma pĂĄgina da web e verifique se os links na pĂĄgina sĂŁo vĂĄlidos. Verifique recursivamente outras pĂĄginas no mesmo domĂnio e continue fazendo isso atĂ© que todas as pĂĄginas tenham sido validadas.
Para isso, vocĂȘ precisarĂĄ de um cliente HTTP como reqwest. Crie um novo Project com o Cargo e adicione reqwest como uma dependĂȘncia:
cargo new link-checker
cd link-checker
cargo add --features blocking,rustls-tls reqwest
Se
cargo addfalhar comerror: no such subcommand, edite o arquivoCargo.tomlĂ mĂŁo. Adicione as dependĂȘncias listadas abaixo.
VocĂȘ tambĂ©m precisarĂĄ de uma maneira de encontrar links. Podemos usar scraper para isso:
cargo add scraper
Por fim, precisaremos de alguma forma de lidar com os erros. Usamos thiserror para isso:
cargo add thiserror
As chamadas cargo add irĂŁo atualizar o arquivo Cargo.toml para ficar assim:
[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"
Agora vocĂȘ pode baixar a pĂĄgina inicial. Tente com um pequeno site como https://www.google.org/.
Seu arquivo src/main.rs deve se parecer com isto:
use reqwest::{blocking::Client, 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:#}"), } }
Execute o cĂłdigo em src/main.rs com
cargo run
Tarefas
- Use threads para verificar os links em paralelo: envie as URLs a serem verificadas para um channel e deixe alguns threads verificarem as URLs em paralelo.
- Estenda isso para extrair recursivamente links de todas as pĂĄginas no domĂnio
www.google.org. Coloque um limite mĂĄximo de 100 pĂĄginas ou menos para que vocĂȘ nĂŁo acabe sendo bloqueado pelo site.