🎯 कस्टमाइज़ करने योग्य, डिटेक्शन-प्रतिरोधी क्लाउड ब्राउज़र जो स्व-विकसित Chromium द्वारा संचालित है, वेब क्रॉलर और एआई एजेंट्स के लिए डिज़ाइन किया गया। 👉अभी आज़माएं
वापस ब्लॉग पर

असिंक्रोनस पायथन वेब स्क्रैपिंग: aiohttp और Scrapeless के साथ 10,000+ URLs तक स्केल करें

Ethan Brown
Ethan Brown

Advanced Bot Mitigation Engineer

28-May-2026

मुख्य बिंदु:

  • Async I/O-बाउंड स्क्रैप पर ~10–100× से सिंक्रोनस को हराता है। asyncio का इवेंट लूप एक पायथन थ्रेड को सैकड़ों इन-फ्लाइट HTTP अनुरोधों को संभालने देता है; सिंक्रोनस समकक्ष हर सॉकेट रीड पर ब्लॉक करता है और प्रत्येक URL के लिए पूरी विलंबता का भुगतान करता है।
  • aiohttp प्रामाणिक async HTTP क्लाइंट है। एकल aiohttp.ClientSession कनेक्शन पूल, कीप-एलाइव्स, कुकीज और टाइमआउट्स को रखता है — इसे asyncio.gather के साथ फैला देने के लिए और प्रति-होस्ट कैप के लिए asyncio.Semaphore के साथ जोड़ें।
  • Scrapeless रेजिडेंशियल प्रॉक्सी async फेचिंग को रूट करती है। एक प्रॉक्सी URL सीधे aiohttp.ClientSession(... proxy=...) में प्लग करता है, प्रत्येक अनुरोध को एक अलग रेजिडेंशियल IP देता है, और ईग्रेरस भौगोलिक क्षेत्र को उपयोगकर्ता नाम में एम्बेडेड देश कोड के साथ लॉक करता है।
  • Scrapeless स्क्रैपिंग ब्राउज़र JS-रेनडर किए गए अल्पसंख्यक का सामना करता है। पृष्ठ जो aiohttp के द्वारा एक JS-ऐप शेल (Next.js, React, Vue) के रूप में लौटाए जाते हैं, को क्लाउड ब्राउज़र सत्र में बढ़ाया जाता है — Scrapeless Python SDK और Playwright के async API के माध्यम से async Python से जुड़े होते हैं।
  • निराशाएँ आउट-ऑफ-बैंड रहती हैं। asyncio.gather(return_exceptions=True) एक बुरा URL बाकी फैला देने को रद्द करने से रोकता है; असफल URLs एक मृत-पत्रिका सूची में अलग समीक्षा के लिए जाते हैं, इनलाइन लूप में नहीं।
  • शुरुआत के लिए मुफ्त। नए Scrapeless खाते में मुफ्त स्क्रैपिंग ब्राउज़र रनटाइम शामिल है — Scrapeless पर साइन अप करें।

परिचय: क्यों async, और सीरियल स्क्रैपिंग आपको क्या खर्च करती है

सिंक्रोनस पायथन स्क्रैपर जो requests का उपयोग करता है, हर सॉकेट रीड पर थ्रेड को ब्लॉक करता है। 500 मिलीसेकंड प्रति अनुरोध पर 1,000 उत्पाद पृष्ठों को स्क्रैप करें और दीवार-घड़ी की लागत लगभग 500 सेकंड है — विलंबता पूरी तरह से, एक URL पर एक समय में भुगतान की जाती है।

asyncio इसे पलटता है। इवेंट लूप तब नियंत्रण का त्याग करता है जब एक सॉकेट इन-फ्लाइट होता है, अगले कॉरूटीन को अपना अनुरोध शुरू करने की अनुमति देता है, और एक ही थ्रेड पर सैकड़ों फेच को बुनता है। वही 1,000 पृष्ठ — प्रति होस्ट 10 समवर्ती अनुरोधों पर सीमित — लगभग 50 सेकंड में साफ होते हैं। वही हार्डवेयर, वही पायथन, वही डेटा।

पकड़: async स्क्रैपिंग में दो असफलता मोड होते हैं जो सिंक्रोनस संस्करण कभी नहीं देखता। पाइपलाइन विफलताएँ, जहाँ एक कॉरूटीन के भीतर फेंकी गई अपवाद समग्र gather को रद्द कर सकती है, और लक्ष्य-पराजय दबाव, जहाँ एक तंग इन-फ्लाइट पूल एक हमले से अलग नहीं दिखता यदि प्रॉक्सी परत IPs के बीच ईग्रेरस फैला नहीं सकती।

यह गाइड दोनों पर चलती है। HTTP स्तरीय aiohttp का उपयोग करता है और 195+ देशों में Scrapeless रेजिडेंशियल प्रॉक्सी। JS-रेनडर की गई स्तरीय Scrapeless स्क्रैपिंग ब्राउज़र में बढ़ती है, जिसका कनेक्शन async Python से Scrapeless Python SDK और Playwright async API के साथ है।


