paint-brush
Analizando HTML con Rust: un tutorial simple usando Tokio, Reqwest y Scraperpor@exactor
13,489 lecturas
13,489 lecturas

Analizando HTML con Rust: un tutorial simple usando Tokio, Reqwest y Scraper

por Maksim Kuznetsov6m2022/01/14
Read on Terminal Reader
Read this story w/o Javascript

Demasiado Largo; Para Leer

En este artículo, escribí algunos pequeños programas para mostrarle cómo funciona el análisis de páginas web en Rust. Elegí: Tokio, Reqwest, bibliotecas Scraper. Tokio tiene como objetivo proporcionar componentes básicos para escribir programas asincrónicos confiables y rápidos en Rust.

Company Mentioned

Mention Thumbnail
featured image - Analizando HTML con Rust: un tutorial simple usando Tokio, Reqwest y Scraper
Maksim Kuznetsov HackerNoon profile picture

En este artículo, escribí algunos pequeños programas para mostrarle cómo funciona el análisis de páginas web en Rust.

Para esta tarea, elegí: bibliotecas Tokio, Reqwest y Scraper .

Tokio

Es una biblioteca para escribir aplicaciones confiables, asincrónicas y delgadas con el lenguaje de programación Rust.

Por qué elijo Tokio:

  • Es rápido: Tokio usa abstracciones de costo cero para asíncrono.
  • Es seguro: Tokio usa solo Rust seguro y paralelismo basado en Rust.
  • Es escalable: Huella mínima en su aplicación.
  • Tokio es también una biblioteca asíncrona más popular que async-std.
  • Eso utiliza la multitarea cooperativa basada en hilos verdes.

Lista de proyectos que usan Tokio, basada en el repositorio Github de Tokio :

  • hyper: una implementación HTTP rápida y correcta para Rust.
  • tónico: una implementación oxidada de gRPC.
  • warp: un marco de servidor web componible súper fácil para velocidades warp.
  • torre: Una biblioteca de componentes modulares y reutilizables para crear servidores y clientes de red robustos.
  • seguimiento: es un marco para instrumentar programas Rust para recopilar información de diagnóstico estructurada basada en eventos.
  • rdbc: una biblioteca de conectividad de base de datos Rust (RDBC) para MySQL, Postgres y SQLite.
  • mio: biblioteca de E/S rápida y de bajo nivel para Rust que se centra en las API sin bloqueo.
  • bytes: una biblioteca de utilidades para trabajar con bytes.
  • telar: la herramienta de prueba para el código Rust concurrente.

Reqoeste

Un cliente HTTP ergonómico con baterías incluidas para Rust.

Características:

  • Cuerpos sin formato, JSON, codificado en urlen, multiparte
  • Política de redirección personalizable
  • Proxies HTTP
  • HTTPS a través de TLS nativo del sistema (u opcionalmente, rustls)
  • tienda de galletas
  • ERA M

Reqwest usa Tokio para solicitudes asíncronas y tiene un modo de trabajo de bloqueo. En este artículo, utilicé el modo asíncrono.

Raspador

Scraper proporciona una interfaz para html5ever y cajas de selectores de Servo, para análisis y consultas de nivel de navegador.

Primer programa: scrapper simple para páginas de "índice de".

 use scraper::{Html, Selector}; #[tokio::main]
async fn main () -> Result <(), Box < dyn std::error::Error>> {    let resp = reqwest::get( "https://www.wireshark.org/download/" ). await ?;    let text = resp.text(). await ?;    let document = Html::parse_document(&text);    let selector = Selector::parse( r#"table > tbody > tr > td > a"# ).unwrap();    for title in document.select(&selector) {        println! ( "{}" , resp.url().to_string());        println! (            "{}" , title .value() .attr( "href" ) .expect( "href not found" ) .to_string() ); }    Ok (()) }

Aquí hay una pequeña actualización para este programa. Aquí la página de Whireshark tiene enlaces duplicados. Podemos ignorarlos usando un hashmap.

 use scraper::{Html, Selector}; use std::collections::HashSet; #[tokio::main]
async fn main () -> Result <(), Box < dyn std::error::Error>> {    let resp = reqwest::get( "https://www.wireshark.org/download/" ). await ?;    println! ( "{}" , resp.url().to_string());    let text = resp.text(). await ?;    let mut urls:HashSet< String > = HashSet::new();    let document = Html::parse_document(&text);    let selector = Selector::parse( r#"table > tbody > tr > td > a"# ).unwrap();    for title in document.select(&selector) {        let url = title.value().attr( "href" ).expect( "href not found" ).to_string();        if url != "/" || url != "." || url != ".." { urls.insert(url); } }    for url in urls{        println! ( "{}" , url); }    Ok (()) }

Para la próxima iteración, quiero mostrarles cómo hacer un analizador menos complejo, pero con más funciones.

Tarea: recopila nuevos fondos de pantalla del sitio web de Wallheaven.

En este sitio, puede encontrar el botón "aleatorio" que le brinda 24 imágenes aleatorias. Para esta tarea, necesitamos analizar esta página. Después de eso, vaya a la URL de la imagen, obtenga una URL de imagen de tamaño completo y descargue la imagen.

 use scraper::{Html, Selector}; use std::io::Cursor; use std::process; #[tokio::main]
async fn main () -> Result <(), Box < dyn std::error::Error>> {    let resp = match reqwest::get( "https://wallhaven.cc/random" ). await {        Ok (x) => x,        Err (_) => {            println! ( "{}" , "error on /random request" ); process::exit( 0x0100 ); //exit if error expected during request to /random
 } };    let text = resp.text(). await ?;    let document = Html::parse_document(&text);    let selector = Selector::parse( r#"ul > li > figure > a"# ).unwrap();    for elem in document.select(&selector) {        let href = elem.value().attr( "href" ).expect( "href not found!" ); download(&href.to_string()). await ?; }    Ok (()) } async fn download (url: & String ) -> Result <(), Box < dyn std::error::Error>> {    let resp = reqwest::get(url). await ?;    let text = resp.text(). await ?;    let document = Html::parse_document(&text);    let selector = Selector::parse( r#"img"# ).unwrap();    for elem in document.select(&selector) {        let picture_url = elem.value().attr( "data-cfsrc" );        match picture_url {            Some (x) => {	    // ignoring another pictures on page like logo and avatar
                if x.contains( "avatar" ) || x.contains( "logo" ) {                    continue ; }	    // trying to get original name of picture
                let file_path = x.split( "/" ).last().expect( "cant find filename" );                match reqwest::get(x). await {                    Ok (x) => {                        let mut file = std::fs::File::create(file_path)?;                        let mut content = Cursor::new(x.bytes(). await ?); std::io::copy(& mut content, & mut file)?;                        println! ( "Created: {}" , file_path); }                    Err (_) => {                        continue ; } }; }            None => {} } }    Ok (()) }

En conclusión, quiero mostrar y resolver algunas tareas prácticas de análisis sintáctico con fines educativos y refactorizados no tan bien como sea posible. Para la producción, vea mejores ejemplos y desarrolle sus propias tareas.