कोई भी पार्सर, जो वास्तविक साइटों पर पांच मिनट से अधिक समय तक काम करता है, एक न एक दिन IP ब्लॉकिंग की समस्या का सामना करता है।
Rust की reqwest पुस्तकालय उत्कृष्ट प्रदर्शन प्रदान करता है,
लेकिन यह प्रॉक्सी रोटेशन की समस्या को सीधे हल नहीं करता है - इसे स्वयं लागू करना होगा।
इस लेख में हम देखेंगे कि SOCKS5 और HTTP(S) प्रॉक्सी को कैसे जोड़ना है, क्लाइंट पूल कैसे बनाना है और IP रोटेशन को इस तरह से व्यवस्थित करना है,
ताकि पार्सर पहले ही 429 और 403 उत्तरों के बाद न गिरे।
पार्सर को प्रॉक्सी की आवश्यकता क्यों है और इसकी अनुपस्थिति से क्या खतरा है
मार्केटप्लेस, मूल्य समेकन और सोशल मीडिया जैसी साइटें एक IP से असामान्य गतिविधियों पर नज़र रखती हैं: प्रति सेकंड दर्जनों अनुरोध, समान User-Agent, अनुरोधों के बीच कुकीज़ की अनुपस्थिति। जैसे ही संदिग्ध गतिविधियों की गिनती एक निश्चित सीमा को पार कर जाती है, IP बैन लिस्ट में चला जाता है - अस्थायी या स्थायी। पार्सर के लिए इसका मतलब है कि डेटा संग्रह पूरी तरह से रुक जाएगा, जब तक कि IP मैन्युअल रूप से नहीं बदला जाता।
प्रॉक्सी इस समस्या को हल करती हैं, अनुरोधों को दर्जनों या सैकड़ों IP पतों के बीच वितरित करके। इस दौरान यह महत्वपूर्ण है कि केवल "प्रॉक्सी को डालना" न हो, बल्कि कनेक्शन और रोटेशन का सही ढंग से आयोजन करना हो, अन्यथा आप या तो reqwest की सीमाओं में फंस जाएंगे (एक क्लाइंट = एक कनेक्शन पूल), या आप एक ही प्रॉक्सी से बहुत बार अनुरोध भेजना शुरू कर देंगे। आगे हम दोनों समस्याओं को क्रम से देखेंगे: पहले मूल कनेक्शन, फिर पूल की संरचना।
प्रोडक्शन पार्सर के लिए केवल IP रोटेशन ही नहीं, बल्कि पतों की गुणवत्ता भी महत्वपूर्ण है। यदि आप Wildberries, Ozon या Avito को पार्स करते हैं, तो डेटा सेंटर के IP वास्तविक उपयोगकर्ताओं के IP की तुलना में तेजी से बैन होते हैं - इस पर अधिक जानकारी प्रॉक्सी के प्रकार के चयन के अनुभाग में।
reqwest में HTTP/HTTPS प्रॉक्सी की मूल सेटिंग
reqwest प्रॉक्सी को Proxy संरचना के माध्यम से समर्थन करता है,
जिसे ClientBuilder में पास करना होता है।
Cargo.toml में निर्भरताएँ जोड़ें:
[dependencies]
reqwest = { version = "0.12", features = ["socks", "json"] }
tokio = { version = "1", features = ["full"] }
HTTP प्रॉक्सी के साथ न्यूनतम उदाहरण, जो लॉगिन और पासवर्ड द्वारा प्रमाणीकरण की आवश्यकता है:
use reqwest::{Client, Proxy};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let proxy = Proxy::all("http://gate.proxycove.com:8000")?
.basic_auth("user_login", "user_password");
let client = Client::builder()
.proxy(proxy)
.timeout(std::time::Duration::from_secs(15))
.build()?;
let resp = client
.get("https://httpbin.org/ip")
.send()
.await?
.text()
.await?;
println!("प्रॉक्सी के माध्यम से उत्तर: {resp}");
Ok(())
}
Proxy::all विधि
HTTP और HTTPS दोनों ट्रैफ़िक को प्रॉक्सी के माध्यम से निर्देशित करती है। यदि प्रोटोकॉल को अलग करना आवश्यक है, तो
Proxy::http() और
Proxy::https()
का उपयोग करें - यह उपयोगी है यदि प्रॉक्सी प्रदाता विभिन्न प्रोटोकॉल के लिए विभिन्न पोर्ट सुनता है।
SOCKS5 प्रॉक्सी को जोड़ना
SOCKS5 HTTP प्रॉक्सी की तुलना में निम्न स्तर पर काम करता है और अनुरोध की सामग्री की जांच किए बिना TCP कनेक्शन का समर्थन करता है -
यह पार्सिंग के लिए तेज और स्थिर है, विशेष रूप से यदि आप केवल HTTP(S) पर नहीं जाते हैं, बल्कि WebSocket एंडपॉइंट्स को भी कॉल करते हैं।
SOCKS5 के लिए Cargo.toml में socks फीचर को सक्षम करना आवश्यक है (जैसा कि ऊपर उल्लेखित है)।
कनेक्शन का उदाहरण:
use reqwest::{Client, Proxy};
async fn build_socks5_client(
host: &str,
login: &str,
password: &str,
) -> reqwest::Result<Client> {
let proxy_url = format!("socks5h://{host}");
let proxy = Proxy::all(&proxy_url)?.basic_auth(login, password);
Client::builder()
.proxy(proxy)
.timeout(std::time::Duration::from_secs(20))
.build()
}
socks5h
स्कीम पर ध्यान दें, socks5 के बजाय।
अंत में "h" का मतलब है कि डोमेन का DNS समाधान प्रॉक्सी सर्वर की ओर से होता है, न कि स्थानीय रूप से।
यह गुमनामी के लिए महत्वपूर्ण है: इसके बिना आपका वास्तविक DNS समाधानकर्ता अनुरोधित डोमेन को प्रॉक्सी के बिना "लीक" कर सकता है,
भले ही स्वयं ट्रैफ़िक SOCKS5 टनल के माध्यम से जा रहा हो।
यदि प्रॉक्सी प्रदाता लॉगिन/पासवर्ड द्वारा प्रमाणीकरण की आवश्यकता नहीं करता है (IP-whitelisting), तो बस
basic_auth को न बुलाएँ -
प्रदाता के पैनल में अपने सर्वर के IP पते को निर्दिष्ट करें, जिससे अनुरोध आते हैं।
विभिन्न प्रॉक्सी के लिए reqwest क्लाइंट पूल
एक सामान्य गलती - हर अनुरोध के लिए नया Client
बनाना। यह महंगा है: reqwest के अंदर TCP कनेक्शनों और TLS सत्रों का एक पूल होता है, और क्लाइंट को फिर से बनाना
सभी keep-alive लाभों को नष्ट कर देता है। सही दृष्टिकोण - प्रत्येक प्रॉक्सी के लिए पहले से एक क्लाइंट बनाना
और उन्हें सामान्य पूल से पुनः उपयोग करना।
use reqwest::{Client, Proxy};
use std::sync::Arc;
#[derive(Clone)]
struct ProxyEndpoint {
url: String,
login: String,
password: String,
}
struct ClientPool {
clients: Vec<Client>,
}
impl ClientPool {
fn build(endpoints: &[ProxyEndpoint]) -> reqwest::Result<Self> {
let mut clients = Vec::with_capacity(endpoints.len());
for ep in endpoints {
let proxy = Proxy::all(&ep.url)?
.basic_auth(&ep.login, &ep.password);
let client = Client::builder()
.proxy(proxy)
.pool_max_idle_per_host(4)
.timeout(std::time::Duration::from_secs(15))
.build()?;
clients.push(client);
}
Ok(Self { clients })
}
}
ऐसा पूल पार्सर के शुरू होने पर एक बार बनाया जाता है। क्लाइंट की संख्या आमतौर पर आपके पूल में अद्वितीय प्रॉक्सी पतों की संख्या के बराबर होती है - यदि प्रदाता गेटवे (gateway) स्तर पर रोटेशन का समर्थन करता है, तो एक या दो क्लाइंट पर्याप्त हैं, क्योंकि IP रोटेशन प्रदाता की ओर से हर नए TCP कनेक्शन पर स्वचालित रूप से होता है।
उच्च समांतरता वाले कार्यों के लिए, पूल को Arc में लपेटें
और tokio कार्यों के बीच साझा करें - reqwest क्लाइंट थ्रेड-सुरक्षित हैं और समवर्ती उपयोग के लिए डिज़ाइन किए गए हैं।
IP रोटेशन की रणनीतियाँ
रोटेशन के लिए दो मौलिक दृष्टिकोण हैं। पहला - प्रदाता की ओर से रोटेशन: आप एक गेटवे पते से कनेक्ट होते हैं, और प्रदाता हर अनुरोध पर या हर N मिनट में नया IP डालता है। दूसरा - आपकी ओर से रोटेशन: आप स्थिर प्रॉक्सी की एक सूची रखते हैं और स्वयं तय करते हैं कि अगले अनुरोध के लिए कौन सा उपयोग करना है।
यदि आप रहवासी प्रॉक्सी का उपयोग कर रहे हैं, जो गेटवे के माध्यम से स्वचालित रोटेशन का समर्थन करते हैं, तो कोड न्यूनतम हो जाता है - बस एक ही क्लाइंट के माध्यम से अनुरोध भेजें, प्रदाता स्वयं आउटगोइंग IP बदलता है। लेकिन यदि नियंत्रण की आवश्यकता है (उदाहरण के लिए, 10 मिनट के लिए सत्र के लिए IP को स्थिर करना), तो रोटेशन को मैन्युअल रूप से लागू करें:
use rand::seq::SliceRandom;
use std::sync::atomic::{AtomicUsize, Ordering};
struct RotatingPool {
clients: Vec<Client>,
counter: AtomicUsize,
}
impl RotatingPool {
// राउंड-रोबिन: बारी-बारी से क्लाइंट्स को चेक करें
fn next_round_robin(&self) -> &Client {
let idx = self.counter.fetch_add(1, Ordering::SeqCst) % self.clients.len();
&self.clients[idx]
}
// यादृच्छिक चयन - पैटर्न विश्लेषण के खिलाफ उपयोगी
fn next_random(&self) -> &Client {
self.clients.choose(&mut rand::thread_rng()).unwrap()
}
}
राउंड-रोबिन तब उपयुक्त होता है जब सभी IP पर समान लोड महत्वपूर्ण होता है। यादृच्छिक चयन पैटर्न को बेहतर तरीके से छुपाता है, यदि लक्षित साइट एक ही पते से अनुरोधों के बीच के अंतराल का विश्लेषण करती है। Ozon पर कीमतों की निगरानी या Avito पर विज्ञापनों को इकट्ठा करने जैसे कार्यों के लिए, वास्तव में प्रभावी संयोजन होता है: प्रॉक्सी का यादृच्छिक चयन और एक ही क्लाइंट के अनुरोधों के बीच 1-3 सेकंड की यादृच्छिक देरी।
स्टिकी-सेशंस के बारे में अलग से कहना चाहिए। यदि पार्सर एक बहु-चरण परिदृश्य (लॉगिन → पृष्ठों पर जाना → क्रिया) से गुजरता है, तो सत्र के बीच में IP को बदलना नहीं चाहिए - साइट सक्रिय कुकी सत्र के दौरान IP परिवर्तन का पता लगाएगी और या तो लॉगआउट कर देगी या खाता ब्लॉक कर देगी। ऐसे परिदृश्यों के लिए, एक क्लाइंट (और तदनुसार एक IP) को पार्सर के विशिष्ट "सत्र" के लिए उसके जीवनकाल के दौरान स्थिर रखें।
त्रुटि प्रबंधन और पुनः प्रयास लॉजिक
प्रॉक्सी समय-समय पर असफल हो जाती हैं: टाइमआउट, 407 (प्रॉक्सी प्रमाणीकरण आवश्यक), TCP कनेक्शन का टूटना। पार्सर को "प्रॉक्सी टूटी हुई - पूल से बाहर निकालें" और "साइट ने 429 लौटाया - बस प्रतीक्षा करें और IP बदलें" के बीच अंतर करना चाहिए। यहाँ एक कार्यशील पैटर्न है जिसमें पुनः प्रयास और मृत प्रॉक्सी को बाहर निकालना शामिल है:
use reqwest::StatusCode;
use std::time::Duration;
async fn fetch_with_retry(
pool: &RotatingPool,
url: &str,
max_attempts: u32,
) -> Result<String, String> {
let mut last_error = String::new();
for attempt in 0..max_attempts {
let client = pool.next_random();
match client.get(url).send().await {
Ok(resp) if resp.status() == StatusCode::TOO_MANY_REQUESTS => {
last_error = "429: दर सीमा, IP बदल रहे हैं".into();
tokio::time::sleep(Duration::from_millis(500 * (attempt as u64 + 1))).await;
continue;
}
Ok(resp) if resp.status().is_success() => {
return resp.text().await.map_err(|e| e.to_string());
}
Ok(resp) => {
last_error = format!("अप्रत्याशित स्थिति: {}", resp.status());
}
Err(e) if e.is_timeout() || e.is_connect() => {
last_error = format!("प्रॉक्सी के साथ समस्या: {e}");
continue;
}
Err(e) => {
last_error = e.to_string();
}
}
}
Err(format!("सभी प्रयास समाप्त: {last_error}"))
}
एक महत्वपूर्ण बिंदु: एक्सपोनेंशियल देरी (500 * प्रयास)
बड़े पैमाने पर 429 उत्तरों पर लक्षित साइट पर लोड को कम करता है और एक साथ पूरे प्रॉक्सी पूल के पूर्ण ब्लॉक होने के जोखिम को भी कम करता है।
प्रोडक्शन पार्सरों के लिए, प्रत्येक प्रॉक्सी पर त्रुटियों की गिनती जोड़ें और यदि वह 5+ लगातार त्रुटियाँ देता है,
तो स्वचालित रूप से पते को बाहर निकालें - यह पहले से ही बैन किए गए IP के माध्यम से बेकार प्रयासों से बचाएगा।
पार्सिंग के लिए कौन सा प्रॉक्सी प्रकार चुनें
प्रॉक्सी के प्रकार का चयन सीधे इस पर प्रभाव डालता है कि आप कितने सफल अनुरोध प्राप्त करेंगे जब तक कि ब्लॉक न हो जाए। डेटा सेंटर के IP सबसे सस्ते और तेज होते हैं, लेकिन ये प्रसिद्ध होस्टिंग प्रदाताओं के रेंज में आते हैं - मार्केटप्लेस और सोशल मीडिया की एंटी-बॉट सिस्टम इन्हें लगभग तुरंत ASN डेटाबेस के माध्यम से पहचान लेती हैं। रहवासी और मोबाइल IP सामान्य उपयोगकर्ताओं की तरह दिखते हैं, इसलिए ये कई गुना अधिक समय तक जीवित रहते हैं।
| प्रॉक्सी का प्रकार | गति | ब्लॉक होने का जोखिम | कब लागू करें |
|---|---|---|---|
| डेटा सेंटर | उच्च | उच्च | ओपन API, बिना एंटी-बॉट वाली साइटें |
| रहवासी | मध्यम | कम | Wildberries, Ozon, Avito, सोशल मीडिया |
| मोबाइल | मध्यम | न्यूनतम | Instagram, TikTok, सख्त एंटी-बॉट |
बड़े पैमाने पर मार्केटप्लेस को पार्स करने के लिए एक उचित संतुलन - रहवासी प्रॉक्सी: ये मोबाइल की तुलना में ट्रैफ़िक के हिसाब से इतनी महंगी नहीं होती हैं, लेकिन "वास्तविक उपयोगकर्ता" स्तर पर परीक्षण पास करती हैं, जो डेटा सेंटर के रेंज को काटती हैं। यदि पार्सर सोशल मीडिया के API के माध्यम से काम करता है या मोबाइल एप्लिकेशन की नकल करता है, तो मोबाइल प्रॉक्सी IP के लिए सबसे कम कैप्चा और अस्थायी बैन प्रतिशत प्रदान करते हैं, जो संचार ऑपरेटरों के पास होते हैं।
प्रदर्शन और लोड के लिए सुझाव
Rust एक ही उदाहरण पर हजारों समवर्ती अनुरोधों को बनाए रखने की अनुमति देता है, लेकिन प्रॉक्सी प्रदाता और लक्षित साइट वास्तविक बैंडविड्थ को सीमित करते हैं। प्रोडक्शन पार्सर के लिए कुछ व्यावहारिक नियम:
tokio::sync::Semaphoreके माध्यम से समांतरता को सीमित करें - एक प्रॉक्सी पूल पर 20-50 से अधिक समवर्ती अनुरोध नहीं, अन्यथा कुछ कनेक्शन टाइमआउट के कारण टूटना शुरू हो जाएंगे।- reqwest को अस्थिर प्रॉक्सी के माध्यम से मृत कनेक्शन को न रखने के लिए
pool_idle_timeoutका उपयोग करें। - प्रत्येक प्रॉक्सी के लिए HTTP स्थिति और प्रतिक्रिया समय को लॉग करें - इससे आप जल्दी से देख सकेंगे कि कौन से पते "सड़ने" लगे हैं और उन्हें बदलने की आवश्यकता है।
- IP रोटेशन के साथ User-Agent को भी रोटेट करना डिटेक्शन की संभावना को काफी कम करता है: उन्हें समकालिक रूप से बदलें, न कि अलग-अलग चक्रों में।
- दीर्घकालिक पार्सरों के लिए स्वास्थ्य-जांच जोड़ें: हर कुछ मिनटों में प्रत्येक प्रॉक्सी के माध्यम से https://httpbin.org/ip पर एक परीक्षण अनुरोध भेजें और अनुत्तरदायी को बाहर निकालें।
TLS फिंगरप्रिंट (JA3/JA3S) पर विशेष ध्यान दें। reqwest डिफ़ॉल्ट रूप से rustls या nativetls का उपयोग करता है
निर्माण की सुविधाओं के आधार पर, और दोनों के पास TLS हैंडशेक के विभिन्न फिंगरप्रिंट होते हैं। कुछ एंटी-बॉट सिस्टम (उदाहरण के लिए,
बड़े मार्केटप्लेस में) विशेष रूप से TLS फिंगरप्रिंट का विश्लेषण करते हैं, न कि केवल IP - इसलिए यहां तक कि एक आदर्श प्रॉक्सी
भी मदद नहीं करेगा यदि स्वयं क्लाइंट एक असामान्य TLS हैंडशेक के रूप में प्रस्तुत होता है। यदि आप "स्वच्छ" प्रॉक्सी के साथ ब्लॉकिंग का सामना करते हैं,
तो reqwest की सुविधाओं की जाँच करें और rustls के बजाय native-tls फीचर का प्रयास करें, या इसके विपरीत।
निष्कर्ष
Rust reqwest में प्रॉक्सी को जल्दी सेट किया जा सकता है - Proxy::all
या SOCKS5 स्कीम socks5h:// के साथ।
असली काम इसके बाद शुरू होता है: क्लाइंट पूल का निर्माण, रोटेशन की रणनीति का चयन (राउंड-रोबिन, यादृच्छिक
या स्टिकी-सेशंस) और त्रुटियों का सही प्रबंधन, जिसमें पूल से गैर-कार्यशील पतों को बाहर निकालना शामिल है। इसके बिना,
Rust पर सबसे तेज़ पार्सर भी कुछ मिनटों के काम के बाद ब्लॉकिंग का सामना करेगा।
यदि आपका पार्सर मार्केटप्लेस या सोशल मीडिया के साथ काम करता है, जहां एंटी-बॉट सुरक्षा केवल अनुरोधों की आवृत्ति को नहीं बल्कि IP की उत्पत्ति का भी विश्लेषण करती है, तो रहवासी प्रॉक्सी से शुरू करें - ये कीमत और सत्रों की जीवितता के बीच सबसे अच्छा संतुलन प्रदान करते हैं। अधिक सख्त लक्ष्यों के लिए जैसे Instagram और TikTok, मोबाइल IP का परीक्षण करना चाहिए, और सरल कार्यों के लिए बिना एंटी-बॉट के तेज और सस्ते डेटा सेंटर प्रॉक्सी पर्याप्त होंगे।