आप इससे क्या कर सकते हैं

  • मात्रा में स्थैतिक कैटलॉग को क्रॉल करें। किताबें, लेख, साइटमैप, कुछ भी जो प्रस्तुत HTML भेजता है — async फैला देने से घंटे भर के क्रॉल को मिनट भर के क्रॉल में बदलता है।
  • समवर्ती फ़ीड खिंचाव चलाएँ। RSS, JSON APIs, साइटमैप अनुक्रम; सीमित समवर्तीता के साथ सैकड़ों एंडपॉइंट्स में फैला दें।
  • क्षेत्रों के बीच मूल्य-निगरानी करें। Scrapeless ईग्रेरस को यूएस, जीबी, डीई, जेपी पर पिन करें और समान उत्पाद पृष्ठ को कई भौगोलिक क्षेत्रों से समानांतर में खींचें।
  • अपनी साइट का ऑडिट-क्रॉल करें। Async 10k-URL साइटमैप को मिनटों में साफ करता है न कि घंटों में और मृत लिंक और धीमे रास्तों को रिपोर्ट करता है।
  • डाउनस्ट्रीम पाइपलाइनों को हाइड्रेट करें। async परत प्रस्तुत HTML या JSON सीधे Postgres, Snowflake, या Kafka में थ्रेड-पूल गला दबाने के बिना डालती है।
  • चुनिंदा रूप में बढ़ाएँ। ~70% पृष्ठों के लिए सस्ते HTTP पर aiohttp को रखें जो प्रस्तुत मार्कअप भेजते हैं; केवल JS-भारी अल्पसंख्यक के लिए क्लाउड ब्राउज़र सत्र शुरू करें।

Scrapeless पर, हम केवल सार्वजनिक रूप से उपलब्ध डेटा तक पहुँच करते हैं जबकि लागू कानूनों, नियमों, और वेबसाइट की गोपनीयता नीतियों का सख्ती से पालन करते हैं। इस पोस्ट में सामग्री केवल प्रदर्शन उद्देश्यों के लिए है।


क्यों Scrapeless for Async Scraping

Scrapeless स्क्रैपिंग ब्राउज़र एक अनुकूलन योग्य, एंटी-डिटेक्शन क्लाउड ब्राउज़र है जिसे वेब क्रॉलर्स और एआई एजेंटों के लिए डिज़ाइन किया गया है; Scrapeless रेजिडेंशियल प्रॉक्सी इसके तहत प्रॉक्सी परत हैं। विशेष रूप से async Python पाइपलाइनों के लिए, यह संयोजन लाता है:

  • 195+ देशों में रेजिडेंशियल प्रॉक्सी, जो एकल HTTP प्रॉक्सी URL के रूप में उजागर होती है जो सीधे aiohttp.ClientSession(... proxy=...) में जाती है।
  • प्रति-आवेदन भौगोलिक पिनिंग एक देश कोड के माध्यम से जो प्रॉक्सी क्रेडेंशियल्स में एम्बेडेड है — कोई प्रति-आवेदन हैंडलशेक लागत नहीं, कोई प्रति-कोरूटीन सत्र पुनर्निर्माण नहीं।
  • स्टीकी-सेशन विकल्प उन प्रवाहों के लिए जिन्हें एक बहु-चरण लॉगिन या पेजिनेटेड ट्रैवर्सल के दौरान एक ही IP की आवश्यकता होती है, और बाकी सब कुछ के लिए घूमते IP।
  • क्लाउड-साइड JS रेंडरिंग जब कोई पृष्ठ React/Vue/Next.js भारी होता है — Python SDK एक browser_ws_endpoint तैयार करता है जिससे आप Playwright के async API के साथ जुड़ते हैं।
  • दोनों स्तरों के लिए एक API कुंजी — प्रॉक्सी और स्क्रैपिंग ब्राउज़र एक ही Scrapeless खाते के खिलाफ बिल होते हैं।

फ्री योजना पर अपना API कुंजी प्राप्त करें Scrapeless पर।

पूर्वापेक्षाएँ

  • Python 3.10 या नया
  • एक Scrapeless खाता और API की — app.scrapeless.com पर साइन अप करें
  • async/await और इवेंट-लूप मॉडल के साथ सहजता
  • एक टर्मिनल

चरण 1 — asyncio, aiohttp, और Scrapeless SDK स्थापित करें

aiohttp में अंतर्निहित asyncio समर्थन शामिल है। scrapeless SDK चरण 6 वृद्धि स्तर के लिए क्लाउड-ब्राउज़र सत्र उत्पन्न करता है। Playwright का असिंक्रोनस API Scrapeless स्क्रेपिंग ब्राउज़र को संचालित करने का मानक असिंक्रोनस-Python तरीका है:

bash Copy
pip install aiohttp scrapeless playwright
playwright install chromium

playwright install chromium एक बार एक स्थानीय CDP क्लाइंट डाउनलोड करता है; वास्तविक रेंडरिंग अभी भी Scrapeless के क्लाउड में चलती है — स्थानीय Chromium केवल प्रोटोकॉल बोलने वाला है।


