Usando Rust para Web Scraping
Web Scraping é uma técnica de extração de informações de sites. É um processo automatizado de obtenção de grandes quantidades de dados de sites, economizando tempo e esforço e disponibilizando as informações para diversos fins.
Neste post, vou demonstrar como você pode usar o Rust para extrair citações de um site.
Vantagens de usar Rust para Web Scraping
- Desempenho : Rust é uma linguagem compilada de tipo estático que oferece alto desempenho, tornando-a uma escolha ideal para raspagem na web.
- Simultaneidade : Rust fornece suporte de simultaneidade integrado, o que facilita o manuseio de várias solicitações simultaneamente, melhorando a eficiência da raspagem da web.
- Segurança de memória : Rust fornece fortes garantias de segurança de memória, o que ajuda a evitar erros comuns relacionados à memória, como exceções de ponteiro nulo e estouros de buffer, tornando-o mais seguro para raspagem da web.
- Fácil de aprender : Rust tem uma sintaxe relativamente simples, facilitando o aprendizado dos desenvolvedores, em comparação com outras linguagens de programação.
- Curva de aprendizado íngreme : embora a sintaxe do Rust seja relativamente simples, a linguagem pode ser difícil de aprender, especialmente para desenvolvedores que são novos em linguagens compiladas de tipagem estática.
- Comunidade limitada : embora o Rust tenha uma comunidade crescente, ainda não é tão amplamente usado quanto outras linguagens de programação, o que significa que encontrar recursos e suporte pode ser difícil.
- Nenhuma biblioteca integrada de raspagem da Web : Rust não possui uma biblioteca integrada para raspagem da Web, o que significa que os desenvolvedores precisam usar bibliotecas de terceiros, o que pode ser menos conveniente.
Agora, instalaremos Rust, verificaremos a versão de Cargoe instalaremos três bibliotecas de raspagem da web, reqwest, scrapere selectpor meio da interface de linha de comando.
Instalar Rust
Para instalar o Rust, você pode usar a ferramenta Rustup, que é a maneira recomendada de instalar o Rust. Você pode executar o seguinte comando em seu terminal:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Instalar carga
Cargo é o gerenciador de pacotes do Rust e é instalado automaticamente quando você instala o Rust. Você pode verificar se o Cargo está instalado executando o seguinte comando:
cargo --version
Execute o seguinte comando em seu terminal:
cargo new toscrape_quotes
Para instalar as bibliotecas de raspagem da Web Rust reqwest, scrapere select, você pode adicionar as seguintes dependências ao seu Cargo.tomlarquivo:
# Do not forget to update the dependencies section of your Cargo.toml file
[dependencies]
csv = "1.1"
reqwest = "0.10.8"
scraper = "0.13.0"
select = "0.6.0"
cd toscrape_quotes && cargo install --path .
Vamos fazer uma raspagem na Web. Devemos nós?
Toscrape.com é um sandbox de raspagem da web para aqueles que desejam se aventurar em tal empreendimento em engenharia de software.
Sem atrair atenção indesejada de advogados corporativos de plataformas ou organizações corporativas, você pode usá-lo como seu playground. Sim, você não quer ver essas pessoas! Alguns usam ternos combinados com Air Jordan 1 Chicago .
Rust Web Scraping Code
extern crate csv;
extern crate reqwest;
extern crate scraper;
use std::error::Error;
use std::fs::File;
use std::time::Duration;
use std::thread::sleep;
use reqwest::Client;
use reqwest::StatusCode;
use scraper::{Html, Selector};
const BASE_URL: &str = "http://quotes.toscrape.com";
const MAX_PAGE: u32 = 5;
const MAX_RETRIES: u32 = 3;
const RETRY_SLEEP_SECONDS: u64 = 2;
struct Quote {
text: String,
author: String,
tags: Vec<String>,
}
fn main() -> Result<(), Box<dyn Error>> {
let client = Client::new().timeout(Duration::from_secs(30)).build()?;
let quotes = get_quotes(&client)?;
write_quotes_to_csv("es)?;
Ok(())
}
fn get_quotes(client: &Client) -> Result<Vec<Quote>, Box<dyn Error>> {
let mut quotes = Vec::new();
for page in 1..=MAX_PAGE {
let html = retry_get(client, &format!("{}/page/{}/", BASE_URL, page))?.text()?;
let fragment = Html::parse_document(&html);
let quote_selector = Selector::parse("span.text").unwrap();
let author_selector = Selector::parse("span > small.author").unwrap();
let tag_selector = Selector::parse("div.tags > a.tag").unwrap();
for quote in fragment.select("e_selector) {
let quote_text = quote.text().collect::<Vec<_>>()[0].trim().to_owned();
let author = quote
.parent()
.unwrap()
.select(&author_selector)
.next()
.unwrap()
.text()
.collect::<Vec<_>>()[0]
.trim()
.to_owned();
let tags = quote
.parent()
.unwrap()
.select(&tag_selector)
.map(|tag| tag.text().collect::<Vec<_>>()[0].trim().to_owned())
.collect::<Vec<_>>();
quotes.push(Quote {
text: quote_text,
author,
tags,
});
}
}
Ok(quotes)
}
fn write_quotes_to_csv(quotes: &[Quote]) -> Result<(), Box<dyn Error>> {
let mut file = File::create("toscrape_quotes.csv")?;
let mut writer = csv::Writer::from_writer(&mut file);
for quote in quotes {
writer.write_record(&[quote.text, quote.author, quote.tags.join(", ")])?;
}
writer.flush()?;
Ok(())
}
fn retry_get(client: &Client, url: &str) -> Result<reqwest::Response, Box<dyn Error>> {
let mut retries = 0;
loop {
match client.get(url).send() {
Ok(res) => {
if res.status().is_success() {
return Ok(res);
} else {
retries += 1;
if retries >= MAX_RETRIES {
return Err(format!("GET request to {} failed with status code {:?}", url, res.status()).into());
}
}
}
Err(err) => {
retries += 1;
if retries >= MAX_RETRIES {
return Err(format!("GET request to {} failed with error: {}", url, err).into());
}
}
}
sleep(Duration::from_secs(RETRY_SLEEP_SECONDS));
}
}
A mainFunção
A mainfunção é o ponto de entrada do código Rust e executa as seguintes etapas:
- Cria um novo cliente HTTP usando
reqwest::Client. - Chama a
get_quotesfunção e passa o cliente para ela, que retorna um vetor deQuotestructs. - Chama a
write_quotes_to_csvfunção e passa o vetor deQuoteestruturas para ela, que grava as aspas em um arquivo .csv. - Retorna o resultado da
write_quotes_to_csvfunção, que éOk(())se tudo deu certo ou um erro se algo deu errado.
fn main() -> Result<(), Box<dyn Error>> {
let client = Client::new().timeout(Duration::from_secs(30)).build()?;
let quotes = get_quotes(&client)?;
write_quotes_to_csv("es)?;
Ok(())
}
A get_quotesfunção pega uma referência a reqwest::Clientcomo argumento e retorna um vetor de Quoteestruturas que são extraídas do site "http://quotes.toscrape.com". Aqui está um detalhamento do que estamos fazendo:
- Inicialize um vetor vazio de
Quotestructs. - Faça um loop pelas páginas a serem raspadas, de 1 a
MAX_PAGE. - Para cada página, faça uma solicitação GET para a URL da página usando a
retry_getfunção e recupere o HTML da página. - Analise o HTML em um
scraper::Htmlfragmento. - Selecione a citação
text,authoretagsno HTML usando seletores CSS. - Crie uma nova
Quoteestrutura para cada citação e anexe-a ao vetor de citações. - Retorne o vetor de aspas.
fn get_quotes(client: &Client) -> Result<Vec<Quote>, Box<dyn Error>> {
let mut quotes = Vec::new();
for page in 1..=MAX_PAGE {
let html = retry_get(client, &format!("{}/page/{}/", BASE_URL, page))?.text()?;
let fragment = Html::parse_document(&html);
let quote_selector = Selector::parse("span.text").unwrap();
let author_selector = Selector::parse("span > small.author").unwrap();
let tag_selector = Selector::parse("div.tags > a.tag").unwrap();
for quote in fragment.select("e_selector) {
let quote_text = quote.text().collect::<Vec<_>>()[0].trim().to_owned();
let author = quote
.parent()
.unwrap()
.select(&author_selector)
.next()
.unwrap()
.text()
.collect::<Vec<_>>()[0]
.trim()
.to_owned();
let tags = quote
.parent()
.unwrap()
.select(&tag_selector)
.map(|tag| tag.text().collect::<Vec<_>>()[0].trim().to_owned())
.collect::<Vec<_>>();
quotes.push(Quote {
text: quote_text,
author,
tags,
});
}
}
Ok(quotes)
}
A write_quotes_to_csvfunção usa uma referência para dividir as Quoteestruturas e gravar as aspas em um .csvarquivo. Aqui está um detalhamento do que estamos fazendo:
- Crie um novo
.csvarquivo com o nometoscrape_quotes.csv. - Crie um novo
csv::Writerusando acsvcaixa. - Grave um novo registro no arquivo .csv para cada citação na fatia de citações, com aspas
text,authoretagsseparadas por vírgulas. - Descarregue o gravador para garantir que todos os dados sejam gravados no arquivo.
- Retorne o resultado da operação de gravação, que é
Ok(())se tudo foi bem-sucedido ou um erro se algo deu errado.
fn write_quotes_to_csv(quotes: &[Quote]) -> Result<(), Box<dyn Error>> {
let mut file = File::create("toscrape_quotes.csv")?;
let mut writer = csv::Writer::from_writer(&mut file);
for quote in quotes {
writer.write_record(&[quote.text, quote.author, quote.tags.join(", ")])?;
}
writer.flush()?;
Ok(())
}
A retry_getfunção usa uma referência reqwest::Cliente uma referência ao URL da string e faz uma solicitação HTTP GET para o URL usando o cliente. Se a solicitação falhar, a função repetirá a solicitação no máximo de MAX_RETRIESvezes com um tempo de espera de RETRY_SLEEP_SECONDSsegundos entre cada nova tentativa. Aqui está um detalhamento do que estamos fazendo:
- Inicialize um contador para o número de tentativas.
- Faça uma solicitação GET para a URL usando o cliente e recupere a resposta.
- Se a resposta for bem-sucedida (ou seja, o código de status da resposta estiver no intervalo 2xx), retorne a resposta.
- Se a resposta não for bem-sucedida, incremente o contador de novas tentativas.
- Se o contador de tentativas for menor que
MAX_RETRIES, aguarde algunsRETRY_SLEEP_SECONDSsegundos e repita a solicitação GET. - Se o contador de novas tentativas for igual a
MAX_RETRIES, retorna um erro indicando que a solicitação GET falhou.
fn retry_get(client: &Client, url: &str) -> Result<reqwest::Response, Box<dyn Error>> {
let mut retries = 0;
loop {
match client.get(url).send() {
Ok(res) => {
if res.status().is_success() {
return Ok(res);
} else {
retries += 1;
if retries >= MAX_RETRIES {
return Err(format!("GET request to {} failed with status code {:?}", url, res.status()).into());
}
}
}
Err(err) => {
retries += 1;
if retries >= MAX_RETRIES {
return Err(format!("GET request to {} failed with error: {}", url, err).into());
}
}
}
sleep(Duration::from_secs(RETRY_SLEEP_SECONDS));
}
}
No entanto, Rust é uma linguagem relativamente nova em comparação com linguagens de programação de raspagem da web mais avançadas, como Python. Portanto, pode ter uma comunidade menor e menos bibliotecas disponíveis. A sintaxe do Rust também pode ser mais difícil para iniciantes, especialmente aqueles familiarizados com linguagens de programação de alto nível como Python com semântica dinâmica.
Seja esperto, divirta-se vasculhando a web e evite advogados corporativos. Haverá advogados, no entanto! Hashtag, o que? #Faturaveis





































![O que é uma lista vinculada, afinal? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)