Verwendung von Rust für Web Scraping

Feb 13 2023
Web Scraping ist eine Technik zum Extrahieren von Informationen aus Websites. Dabei handelt es sich um einen automatisierten Prozess zum Abrufen großer Datenmengen von Websites, der Zeit und Mühe spart und die Informationen für verschiedene Zwecke verfügbar macht.

Web Scraping ist eine Technik zum Extrahieren von Informationen aus Websites. Dabei handelt es sich um einen automatisierten Prozess zum Abrufen großer Datenmengen von Websites, der Zeit und Mühe spart und die Informationen für verschiedene Zwecke verfügbar macht.

In diesem Beitrag werde ich zeigen, wie Sie Rust verwenden können , um Zitate von einer Website zu extrahieren.

Vorteile der Verwendung von Rust für Web Scraping

  1. Leistung : Rust ist eine statisch typisierte, kompilierte Sprache, die eine hohe Leistung bietet, was sie zu einer idealen Wahl für Web Scraping macht.
  2. Parallelität : Rust bietet integrierte Parallelitätsunterstützung, die die gleichzeitige Bearbeitung mehrerer Anfragen erleichtert und so die Effizienz des Web-Scrapings verbessert.
  3. Speichersicherheit : Rust bietet starke Speichersicherheitsgarantien, die dazu beitragen, häufige speicherbezogene Fehler wie Nullzeigerausnahmen und Pufferüberläufe zu verhindern und es so für Web-Scraping sicherer zu machen.
  4. Leicht zu erlernen : Rust hat eine relativ einfache Syntax, was es für Entwickler im Vergleich zu anderen Programmiersprachen einfacher macht, es zu erlernen.
  1. Steile Lernkurve : Obwohl die Syntax von Rust relativ einfach ist, kann das Erlernen der Sprache eine Herausforderung sein, insbesondere für Entwickler, die mit statisch typisierten, kompilierten Sprachen noch nicht vertraut sind.
  2. Begrenzte Community : Obwohl Rust über eine wachsende Community verfügt, wird es immer noch nicht so weit verbreitet wie andere Programmiersprachen, was bedeutet, dass es schwierig sein kann, Ressourcen und Support zu finden.
  3. Keine integrierte Web-Scraping-Bibliothek : Rust verfügt nicht über eine integrierte Bibliothek für Web-Scraping, was bedeutet, dass Entwickler auf Bibliotheken von Drittanbietern zurückgreifen müssen, was weniger praktisch sein kann.

Jetzt installieren wir Rust, überprüfen die Version von Cargound installieren drei Web-Scraping-Bibliotheken, reqwest, scraperund selectüber die Befehlszeilenschnittstelle.

Rust installieren

Um Rust zu installieren, können Sie das Rustup-Tool verwenden. Dies ist die empfohlene Methode zur Installation von Rust. Sie können den folgenden Befehl in Ihrem Terminal ausführen:

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

Cargo installieren

Cargo ist der Paketmanager für Rust und wird automatisch installiert, wenn Sie Rust installieren. Sie können überprüfen, ob Cargo installiert ist, indem Sie den folgenden Befehl ausführen:

cargo --version

Führen Sie den folgenden Befehl in Ihrem Terminal aus:

cargo new toscrape_quotes

Um die Rust-Web-Scraping-Bibliotheken reqwest, scraperund zu installieren select, können Sie Ihrer Cargo.tomlDatei die folgenden Abhängigkeiten hinzufügen:

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

Lassen Sie uns etwas Web Scraping durchführen. Sollen wir?

Toscrape.com ist eine Web-Scraping-Sandbox für diejenigen, die sich in ein solches Unterfangen im Software-Engineering stürzen möchten.

Ohne unerwünschte Aufmerksamkeit von Unternehmensanwälten von Unternehmensplattformen oder -organisationen zu erregen, können Sie es als Ihre Spielwiese nutzen. Ja, du willst diese Leute nicht sehen! Einige tragen Anzüge gepaart mit 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(&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));
    }
}

Die mainFunktion

Die mainFunktion ist der Einstiegspunkt des Rust-Codes und führt die folgenden Schritte aus:

  1. Erstellt einen neuen HTTP-Client mit reqwest::Client.
  2. Ruft die get_quotesFunktion auf und übergibt ihr den Client, der einen Vektor von QuoteStrukturen zurückgibt.
  3. Ruft die write_quotes_to_csvFunktion auf und übergibt Quoteihr den Strukturvektor, der die Anführungszeichen in eine CSV-Datei schreibt.
  4. Gibt das Ergebnis der write_quotes_to_csvFunktion zurück, d. h. entweder, Ok(())wenn alles erfolgreich war, oder einen Fehler, wenn etwas schief gelaufen ist.

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