चरण 2 — अपने Scrapeless क्रेडेंशियल्स कॉन्फ़िगर करें

अपने Scrapeless API की, अपने चैनल ID, और अपने आवासीय-प्रॉक्सी चैनल पासवर्ड को परिवेश परिवर्तनों के रूप में निर्यात करें। ये तीनों Scrapeless डैशबोर्ड में प्रोक्सी → आवासीय के तहत दिखाई देते हैं app.scrapeless.com पर — जेनरेट पर क्लिक करें और डैशबोर्ड एक कोलन-सीमांकित स्ट्रिंग प्रिंट करता है <GATEWAY>:<PORT>:<CHANNEL_ID>-proxy-country_US-r_10m-s_<SESSION_ID>:<PASSWORD> के रूप में:

bash Copy
export SCRAPELESS_API_KEY="your_api_token_here"
export SCRAPELESS_CHANNEL_ID="your_channel_id"          # उपयोगकर्ता नाम की शुरुआत में प्रिंट किया गया
export SCRAPELESS_PROXY_PASS="your_channel_password"
export SCRAPELESS_PROXY_GATEWAY="gw-us.scrapeless.io"   # क्षेत्रीय गेटवे के लिए नीचे देखें

क्षेत्रीय गेटवे: gw-us.scrapeless.io (अमेरिका), gw-eu.scrapeless.io (यूरोप), gw-ap.scrapeless.io (एशिया-प्रशांत)। सबसे निकटतम गेटवे का चयन करें ताकि हैंडशेक लेटेंसी कम हो; बाहर जाने वाला देश फिर भी country_<CC> उपयोगकर्ता नाम पैरामीटर द्वारा नियंत्रित किया जाता है चाहे आप किस गेटवे के माध्यम से कनेक्ट होते हैं। सभी के लिए पोर्ट 8789 है।

आवासीय-प्रॉक्सी उपयोगकर्ता नाम चार पैरामीटर से निर्मित होता है:

  • <CHANNEL_ID> — आपका चैनल पहचानकर्त्ता (डैशबोर्ड पर उपयोगकर्ता नाम की शुरुआत में प्रिंट किया गया)।
  • country_<CC> — दो-व्याकरण में देश पिन। Scrapeless country_US, country_UK, country_DE, country_JP आदि का उपयोग करता है (नोट: UK, न कि ISO GB)।
  • r_<duration> — स्टिकी-सेशन रोटेशन अंतराल (जैसे r_10m 10 मिनट के लिए वही आईपी रखता है फिर रोटेट करता है)।
  • s_<SESSION_ID> — स्टिकी-सेशन पहचानकर्ता; अनुरोधों के बीच समान s_<id> पुन: उपयोग करें ताकि विंडो के दौरान वही आईपी हो।

रोटेटिंग आईपी प्राप्त करने के लिए r_ और s_ को छोड़ दें (प्रति अनुरोध एक ताजा आवासीय आईपी)। उन्हें उन प्रक्रियाओं के लिए बनाए रखें जिन्हें सत्र निरंतरता की आवश्यकता होती है, जैसे कि लॉगिन के बाद एक पृष्ठित यात्रा।


चरण 3 — बुनियादी: aiohttp + Scrapeless प्रॉक्सी के साथ एकल असिंक्रोनस फेच

सबसे छोटा कार्यात्मक असिंक्रोनस स्क्रैपर। एक ClientSession, एक GET, एक HTML पे लोड आवासीय प्रॉक्सी के माध्यम से वापस:

python Copy
import asyncio
import os
import aiohttp

PROXY = (
    f"http://{os.environ['SCRAPELESS_CHANNEL_ID']}-proxy-country_US"
    f":{os.environ['SCRAPELESS_PROXY_PASS']}"
    f"@{os.environ['SCRAPELESS_PROXY_GATEWAY']}:8789"
)

async def fetch(session: aiohttp.ClientSession, url: str) -> str:
    timeout = aiohttp.ClientTimeout(total=30)
    async with session.get(url, proxy=PROXY, timeout=timeout) as resp:
        resp.raise_for_status()
        return await resp.text()

async def main() -> None:
    async with aiohttp.ClientSession() as session:
        html = await fetch(session, "https://books.toscrape.com/")
        print(f"Fetched {len(html):,} chars via US residential egress")

if __name__ == "__main__":
    asyncio.run(main())

इस स्निपेट में तीन बातें जल्दी से लॉक इन होती हैं:

  • ClientSession एक बार बनाई जाती है और पुन: उपयोग की जाती है। प्रत्येक session.get(...) वही कनेक्शन पूल साझा करता है — प्रति अनुरोध सत्र को फिर से बनाना असिंक्रोनस का पूरा उद्देश्य पराजित करता है।
  • प्रॉक्सी URL प्रति अनुरोध भेजा जाता है, प्रति सत्र नहीं। इससे समान ClientSession को विभिन्न देशों के माध्यम से विभिन्न अनुरोधों को रूट करने के लिए स्वतंत्र रहता है।
  • ClientTimeout(total=30) प्रत्येक अनुरोध को सीमित करता है। एक सिंगल लटकी हुई कनेक्शन बाकी सभी को बाधित नहीं कर सकता।

