Web Kazıma için Rust Kullanma

Feb 13 2023
Web Scraping, web sitelerinden bilgi çıkarma tekniğidir. Web sitelerinden büyük miktarda veri alma, zamandan ve emekten tasarruf etme ve bilgileri çeşitli amaçlar için kullanıma sunma otomatikleştirilmiş bir süreçtir.

Web Scraping, web sitelerinden bilgi çıkarma tekniğidir. Web sitelerinden büyük miktarda veri alma, zamandan ve emekten tasarruf etme ve bilgileri çeşitli amaçlar için kullanıma sunma otomatikleştirilmiş bir süreçtir.

Bu gönderide, bir web sitesinden alıntılar çıkarmak için Rust'ı nasıl kullanabileceğinizi göstereceğim .

Web Scraping için Rust kullanmanın avantajları

  1. Performans : Rust, yüksek performans sağlayan statik olarak yazılmış, derlenmiş bir dildir ve bu da onu web kazıma için ideal bir seçim haline getirir.
  2. Eşzamanlılık : Rust, birden fazla isteği aynı anda işlemeyi kolaylaştıran yerleşik eşzamanlılık desteği sağlayarak web kazıma verimliliğini artırır.
  3. Bellek Güvenliği : Rust, boş işaretçi istisnaları ve arabellek taşmaları gibi bellekle ilgili yaygın hataların önlenmesine yardımcı olan güçlü bellek güvenliği garantileri sağlayarak web kazıma için daha güvenli hale getirir.
  4. Öğrenmesi Kolay : Rust nispeten basit bir sözdizimine sahiptir, bu da diğer programlama dillerine kıyasla geliştiricilerin öğrenmesini kolaylaştırır.
  1. Yoğun Öğrenme Eğrisi : Rust'ın sözdizimi nispeten basit olsa da, özellikle statik olarak yazılan, derlenmiş dillerde yeni olan geliştiriciler için dili öğrenmesi zor olabilir.
  2. Sınırlı Topluluk : Rust büyüyen bir topluluğa sahip olsa da diğer programlama dilleri kadar yaygın olarak kullanılmamaktadır, bu da kaynak ve destek bulmanın zor olabileceği anlamına gelir.
  3. Yerleşik Web Scraping Kitaplığı Yok : Rust'ın web scraping için yerleşik bir kitaplığı yoktur, bu da geliştiricilerin üçüncü taraf kitaplıkları kullanması gerektiği anlamına gelir ki bu daha az kullanışlı olabilir.

Şimdi, komut satırı arayüzü aracılığıyla , , ve üç web scraping kitaplığını yükleyeceğiz , Rustsürümünü doğrulayacağız ve kuracağız.Cargoreqwestscraperselect

Rust'ı yükleyin

Rust'u yüklemek için, Rust'u kurmanın önerilen yolu olan Rustup aracını kullanabilirsiniz. Terminalinizde aşağıdaki komutu çalıştırabilirsiniz:

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

Kargo Yükle

Cargo, Rust'ın paket yöneticisidir ve Rust'ı kurduğunuzda otomatik olarak yüklenir. Aşağıdaki komutu çalıştırarak Cargo'nun kurulu olup olmadığını kontrol edebilirsiniz:

cargo --version

Terminalinizde aşağıdaki komutu çalıştırın:

cargo new toscrape_quotes

Rust web scraping kitaplıkları reqwest, scraper, ve yüklemek için selectdosyanıza aşağıdaki bağımlılıkları ekleyebilirsiniz Cargo.toml:

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

Biraz Web Scraping Yapalım. Yapalım mı?

Toscrape.com, yazılım mühendisliğinde böyle bir çabaya girmek isteyenler için bir web kazıma sanal alanıdır.

Kurumsal platformların veya kuruluşların kurumsal avukatlarının istenmeyen ilgisini çekmeden oyun alanınız olarak kullanabilirsiniz. Evet, o insanları görmek istemezsin! Bazıları Air Jordan 1 Chicago ile eşleştirilmiş takım elbise giyer .

