Utilisation de Rust pour le Web Scraping

Feb 13 2023
Le Web Scraping est une technique d'extraction d'informations à partir de sites Web. Il s'agit d'un processus automatisé de récupération de grandes quantités de données à partir de sites Web, ce qui permet d'économiser du temps et des efforts et de rendre les informations disponibles à diverses fins.

Le Web Scraping est une technique d'extraction d'informations à partir de sites Web. Il s'agit d'un processus automatisé de récupération de grandes quantités de données à partir de sites Web, ce qui permet d'économiser du temps et des efforts et de rendre les informations disponibles à diverses fins.

Dans cet article, je vais vous montrer comment vous pouvez utiliser Rust pour extraire des citations d'un site Web.

Avantages de l'utilisation de Rust pour le Web Scraping

  1. Performances : Rust est un langage compilé à typage statique qui offre des performances élevées, ce qui en fait un choix idéal pour le scraping Web.
  2. Concurrence : Rust fournit une prise en charge intégrée de la concurrence, ce qui facilite la gestion simultanée de plusieurs requêtes, améliorant ainsi l'efficacité du grattage Web.
  3. Sécurité de la mémoire : Rust fournit de solides garanties de sécurité de la mémoire, ce qui aide à prévenir les bogues courants liés à la mémoire, tels que les exceptions de pointeur nul et les débordements de mémoire tampon, ce qui le rend plus sûr pour le grattage Web.
  4. Facile à apprendre : Rust a une syntaxe relativement simple, ce qui facilite l'apprentissage des développeurs, par rapport aux autres langages de programmation.
  1. Courbe d'apprentissage abrupte : bien que la syntaxe de Rust soit relativement simple, le langage peut être difficile à apprendre, en particulier pour les développeurs qui découvrent les langages compilés à typage statique.
  2. Communauté limitée : bien que Rust ait une communauté grandissante, il n'est toujours pas aussi largement utilisé que d'autres langages de programmation, ce qui signifie qu'il peut être difficile de trouver des ressources et de l'assistance.
  3. Pas de bibliothèque de scraping Web intégrée : Rust n'a pas de bibliothèque intégrée pour le scraping Web, ce qui signifie que les développeurs doivent utiliser des bibliothèques tierces, ce qui peut être moins pratique.

Maintenant, nous allons installer Rust, vérifier la version de Cargoet installer trois bibliothèques de grattage Web, reqwest, scraperet selectvia l'interface de ligne de commande.

Installer la rouille

Pour installer Rust, vous pouvez utiliser l'outil Rustup, qui est la méthode recommandée pour installer Rust. Vous pouvez exécuter la commande suivante dans votre terminal :

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Installer la cargaison

Cargo est le gestionnaire de paquets pour Rust, et il est installé automatiquement lorsque vous installez Rust. Vous pouvez vérifier si Cargo est installé en exécutant la commande suivante :

cargo --version

Exécutez la commande suivante dans votre terminal :

cargo new toscrape_quotes

Pour installer les bibliothèques de scraping Web Rust reqwest, scraperet select, vous pouvez ajouter les dépendances suivantes à votre Cargo.tomlfichier :

# 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 .

Faisons du Web Scraping. On y va?

Toscrape.com est un bac à sable de grattage Web pour ceux qui souhaitent se lancer dans une telle entreprise en génie logiciel.

Sans attirer l'attention indésirable des avocats d'entreprise des plates-formes ou des organisations d'entreprise, vous pouvez l'utiliser comme terrain de jeu. Ouais, vous ne voulez pas voir ces gens ! Certains portent des costumes jumelés avec des Air Jordan 1 Chicago .

Code de grattage Web de rouille

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(&quotes)?;
    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(&quote_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));
    }
}

La mainfonction

La mainfonction est le point d'entrée du code Rust, et elle effectue les étapes suivantes :

  1. Crée un nouveau client HTTP à l'aide de reqwest::Client.
  2. Appelle la get_quotesfonction et lui passe le client, qui renvoie un vecteur de Quotestructures.
  3. Appelle la write_quotes_to_csvfonction et Quotelui transmet le vecteur de structs, qui écrit les guillemets dans un fichier .csv.
  4. Renvoie le résultat de la write_quotes_to_csvfonction, qui est soit Ok(())si tout a réussi, soit une erreur si quelque chose s'est mal passé.

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(&quotes)?;
    Ok(())
}

La get_quotesfonction prend une référence à reqwest::Clientcomme argument et renvoie un vecteur de Quotestructures extraites du site Web "http://quotes.toscrape.com". Voici une ventilation de ce que nous faisons :

  1. Initialiser un vecteur vide de Quotestructs.
  2. Parcourez les pages à gratter, de 1 à MAX_PAGE.
  3. Pour chaque page, faire une requête GET à l'URL de la page à l'aide de la retry_getfonction et récupère le HTML de la page.
  4. Analysez le HTML en un scraper::Htmlfragment.
  5. Sélectionnez les guillemets text, authoret tagsdans le code HTML à l'aide des sélecteurs CSS.
  6. Créez une nouvelle Quotestructure pour chaque citation et ajoutez-la au vecteur de citations.
  7. Renvoie le vecteur de guillemets.

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(&quote_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)
}

La write_quotes_to_csvfonction prend une référence pour découper les Quotestructures et écrire les guillemets dans un .csvfichier. Voici une ventilation de ce que nous faisons :

  1. Créez un nouveau .csvfichier avec le nom toscrape_quotes.csv.
  2. Créez-en un nouveau csv::Writerà l'aide de la csvcaisse.
  3. Écrivez un nouvel enregistrement dans le fichier .csv pour chaque citation dans la tranche de citations, avec les citations text, authoret tagsséparées par des virgules.
  4. Videz l'enregistreur pour vous assurer que toutes les données sont écrites dans le fichier.
  5. Renvoie le résultat de l'opération d'écriture, qui est soit Ok(())si tout a réussi, soit une erreur si quelque chose s'est mal passé.

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(())
}

La retry_getfonction prend une référence à reqwest::Clientet une référence à l'URL de chaîne, et elle envoie une requête HTTP GET à l'URL à l'aide du client. Si la requête échoue, la fonction réessaie la requête un maximum de MAX_RETRIESfois avec un temps de veille de RETRY_SLEEP_SECONDSsecondes entre chaque nouvelle tentative. Voici une ventilation de ce que nous faisons :

  1. Initialiser un compteur pour le nombre de tentatives.
  2. Envoyez une requête GET à l'URL à l'aide du client et récupérez la réponse.
  3. Si la réponse est réussie (c'est-à-dire si le code d'état de la réponse est dans la plage 2xx), renvoyez la réponse.
  4. Si la réponse échoue, incrémentez le compteur de tentatives.
  5. Si le compteur de tentatives est inférieur à MAX_RETRIES, attendez quelques RETRY_SLEEP_SECONDSsecondes et relancez la requête GET.
  6. Si le compteur de tentatives est égal à MAX_RETRIES, renvoie une erreur indiquant que la requête GET a échoué.

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));
    }
}

Cependant, Rust est un langage relativement nouveau par rapport aux langages de programmation de grattage Web plus avancés comme Python. Par conséquent, il peut avoir une communauté plus petite et moins de bibliothèques disponibles. La syntaxe de Rust peut également être plus difficile pour les débutants, en particulier ceux qui sont familiers avec les langages de programmation de haut niveau comme Python avec une sémantique dynamique.

Soyez intelligent, amusez-vous à gratter le Web et évitez les avocats d'entreprise. Il y aura des avocats, cependant! Hashtag, quoi ? # Facturables