चरण 4 — उन्नत: asyncio.gather और एक सेमाफोर कैप के साथ समवर्ती फेच तक पैमाना

बिना किसी समवर्ती सीमा के 100 URLs तक फैलाना एक स्क्रैपर को 10 सेकंड में अवरुद्ध कर देता है। मानक पैटर्न asyncio.Semaphore है ताकि प्रति होस्ट उड़ान में अनुरोधों की संख्या को सीमित किया जा सके:

python Copy
import asyncio
import os
import aiohttp

PROXY = (
    f"http://{os.environ['SCRAPELESS_CHANNEL_ID']}-proxy-country_US"
    f":{os.environ['SCRAPELESS_PROXY_PASS']}"
    f"@{os.environ['SCRAPELESS_PROXY_GATEWAY']}:8789"
)

# प्रति होस्ट 5 समवर्ती अनुरोधों पर कैप। लक्ष्य के अनुसार समायोजित करें — सार्वजनिक कैटलॉग
# उच्च सहन करते हैं, एंटी-बॉट-संरक्षित मूल कम चाहते हैं।
PER_HOST = asyncio.Semaphore(5)

async def fetch(session: aiohttp.ClientSession, url: str) -> str:
    async with PER_HOST:
        timeout = aiohttp.ClientTimeout(total=30)
        async with session.get(url, proxy=PROXY, timeout=timeout) as resp:

The translated text in Hindi is as follows:

python Copy
resp.raise_for_status()
            return await resp.text()

async def main() -> None:
    urls = [
        f"https://books.toscrape.com/catalogue/page-{n}.html"
        for n in range(1, 51)  # 50 कैटलॉग पृष्ठ
    ]
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, url) for url in urls]
        pages = await asyncio.gather(*tasks)
    print(f"कुल {len(pages)} पृष्ठ मिले, कुल {sum(len(p) for p in pages):,} अक्षर")

if __name__ == "__main__":
    asyncio.run(main())

asyncio.Semaphore(5) वह लोड-बेयरिंग लाइन है। इसके बिना, asyncio.gather सभी 50 कोरूटीन को एक साथ लॉन्च करता है और गेटवे या तो दर सीमित करता है या उनमें से आधे को अस्वीकार करता है। इसके साथ, केवल 5 एक समय में चलन में होते हैं; बाकी घटना लूप में एक स्लॉट खुलने की प्रतीक्षा करते हैं।

मल्टी-होस्ट फैन-आउट के लिए, हर मूल के लिए एक सेमफोर बनाएं और इसे होस्टनाम द्वारा की करें — इस तरह एक मूल पर ठहराव अन्य के खिलाफ फ़ेच को बाधित नहीं करता है।

अपनी API कुंजी प्राप्त करें:

Scrapeless मुफ्त योजना


कदम 5 — पाइपलाइन को रोकने के बिना विफलताओं को संभालें

एक raise_for_status() एक कोरूटीन के अंदर पूरे gather को रद्द कर देगा और हर दूसरे चलने वाले परिणाम को खो देगा। दो बचाव:

बचाव 1: return_exceptions=True gather को बताएं कि वह अपवादों को मूल्यों के रूप में कैप्चर करे बजाय उनके प्रचारित करने के। पाइपलाइन किसी भी तरह समाप्त होती है; कॉलर बाद में तय करता है कि किन URL पर कार्रवाई करनी है।

बचाव 2: एक डेड-लेटर सूची। विफल URL को एक अलग संरचना में अलग समीक्षा के लिए एकत्र करें। विफलता संभालना बैंड से बाहर रहता है — जब सफलता और विफलता के रास्ते इंटरलीव नहीं होते हैं, तो आसिंक पाइपलाइंस साफ रहती हैं।

python Copy
import asyncio
import json
import aiohttp

async def fetch_safe(session, url):
    try:
        async with session.get(
            url, timeout=aiohttp.ClientTimeout(total=30)
        ) as resp:
            resp.raise_for_status()
            return {"url": url, "html": await resp.text()}
    except (aiohttp.ClientError, asyncio.TimeoutError) as exc:
        return {"url": url, "error": repr(exc)}

async def main(urls):
    async with aiohttp.ClientSession() as session:
        results = await asyncio.gather(
            *(fetch_safe(session, u) for u in urls)
        )

    ok = [r for r in results if "html" in r]
    failed = [r for r in results if "error" in r]
    print(f"सफल: {len(ok)}   विफल: {len(failed)}")

    # अलग समीक्षा के लिए डेड-लेटर फ़ाइल — पाइपलाइन कभी भी विफलताओं पर रुकती नहीं है
    with open("dead_letter.jsonl", "w", encoding="utf-8") as f:
        for r in failed:
            f.write(json.dumps(r) + "\n")