Die get_quotesFunktion verwendet einen Verweis auf reqwest::Clientals Argument und gibt einen Vektor von QuoteStrukturen zurück, die von der Website entfernt wurden.http://quotes.toscrape.com". Hier ist eine Aufschlüsselung dessen, was wir tun:

  1. Initialisieren Sie einen leeren Strukturvektor Quote.
  2. Durchlaufen Sie die Seiten, die gescrapt werden sollen, von 1 bis MAX_PAGE.
  3. Stellen Sie für jede Seite mithilfe der Funktion eine GET-Anfrage an die URL der Seite retry_getund rufen Sie den HTML-Code der Seite ab.
  4. Analysieren Sie den HTML-Code in ein scraper::HtmlFragment.
  5. Wählen Sie mithilfe von CSS-Selektoren das Zitat text, authorund tagsaus dem HTML-Code aus.
  6. Erstellen Sie für jedes Zitat eine neue QuoteStruktur und hängen Sie sie an den Anführungszeichenvektor an.
  7. Gibt den Anführungszeichenvektor zurück.

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

Die write_quotes_to_csvFunktion benötigt einen Verweis, um die QuoteStrukturen aufzuteilen und die Anführungszeichen in eine .csvDatei zu schreiben. Hier ist eine Aufschlüsselung dessen, was wir tun:

  1. Erstellen Sie eine neue .csvDatei mit dem Namen toscrape_quotes.csv.
  2. csv::WriterErstellen Sie mit der Kiste eine neue csv.
  3. Schreiben Sie für jedes Zitat im Anführungszeichensegment einen neuen Datensatz in die CSV-Datei, wobei die Anführungszeichen text, authorund tagsdurch Kommas getrennt sind.
  4. Leeren Sie den Writer, um sicherzustellen, dass alle Daten in die Datei geschrieben werden.
  5. Gibt das Ergebnis des Schreibvorgangs zurück, d. h. entweder, Ok(())wenn alles erfolgreich war, oder einen Fehler, wenn etwas schief gelaufen ist.

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

Die retry_getFunktion nimmt einen Verweis auf reqwest::Clientund einen Verweis auf die Zeichenfolgen-URL entgegen und stellt mithilfe des Clients eine HTTP-GET-Anfrage an die URL. Wenn die Anfrage fehlschlägt, wiederholt die Funktion die Anfrage maximal so MAX_RETRIESoft mit einer Ruhezeit von RETRY_SLEEP_SECONDSSekunden zwischen den einzelnen Wiederholungen. Hier ist eine Aufschlüsselung dessen, was wir tun:

  1. Initialisieren Sie einen Zähler für die Anzahl der Wiederholungsversuche.
  2. Stellen Sie mit dem Client eine GET-Anfrage an die URL und rufen Sie die Antwort ab.
  3. Wenn die Antwort erfolgreich ist (dh der Statuscode der Antwort liegt im Bereich 2xx), wird die Antwort zurückgegeben.
  4. Wenn die Antwort nicht erfolgreich ist, erhöhen Sie den Wiederholungszähler.
  5. Wenn der Wiederholungszähler kleiner als ist MAX_RETRIES, warten Sie RETRY_SLEEP_SECONDSSekunden und wiederholen Sie die GET-Anfrage.
  6. Wenn der Wiederholungszähler gleich ist MAX_RETRIES, wird ein Fehler zurückgegeben, der darauf hinweist, dass die GET-Anfrage fehlgeschlagen ist.

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

Allerdings ist Rust im Vergleich zu fortgeschritteneren Web-Scraping-Programmiersprachen wie Python eine relativ neue Sprache. Daher verfügt es möglicherweise über eine kleinere Community und weniger verfügbare Bibliotheken. Auch die Syntax von Rust kann für Anfänger schwieriger sein, insbesondere für diejenigen, die mit höheren Programmiersprachen wie Python mit dynamischer Semantik vertraut sind.

Seien Sie schlau, haben Sie Spaß beim Surfen im Internet und meiden Sie Unternehmensanwälte. Aber es wird Anwälte geben! Hashtag, was? #Abrechnungen