การใช้ Rust สำหรับการขูดเว็บ
Web Scrapingเป็นเทคนิคการดึงข้อมูลจากเว็บไซต์ เป็นกระบวนการอัตโนมัติในการดึงข้อมูลจำนวนมากจากเว็บไซต์ ประหยัดเวลาและความพยายาม และทำให้ข้อมูลพร้อมใช้งานสำหรับวัตถุประสงค์ต่างๆ
ในโพสต์นี้ ฉันจะสาธิตวิธีที่คุณสามารถใช้Rustเพื่อแยกคำพูดจากเว็บไซต์
ข้อดีของการใช้ Rust สำหรับ Web Scraping
- ประสิทธิภาพ : Rust เป็นภาษาที่คอมไพล์แล้วพิมพ์แบบสแตติกซึ่งให้ประสิทธิภาพสูง ทำให้เป็นตัวเลือกที่เหมาะสำหรับการขูดเว็บ
- การทำงานพร้อมกัน : Rust ให้การสนับสนุนการทำงานพร้อมกันในตัว ซึ่งทำให้ง่ายต่อการจัดการคำขอหลายรายการพร้อมกัน ปรับปรุงประสิทธิภาพของการขูดเว็บ
- ความปลอดภัยของหน่วยความจำ : สนิมให้การรับประกันความปลอดภัยของหน่วยความจำที่แข็งแกร่ง ซึ่งช่วยป้องกันข้อบกพร่องที่เกี่ยวข้องกับหน่วยความจำทั่วไป เช่น ข้อยกเว้นพอยน์เตอร์แบบ null และบัฟเฟอร์ล้น ทำให้ปลอดภัยยิ่งขึ้นสำหรับการขูดเว็บ
- เรียนรู้ง่าย : Rust มีไวยากรณ์ที่ค่อนข้างง่าย ทำให้นักพัฒนาสามารถเรียนรู้ได้ง่ายขึ้น เมื่อเทียบกับภาษาโปรแกรมอื่นๆ
- เส้นโค้งการเรียนรู้ที่สูงชัน : แม้ว่าไวยากรณ์ของ Rust จะค่อนข้างเรียบง่าย แต่ภาษาก็อาจเป็นสิ่งที่ท้าทายในการเรียนรู้ โดยเฉพาะอย่างยิ่งสำหรับนักพัฒนาที่ยังไม่คุ้นเคยกับภาษาที่คอมไพล์แล้วพิมพ์แบบสแตติก
- ชุมชนจำกัด : แม้ว่า Rust จะมีชุมชนที่กำลังเติบโต แต่ก็ยังไม่ได้ใช้กันอย่างแพร่หลายเท่ากับภาษาโปรแกรมอื่นๆ ซึ่งหมายความว่าการค้นหาทรัพยากรและการสนับสนุนอาจเป็นเรื่องยาก
- ไม่มีไลบรารีการขูดเว็บในตัว : Rust ไม่มีไลบรารีในตัวสำหรับการขูดเว็บ หมายความว่านักพัฒนาต้องใช้ไลบรารีของบุคคลที่สามซึ่งสะดวกน้อยกว่า
ตอนนี้ เราจะติดตั้งRustตรวจสอบเวอร์ชันของCargoและติดตั้งไลบรารีการขูดเว็บสามไลบรารี , reqwest, scraperและselectผ่านอินเทอร์เฟซบรรทัดคำสั่ง
ติดตั้งสนิม
ในการติดตั้ง Rust คุณสามารถใช้เครื่องมือ Rustup ซึ่งเป็นวิธีที่แนะนำในการติดตั้ง Rust คุณสามารถเรียกใช้คำสั่งต่อไปนี้ในเทอร์มินัลของคุณ:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
ติดตั้งคาร์โก้
Cargo เป็นผู้จัดการแพ็คเกจสำหรับ Rust และติดตั้งโดยอัตโนมัติเมื่อคุณติดตั้ง Rust คุณสามารถตรวจสอบว่ามีการติดตั้ง Cargo หรือไม่โดยเรียกใช้คำสั่งต่อไปนี้:
cargo --version
เรียกใช้คำสั่งต่อไปนี้ในเทอร์มินัลของคุณ:
cargo new toscrape_quotes
ในการติดตั้งไลบรารีการขูดเว็บของ Rust reqwest, scraperและselectคุณสามารถเพิ่มการอ้างอิงต่อไปนี้ใน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 .
มาทำ Web Scraping กันเถอะ เราจะ?
Toscrape.comเป็นแซนด์บ็อกซ์สำหรับการขูดเว็บสำหรับผู้ที่ต้องการโจมตีในความพยายามดังกล่าวในวิศวกรรมซอฟต์แวร์
คุณสามารถใช้มันเป็นสนามเด็กเล่นของคุณได้โดยไม่ดึงดูดความสนใจที่ไม่พึงประสงค์จากนักกฎหมายองค์กรของแพลตฟอร์มองค์กรหรือองค์กร ใช่คุณไม่ต้องการเห็นคนเหล่านั้น! บางคนใส่สูทคู่กับAir Jordan 1 Chicago
รหัสการขูดเว็บสนิม
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));
}
}
ฟังmainก์ชั่น
ฟังก์ชัน นี้mainเป็นจุดเริ่มต้นของรหัส Rust และดำเนินการตามขั้นตอนต่อไปนี้:
- สร้างไคลเอ็นต์ HTTP ใหม่โดยใช้
reqwest::Client. - เรียกใช้
get_quotesฟังก์ชันและส่งไคลเอ็นต์ไปยังฟังก์ชันนั้น ซึ่งจะส่งคืนเวกเตอร์ของQuoteโครงสร้าง - เรียกใช้
write_quotes_to_csvฟังก์ชันและส่งเวกเตอร์ของQuoteโครงสร้างไปให้ ซึ่งเขียนเครื่องหมายคำพูดลงในไฟล์ .csv - ส่งกลับผลลัพธ์ของ
write_quotes_to_csvฟังก์ชัน ซึ่งก็คือOk(())หากทุกอย่างสำเร็จ หรือส่งกลับข้อผิดพลาดหากมีสิ่งผิดปกติเกิดขึ้น
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(())
}
ฟังก์ชันget_quotesใช้การอ้างอิงreqwest::Clientเป็นอาร์กิวเมนต์และส่งคืนเวกเตอร์ของQuoteโครงสร้างที่คัดลอกมาจากเว็บไซต์ "http://quotes.toscrape.com"นี่คือรายละเอียดของสิ่งที่เรากำลังทำ:
- เริ่มต้นเวกเตอร์ว่างของ
Quoteโครงสร้าง - วนซ้ำหน้าที่จะคัดลอกจาก 1
MAX_PAGEถึง - สำหรับแต่ละเพจ ให้ส่งคำขอ GET ไปยัง URL ของเพจโดยใช้ฟังก์ชัน
retry_getและดึง HTML ของเพจ - แยกวิเคราะห์ HTML เป็น
scraper::Htmlส่วนย่อย - เลือกคำพูด
text,author, และtagsจาก HTML โดยใช้ตัวเลือก CSS - สร้าง
Quoteโครงสร้างใหม่สำหรับแต่ละคำพูดและต่อท้ายเวกเตอร์ของคำพูด - ส่งกลับเวกเตอร์ของคำพูด
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)
}
ฟังก์ชันwrite_quotes_to_csvใช้การอ้างอิงเพื่อแบ่งQuoteโครงสร้างและเขียนเครื่องหมายคำพูดลงใน.csvไฟล์ นี่คือรายละเอียดของสิ่งที่เรากำลังทำ:
- สร้างไฟล์ใหม่
.csvชื่อtoscrape_quotes.csv. - สร้างใหม่
csv::Writerโดยใช้csvลัง - เขียนบันทึกใหม่ลงในไฟล์ .csv สำหรับแต่ละเครื่องหมายคำพูดในส่วนของเครื่องหมายคำพูด โดยมีเครื่องหมายคำพูด
text,authorและtagsคั่นด้วยเครื่องหมายจุลภาค - ล้างตัวเขียนเพื่อให้แน่ใจว่าข้อมูลทั้งหมดถูกเขียนลงในไฟล์
- ส่งคืนผลลัพธ์ของการดำเนินการเขียน ซึ่งก็คือ
Ok(())หากทุกอย่างสำเร็จ หรือแสดงข้อผิดพลาดหากมีสิ่งผิดปกติเกิดขึ้น
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(())
}
ฟังก์ชันretry_getใช้การอ้างอิงไปยังreqwest::ClientURL ของสตริง และสร้างคำขอ HTTP GET ไปยัง URL โดยใช้ไคลเอ็นต์ หากคำขอล้มเหลว ฟังก์ชันจะลองคำขออีกMAX_RETRIESครั้งสูงสุดโดยมีเวลาพักเครื่องเป็นRETRY_SLEEP_SECONDSวินาทีระหว่างการลองใหม่แต่ละครั้ง นี่คือรายละเอียดของสิ่งที่เรากำลังทำ:
- เริ่มต้นตัวนับสำหรับจำนวนการลองใหม่
- ส่งคำขอ GET ไปยัง URL โดยใช้ไคลเอ็นต์และรับการตอบกลับ
- หากการตอบกลับสำเร็จ (เช่น รหัสสถานะของการตอบกลับอยู่ในช่วง 2xx) ให้ส่งคืนการตอบกลับ
- หากการตอบสนองไม่สำเร็จ ให้เพิ่มตัวนับการลองใหม่
- หากตัวนับการลองใหม่มีค่าน้อยกว่า
MAX_RETRIESให้รอRETRY_SLEEP_SECONDSสักครู่แล้วลองคำขอ GET อีกครั้ง - ถ้าตัวนับการลองใหม่เท่ากับ ให้
MAX_RETRIESส่งคืนข้อผิดพลาดที่ระบุว่าคำขอ GET ล้มเหลว
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));
}
}
อย่างไรก็ตาม Rust เป็นภาษาที่ค่อนข้างใหม่เมื่อเทียบกับภาษาโปรแกรมขูดเว็บขั้นสูงเช่น Python ดังนั้นจึงอาจมีชุมชนที่เล็กลงและมีห้องสมุดน้อยลง ไวยากรณ์ของ Rust อาจยากขึ้นสำหรับผู้เริ่มต้น โดยเฉพาะอย่างยิ่งผู้ที่คุ้นเคยกับภาษาโปรแกรมระดับสูง เช่น Python ที่มีความหมายเชิงไดนามิก
ฉลาด สนุกกับการท่องเว็บ และหลีกเลี่ยงทนายความของบริษัท จะมีทนายความแม้ว่า! แฮชแท็กว่าไง? #เงินเก็บ





































![รายการที่เชื่อมโยงคืออะไร? [ส่วนที่ 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)