दो बातें नोट करने के लिए:

  • त्रुटि एंवेलप ({"url": ..., "error": ...}) सफल एंवेलप के समान आकार का है, बस एक अलग कुंजी के साथ। डाउनस्ट्रीम उपभोक्ता यह तय करते हैं कि कौन सी कुंजी मौजूद है बिना अपवाद पाठ को पार्स किए।
  • aiohttp.ClientError सामान्य विफलता सतह (संयोग ड्रॉप, गलत उत्तर, DNS समस्याएँ) को कवर करता है; asyncio.TimeoutError ClientTimeout द्वारा उठाया जाता है। इन दोनों को पकड़ना वास्तविक दुनिया के ~95% असिंक्रोनस स्क्रैप्स को कवर करता है।

यह कोड जानबूझकर यह नहीं करता है: सफलता के पथ में विफल URL को फिर से जारी नहीं करता है। डेड-लेटर प्रोसेसिंग एक अलग रन में होती है — अलग प्रॉक्सी देश, अलग समवर्ती सीमा, या चरण 6 से क्लाउड ब्राउज़र टियर के साथ। इसे इनलाइन मिलाना एक असिंक्रोनस स्क्रैपर को दो इंटरलीव्ड नियंत्रण प्रवाह में बदल देता है, और बग इंटरलीविंग में आते हैं।


कदम 6 — JS-प्रदर्शित पृष्ठों को Scrapeless स्क्रैपिंग ब्राउज़र में बढ़ाएं

aiohttp उस किसी भी बाइट को लौटाता है जो मूल भेजता है। Next.js, React और Vue ऐप्स के लिए, वे बाइट्स एक खाली <div id="root"> के साथ एक स्क्रिप्ट टैग हैं — वास्तविक सामग्री क्लाइंट-साइड पेंट होती है। सामान्य HTTP इसे प्रदर्शित नहीं कर सकता; एक क्लाउड ब्राउज़र कर सकता है।

सबसे साफ़ बढ़ाने का पैटर्न: उन ~70% पृष्ठों पर aiohttp रखें जो प्रदर्शित HTML में भेजे जाते हैं, और JS-प्रदर्शित अल्पसंख्यक को Scrapeless स्क्रैपिंग ब्राउज़र में बढ़ाएं। Python SDK एक क्लाउड-ब्राउज़र सत्र बनाता है और एक browser_ws_endpoint प्रदान करता है; Playwright की असिंक्रोनस API इसे Chrome DevTools प्रोटोकॉल के माध्यम से जोड़ती है:

python Copy
import asyncio
from scrapeless import Scrapeless
from scrapeless.types import ICreateBrowser
from playwright.async_api import async_playwright

async def render_via_cloud_browser(url: str, country: str = "US") -> str:
    client = Scrapeless()  # पर्यावरण से SCRAPELESS_API_KEY पढ़ता है
    session = client.browser.create(
        ICreateBrowser(proxy_country=country, session_ttl=240)
    )

    async with async_playwright() as p:
        browser = await p.chromium.connect_over_cdp(session.browser_ws_endpoint)
        context = browser.contexts[0] if browser.contexts else await browser.new_context()
        page = context.pages[0] if context.pages else await context.new_page()
        await page.goto(url, wait_until="networkidle", timeout=60_000)
        html = await page.content()
        await browser.close()
        return html
hi Copy
async def मुख्य():
    # quotes.toscrape.com/js/ कैननिकल "JS की ज़रूरत" सैंडबॉक्स है।
    # साधारण HTTP 0 उद्धरण तत्व लौटाता है; क्लाउड-Rendered 10 लौटाता है।
    html = await render_via_cloud_browser("https://quotes.toscrape.com/js/")
    print(f"Rendered {len(html):,} अक्षर पोस्ट-पेंट DOM सहित")

if __name__ == "__main__":
    asyncio.runमुख्य())

session.browser_ws_endpoint एक wss://browser.scrapeless.com/...?token=... URL है। Playwright का connect_over_cdp उस एंडपॉइंट से CDP से बात करता है; रेंडरिंग Scrapeless के क्लाउड में चलती है, स्थानीय मशीन पर नहीं। स्थानीय playwright install chromium चरण केवल प्रोटोकॉल क्लाइंट है।

session_ttl=240 सत्र को 4 मिनट के लिए जीवित रखता है — एक ही पृष्ठ पर मल्टी-स्टेप पारगमन के लिए पर्याप्त। लंबे समय तक चलने वाले क्रॉल के लिए, प्रत्येक URL या तार्किक कार्य-इकाई के लिए एक ताजा सत्र बनाएं; क्लाउड सत्र बनाना सस्ता है।


चरण 7 — इसे सब एक साथ लाना: एक स्तरित असिंक्रोनस स्क्रैपर

एक असिंक्रोनस स्क्रैपिंग पाइपलाइन का वास्तविक आकार HTTP पहले, ब्राउज़र बाद में है: aiohttp का प्रयास करें, रिक्त या अवरुद्ध प्रतिक्रियाओं को Scrapeless स्क्रैपिंग ब्राउज़र पर बढ़ाएं। दोनों स्तर समान सह-स्थायी कैप साझा करते हैं लेकिन अलग सेमाफोर में रहते हैं - क्लाउड-ब्राउज़र सत्र HTTP अनुरोधों की तुलना में अधिक दुर्लभ हैं।