Rust Web Kazıma Kodu

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

işlev main_

İşlev main, Rust kodunun giriş noktasıdır ve aşağıdaki adımları gerçekleştirir:

  1. kullanarak yeni bir HTTP istemcisi oluşturur reqwest::Client.
  2. İşlevi çağırır get_quotesve bir yapı vektörü döndüren istemciyi işleve iletir Quote.
  3. İşlevi çağırır write_quotes_to_csvve tırnakları bir .csv dosyasına yazan yapıların vektörünü Quoteona iletir.
  4. write_quotes_to_csvHer şey başarılı olursa Ok(())veya bir şeyler ters giderse bir hata olan işlevin sonucunu döndürür .

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

İşlev , argüman olarak get_quotesbir referans alır ve web sitesinden kazınmış bir yapı vektörü döndürür "reqwest::ClientQuotehttp://quotes.toscrape.com". İşte yaptıklarımızın bir dökümü:

  1. Boş bir yapı vektörü başlat Quote.
  2. 1'den MAX_PAGE.
  3. Her sayfa için, işlevi kullanarak sayfanın URL'sine bir GET isteği yapın retry_getve sayfanın HTML'sini alın.
  4. HTML'yi bir scraper::Htmlparça halinde ayrıştırın.
  5. CSS seçicileri kullanarak HTML'den alıntı text, authorve öğesini seçin.tags
  6. Her alıntı için yeni bir Quoteyapı oluşturun ve bunu tırnak vektörüne ekleyin.
  7. Tırnak vektörünü döndür.

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

İşlev , yapıları dilimlemek ve alıntıları bir dosyaya yazmak write_quotes_to_csviçin bir referans alır . İşte ne yaptığımızın bir dökümü:Quote.csv

  1. .csvadıyla yeni bir dosya oluşturun toscrape_quotes.csv.
  2. csv::WriterSandığı kullanarak yeni bir tane oluşturun csv.
  3. Tırnak dilimindeki her alıntı için .csv dosyasına, alıntıyı text, author, ve tagsvirgülle ayırarak yeni bir kayıt yazın.
  4. Tüm verilerin dosyaya yazıldığından emin olmak için yazıcıyı temizleyin.
  5. Yazma işleminin sonucunu, yani Ok(())her şey başarılı olursa veya bir şeyler ters giderse bir hata verir.

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

İşlev , dize URL'sine retry_getbir başvuru reqwest::Clientve bir başvuru alır ve istemciyi kullanarak URL'ye bir HTTP GET isteği yapar. MAX_RETRIESİstek başarısız olursa işlev, her yeniden deneme arasında saniyelik uyku süresiyle isteği maksimum kez yeniden dener RETRY_SLEEP_SECONDS. İşte ne yaptığımızın bir dökümü:

  1. Yeniden deneme sayısı için bir sayaç başlatın.
  2. İstemciyi kullanarak URL'ye bir GET isteği yapın ve yanıtı alın.
  3. Yanıt başarılıysa (yani yanıtın durum kodu 2xx aralığındaysa), yanıtı döndürün.
  4. Yanıt başarılı değilse, yeniden deneme sayacını artırın.
  5. Yeniden deneme sayacı değerinden küçükse MAX_RETRIES, saniye bekleyin RETRY_SLEEP_SECONDSve GET isteğini yeniden deneyin.
  6. Yeniden deneme sayacı eşitse MAX_RETRIES, GET isteğinin başarısız olduğunu belirten bir hata döndürün.

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

Ancak Rust, Python gibi daha gelişmiş web kazıma programlama dillerine kıyasla nispeten yeni bir dildir. Bu nedenle, daha küçük bir topluluğa ve daha az kitaplığa sahip olabilir. Rust'ın sözdizimi yeni başlayanlar için, özellikle Python gibi dinamik semantik ile üst düzey programlama dillerine aşina olanlar için daha zor olabilir.

Akıllı olun, internette dolaşırken eğlenin ve şirket avukatlarından kaçının. Yine de avukatlar olacak! Hashtag, ne? #Faturalar