python Copy
import asyncio
import os
import aiohttp
from scrapeless import Scrapeless
from scrapeless.types import ICreateBrowser
from playwright.async_api import async_playwright

PROXY = (
    f"http://{os.environ['SCRAPELESS_CHANNEL_ID']}-proxy-country_US"
    f":{os.environ['SCRAPELESS_PROXY_PASS']}"
    f"@{os.environ['SCRAPELESS_PROXY_GATEWAY']}:8789"
)
HTTP_LIMIT = asyncio.Semaphore(10)      # aiohttp स्तर
BROWSER_LIMIT = asyncio.Semaphore(3)    # क्लाउड ब्राउज़र स्तर

async def http_fetch(session: aiohttp.ClientSession, url: str) -> str | None:
    async with HTTP_LIMIT:
        try:
            async with session.get(
                url, proxy=PROXY,
                timeout=aiohttp.ClientTimeout(total=30),
            ) as resp:
                resp.raise_for_status()
                return await resp.text()
        except (aiohttp.ClientError, asyncio.TimeoutError):
            return None

async def browser_fetch(client: Scrapeless, url: str) -> str:
    async with BROWSER_LIMIT:
        session = client.browser.create(
            ICreateBrowser(proxy_country="US", session_ttl=240)
        )
        async with async_playwright() as p:
            browser = await p.chromium.connect_over_cdp(session.browser_ws_endpoint)
            context = (
                browser.contexts[0] if browser.contexts
                else await browser.new_context()
            )
            page = await context.new_page()
            await page.goto(url, wait_until="networkidle", timeout=60_000)
            html = await page.content()
            await browser.close()
            return html

from urllib.parse import urlparse

# (a) ज्ञात JS-भारी होस्ट हमेशा वृद्धि — सबसे विश्वसनीय संकेत।
JS_HEAVY_HOSTS = {"quotes.toscrape.com"}

def should_escalate(url: str, html: str | None) -> bool:
    # (a) अनुमति सूची हिट — स्पष्ट JS-भारी होस्ट।
    if urlparse(url).hostname in JS_HEAVY_HOSTS:
        return True
    # (b) पोस्ट-पार्स संकेत — खाली शरीर या पहचानने योग्य ऐप शेल।
    if html is None or len(html) < 2000 or '<div id="root"></div>' in html:
        return True
    return False

async def scrape_one(http_session, client, url):
    html = await http_fetch(http_session, url)
    tier = "http"
    if should_escalate(url, html):
        tier = "browser"
        html = await browser_fetch(client, url)
    return {"url": url, "tier": tier, "html_len": len(html) if html else 0}

async def मुख्य(urls):
    client = Scrapeless()
    async with aiohttp.ClientSession() as http_session:
        results = await asyncio.gather(
            *(scrape_one(http_session, client, u) for u in urls)
        )
    return results

if __name__ == "__main__":
    urls = [
        "https://books.toscrape.com/",       # स्थिर — aiohttp स्तर
        "https://quotes.toscrape.com/js/",   # JS — बढ़ता है
    ]
    print(asyncio.run(main(urls)))

should_escalate दोनों संकेतों को संयोजित करता है जो गद्य का उल्लेख करते हैं: (a) "ज्ञात JS-भारी" होस्टों की एक स्पष्ट अनुमति सूची, और (b) एक पोस्ट-पार्स संकेत (खाली शरीर / ऐप शेल)। अनुमति सूची अधिक विश्वसनीय लिवर है — एक Next.js या React शेल अक्सर 2,000-बाइट सीमा को साफ करता है जब भी शरीर खाली होता है, इसलिए <div id="root"></div> जांच के कारण इसे चूकना आसान है। होस्ट नाम की जांच किसी भी बाइट गिनने से पहले होती है।


आपको क्या मिलता है

पाइपलाइन एक ऐरे को उत्पन्न करती है जिसमें इस तरह के डिक्शनोरी का आकार होता है:

json Copy
[
  {
    "url": "https://books.toscrape.com/",
    "tier": "http",
    "html_len": 51274
  },
  {
    "url": "https://quotes.toscrape.com/js/",
    "tier": "browser",
    "html_len": 9246
  }
]

इस पैटर्न को चलाने से ईमानदार टिप्पणियाँ:

Copy
- **शीतल कनेक्शन लागत वास्तविक है।** एक ताजे `ClientSession` पर पहला अनुरोध TLS + DNS का भुगतान करता है; उसी सत्र पर बाद के अनुरोध कनेक्शन का पुन: उपयोग करते हैं। प्रति अनुरोध सत्र को फिर से न बनाएं।
- **समानांतरता सीमाएं लक्ष्य पर निर्भर करती हैं, aiohttp पर नहीं।** सार्वजनिक कैटलॉग के लिए प्रति होस्ट पाँच एक सुरक्षित प्रारंभिक बिंदु है; एंटी-बॉट-रक्षित मूल के लिए तीन सुरक्षित हैं; मित्रवत APIs के लिए दस वास्तविकता है।
- **क्लाउड-ब्राउज़र सत्र एकल पृष्ठ लोड से लंबे होते हैं।** यदि एक पाइपलाइन को लॉगिन के साथ-साथ यात्रा करने और निकालने की आवश्यकता है, तो एक तार्किक कार्य-यूनिट के लिए एक सत्र बनाएं और उस इकाई के भीतर पृष्ठों के बीच इसका पुन: उपयोग करें — `context.new_page()` उसी सत्र के भीतर सस्ता है।
- **DNS समाधान aiohttp के भीतर रहता है।** कनेक्टर `ClientSession` की अवधि के लिए हल किए गए IP को कैश करता है। लंबे समय तक चलने वाले क्रॉलर्स के लिए, हर कुछ घंटों में सत्र को पुनर्नवीनीकरण करें ताकि DNS बासी न हो जाए।
- **`ClientTimeout(total=30)` प्रति अनुरोध है, न कि प्रति-`gather`।** 1,000-URL फैन-आउट 30 सेकंड में टाइमआउट नहीं होता — प्रत्येक अनुरोध को अपना 30-सेकंड का बजट मिलता है।

---

## निष्कर्ष: अपने असिंक्रोनस Python स्क्रैपर्स को स्केल करें

असिंक्रोनस पैटर्न चार चालों में घटता है: एक `ClientSession` शुरू करें, `Semaphore` के साथ समानांतरता को सीमित करें, Scrapeless आवासीय प्रॉक्सी के माध्यम से फैन-आउट को रूट करें, और जावास्क्रिप्ट-रेन्डर किए गए अल्पसंख्यक को Scrapeless स्क्रैपिंग ब्राउज़र तक बढ़ाएं, Python SDK और Playwright के असिंक्रोनस API के माध्यम से।

अवासी-प्रॉक्सी परत पर गहराई में जाने के लिए, जो प्रत्येक असिंक्रोनस फ़ेच को रूट करता है, देखें [SSL प्रॉक्सी क्या है?](https://www.scrapeless.com/hi/blog/what-is-an-ssl-proxy-2026)।

प्रॉक्सी उपयोगकर्ता नाम पर देश के उपसर्ग के साथ ईग्रेस को पिन करें, प्रति-होस्ट सेमाफोर्स को कसकर रखें, सफलतापूर्वक बनाम विफलता की आकृति पर शाखा करें न कि अपवादों को इनलाइन में पकड़ते हुए, और एक खाली HTTP प्रतिक्रिया को बढ़ाने का संकेत मानें — न कि उत्तर।

---

## क्या आप अपने AI-संचालित डेटा पाइपलाइन बनाने के लिए तैयार हैं?

हमारे समुदाय में शामिल हों ताकि एक मुफ्त योजना प्राप्त कर सकें और डेवलपर्स के साथ जुड़ सकें जो असिंक्रोनस स्क्रैपिंग पाइपलाइनों का निर्माण कर रहे हैं: [Discord](https://discord.gg/scrapeless) · [Telegram](https://t.me/scrapeless)。

मुफ्त स्क्रैपिंग ब्राउज़र रनटाइम के लिए [Scrapeless](https://app.scrapeless.com/passport/login/?utm_source=website&utm_medium=blog&utm_campaign=scrapingbrowser&utm_term=async) पर साइन अप करें और ऊपर दिए गए पैटर्नों को कैटलॉग, फ़ीड और क्षेत्रों में अनुकूलित करें जिनकी पाइपलाइन को आवश्यकता है। मूल्य निर्धारण विवरण देखें [scrapeless.com/en/pricing](https://www.scrapeless.com/hi/pricing); आवासीय प्रॉक्सी का दस्तावेजीकरण [scrapeless.com/en/product/proxy-solutions](https://www.scrapeless.com/hi/product/proxy-solutions) पर है; पूर्ण SDK संदर्भ [docs.scrapeless.com](https://docs.scrapeless.com) पर है।

---

## सामान्य प्रश्न

**प्रश्न 1: मैं प्रति होस्ट कितने समवर्ती अनुरोध चलाना चाहिए?**

सार्वजनिक कैटलॉग के लिए जिनमें कोई एंटी-बॉट स्टैक नहीं है, प्रति होस्ट 10 इन-फ्लाइट अनुरोध एक सुरक्षित सीमा है। एंटी-बॉट-रक्षित मूल के लिए, 3 अधिक वास्तविक है। सेमाफोर एक लिवर है; कम से शुरू करें, 429 प्रतिक्रियाओं के लिए देखें, और वहां से ट्यून करें।

**प्रश्न 2: क्या मुझे Scrapeless आवासीय प्रॉक्सी की आवश्यकता है यदि मेरा लक्ष्य मेरे डेटा सेंटर से अनब्लॉक है?**

अनब्लॉक HTTP लक्ष्यों के लिए, नहीं — aiohttp प्रॉक्सी के बिना काम करता है। Scrapeless प्रॉक्सी परत अपनी जगह तब कमाती है जब लक्ष्य भू-सीमित होता है (आपको यूएस/यूके/जेपी ईग्रेस की आवश्यकता होती है), जब आपका डेटा सेंटर IP दर-सीमित या अवरुद्ध होता है, या जब आपको कई मूल के बीच उड़ान पूल फैलाने के लिए प्रत्येक अनुरोध के लिए एक ताजा आवासीय IP की आवश्यकता होती है।

**प्रश्न 3: मुझे कब aiohttp से Scrapeless स्क्रैपिंग ब्राउज़र पर बढ़ाना चाहिए?**

जब HTML जो aiohttp लौटाता है, एक JS-एप्लिकेशन शेल है जिसमें कोई सामग्री नहीं है। ह्यूरिस्टिक: पहले स्तर के फ़ेच के बाद आप जिन तत्वों की परवाह करते हैं उनकी गिनती करें; यदि गिनती शून्य या अपेक्षित से बहुत कम है, तो पृष्ठ क्लाइंट-साइड में रेंडर होता है। क्लाउड ब्राउज़र स्तर उनका प्रबंधन करता है।

**प्रश्न 4: क्या असिंक्रोनस स्क्रैपिंग कानूनी है?**

असिंक्रोनस एक परिवहन पैटर्न है; वैधता इस पर निर्भर करती है कि आप क्या स्क्रैप करते हैं, कहाँ से, और किन शर्तों के तहत। सार्वजनिक रूप से दृश्य डेटा आमतौर पर सुलभ होता है; न्यायालयों में भिन्नता होती है; साइट सेवा की शर्तें लागू होती हैं; उच्च-दांव उपयोग मामलों के लिए सलाह लेने के लिए परामर्श करें। Scrapeless केवल सार्वजनिक रूप से उपलब्ध डेटा पर पहुंचता है।

**प्रश्न 5: क्या मैं Scrapeless स्क्रैपिंग ब्राउज़र के बिना aiohttp का उपयोग कर सकता हूँ?**

हाँ। aiohttp स्तर (चरण 3–5) किसी भी लक्ष्य के लिए एक पूर्ण असिंक्रोनस स्क्रैपर के रूप में काम करता है जो रेंडर किया गया HTML भेजता है। Scrapeless स्क्रैपिंग ब्राउज़र बढ़ाने की परत है — केवल तब बुलाया जाता है जब HTTP स्तर खाली लौटता है।

**प्रश्न 6: मैं किसी विशिष्ट देश के लिए ईग्रेस को कैसे पिन करूं?**

देश को Scrapeless आवासीय-प्रॉक्सी उपयोगकर्ता नाम में `country_<CC>` (बड़े अक्षर दो-लेटर कोड, अंडरस्कोर से अलग किया गया) के रूप में डालें: `country_US`, `country_UK`, `country_DE`, `country_JP`। उपयोगकर्ता नाम स्ट्रिंग में चरण को बदलें और गेटवे हर अनुरोध को उस देश में आवासीय IP के माध्यम से रूट करता है। ब्राउज़र-स्तरीय अनुरोधों के लिए, क्लाउड-ब्राउज़र सत्र को बनाते समय `ICreateBrowser(...)` में `proxy_country="US"` पास करें।

**प्रश्न 7: Playwright के असिंक्रोनस API का उपयोग क्यों करें न कि एक समन्वयित ब्राउज़र क्लाइंट?**
सिंक ब्राउज़र क्लाइंट इवेंट लूप को रोकते हैं। asyncio का पूरा उद्देश्य लूप को फ्री रखना है; एक coroutine के अंदर से सिंक `page.goto(...)` को कॉल करने से हर अन्य चलती हुई टास्क रुक जाती है। Playwright का `async_playwright` ही एकमात्र मानक पायथन विकल्प है जो क्लाउड-ब्राउज़र स्तर को coroutine-मित्रवत रखता है। Scrapeless SDK अभी भी सत्र को मिंट करता है — Playwright केवल `browser_ws_endpoint` के लिए CDP से बात करता है।

स्क्रैपलेस में, हम केवल सार्वजनिक रूप से उपलब्ध डेटा का उपयोग करते हैं, जबकि लागू कानूनों, विनियमों और वेबसाइट गोपनीयता नीतियों का सख्ती से अनुपालन करते हैं। इस ब्लॉग में सामग्री केवल प्रदर्शन उद्देश्यों के लिए है और इसमें कोई अवैध या उल्लंघन करने वाली गतिविधियों को शामिल नहीं किया गया है। हम इस ब्लॉग या तृतीय-पक्ष लिंक से जानकारी के उपयोग के लिए सभी देयता को कोई गारंटी नहीं देते हैं और सभी देयता का खुलासा करते हैं। किसी भी स्क्रैपिंग गतिविधियों में संलग्न होने से पहले, अपने कानूनी सलाहकार से परामर्श करें और लक्ष्य वेबसाइट की सेवा की शर्तों की समीक्षा करें या आवश्यक अनुमतियाँ प्राप्त करें।

सबसे लोकप्रिय लेख

सूची