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

स्क्रैपेलेस के साथ सर्वर द्वारा भेजे जाने वाले इवेंट्स (SSE) स्ट्रीम्स को स्क्रैप करना

Michael Lee
Michael Lee

Expert Network Defense Engineer

05-Aug-2026

एक लाइव ब्लॉग की टिप्पणी काउंटर, एक समर्थन विजेट के "एजेंट टाइप कर रहा है" संकेतक, और एक AI चैट उत्तर जो शब्द दर शब्द भरता है सभी में एक समान विशेषता है: ब्राउज़र ने एक बार कनेक्शन खोला, और सर्वर तब से उसी खुली प्रतिक्रिया में हर अपडेट को धकेल रहा है। टिप्पणी काउंटर के लिए कोई दूसरा अनुरोध नहीं था, टाइपिंग संकेतक के लिए कोई पोलिंग लूप नहीं था - एक HTTP GET, खुला रखा गया, Content-Type: text/event-stream के साथ, और सर्वर जब भी कुछ बदलता है data: {...}\n\n उस पर लिखता है। एक उपकरण जो पृष्ठ को एक बार लाता है और आगे बढ़ता है वह कभी भी इनमें से कोई भी नहीं देखता, क्योंकि डेटा प्रारंभिक उत्तर हेडर के बाद आता है, एक कनेक्शन पर जो कभी बंद नहीं होता है।

Playwright को Scrapeless Scraping Browser के साथ wss://browser.scrapeless.com/api/v2/browser पर कनेक्ट करें, और इसके नीचे CDP सत्र आपको उन धकेले गए फ़्रेमों को पढ़ने के लिए दो अलग-अलग तरीके देता है जैसे वे आते हैं: Playwright का अपना स्ट्रीम की गई प्रतिक्रिया रीडर, और Chrome DevTools प्रोटोकॉल से कच्चा Network.eventSourceMessageReceived इवेंट। यह गाइड उस क्लाउड ब्राउज़र से कनेक्ट होता है, एक असली सर्वर-प्रेषित घटनाएँ (SSE) स्ट्रीम खोलता है, और दोनों तरीकों से फ़्रेम कैप्चर करता है, हर कोड पथ एक लाइव सार्वजनिक फ़ीड पर चलाया जाता है।

SSE को एक अलग कैप्चर पथ की आवश्यकता क्यों है

page.goto() के बाद एक DOM पढ़ने से केवल वही दिखता है जो उस क्षण में पृष्ठ केMarkup में होता है। SSE-फीडेड विजेट कभी भी पूरे पृष्ठ को फिर से नहीं दर्शाता - यह हर बार एक नया data: लाइन आती है तो एक टुकड़ा जोड़ता या बदलता है, इसलिए एकल DOM स्नैपशॉट केवल उस अपडेट को पकड़ता है जो वर्तमान में था जब आपने देखा। अपडेट स्वयं कभी भी DOM को छूते नहीं हैं यदि पृष्ठ में उन्हें दर्शाने के लिए कुछ भी नहीं है; उन्हें पढ़ने के लिए एकमात्र विश्वसनीय स्थान स्ट्रीम स्वयं है।

SSE भी दो अन्य रियल-टाइम ट्रांसपोर्ट्स की तुलना में संकीर्ण मामला है जिन्हें ब्राउज़र खोल सकता है। एक छुपा JSON एंडपॉइंट एक अनुरोध का एक उत्तर देता है - इसे page.expect_response() के साथ पढ़ें और आप समाप्त हैं। एक वेब-सॉकट को एक Upgrade: websocket हैंडशेक की आवश्यकता होती है इससे पहले कि कोई भी पक्ष फ़्रेम भेज सके, और एक बार खुल जाने पर यह पूर्ण-द्विपक्षीय होता है - कोई भी पक्ष किसी भी समय लिख सकता है। SSE को इसकी आवश्यकता नहीं है: WHATWG सर्वर-प्रेषित घटनाओं की विनिर्देशन इसे एक साधारण HTTP प्रतिक्रिया के रूप में परिभाषित करता है जिसका शरीर कभी समाप्त नहीं होता है, जो एक साधारण GET के उत्तर के रूप में भेजा जाता है, जिसे पढ़ने के लिए कुछ अधिक असामान्य की आवश्यकता नहीं होती है सिवाय एक स्ट्रीमिंग बॉडी रीडर के। सर्वर इसमें लिखता है; क्लाइंट केवल पढ़ता है।

Chrome DevTools प्रोटोकॉल उस स्ट्रीम को सीधे उजागर करता है, उसी तरह जैसे यह WebSocket फ़्रेमों और इंटरसेप्टेड HTTP प्रतिक्रियाओं को उजागर करता है। इसका नेटवर्क डोमेन हर संदेश के लिए एक समर्पित eventSourceMessageReceived इवेंट फायर करता है जो पृष्ठ के EventSource कनेक्शन को प्राप्त होता है, जो सामान्य प्रतिक्रिया-शरीर घटनाओं से अलग है जो एक साधारण फेच के लिए फायर होती हैं। Scrapeless Scraping Browser एक क्लाउड क्रोमियम सत्र है जो केवल CDP के माध्यम से पहुँचा जा सकता है - इसे चलाने के लिए कोई WebDriver/Selenium अंत बिंदु नहीं है - इसलिए कोई भी CDP-सक्षम क्लाइंट, यहाँ Playwright, दोनों परतों को पढ़ सकता है: ब्राउज़र का अपना स्ट्रीम किया गया बॉडी, या उसके नीचे का प्रोटोकॉल इवेंट।

आवश्यकताएँ

आपको Python 3.9 या उससे नया चाहिए - playwright 1.59.0 PyPI पर Requires-Python >=3.9 घोषित करता है - playwright पैकेज, और app.scrapeless.com पर मुफ्त योजना से एक Scrapeless API कुंजी। स्थानीय Chrome बाइनरी की आवश्यकता नहीं है: connect_over_cdp एक ब्राउज़र तक पहुँचता है जो पहले से ही Scrapeless के क्लाउड में मौजूद है।

नीचे दिए गए उदाहरण Wikimedia Foundation के सार्वजनिक recentchange स्ट्रीम से कनेक्ट होते हैं, जो Wikimedia की अपनी EventStreams सेवा पृष्ठ पर दस्तावेजीकृत है। इसे किसी API कुंजी और किसी खाते की आवश्यकता नहीं है - हर Wikimedia परियोजना में हर संपादन डिज़ाइन द्वारा सार्वजनिक है, और स्ट्रीम विशेष रूप से इसलिए मौजूद है ताकि उपकरण इसे उपभोग कर सकें। दोनों उदाहरण खुद को पांच असली फ़्रेमों के बाद रोक देते हैं, इसलिए न तो चलाना कनेक्शन को अधिक समय तक खुला रखता है जितना इसे साबित करने के लिए आवश्यक है कि कैप्चर काम करता है।

स्थापना

bash Copy
pip install playwright
bash Copy
export SCRAPELESS_API_KEY="your_scrapeless_api_key"

CDP के माध्यम से कनेक्ट करें

किसी भी Playwright-to-Scraping-Browser स्क्रिप्ट द्वारा उपयोग किए जाने वाले उसी URL-निर्माता पैटर्न का पुन: उपयोग करें: एक WSS एंडपॉइंट पर तीन क्वेरी पैरामीटर।

python Copy
import os
from urllib.parse import urlencode

API_KEY = os.environ["SCRAPELESS_API_KEY"]

def scraping_browser_url(proxy_country="US", session_ttl=60):
    params = urlencode({"token": API_KEY, "sessionTTL": session_ttl, "proxyCountry": proxy_country})
    return f"wss://browser.scrapeless.com/api/v2/browser?{params}"

एक भू-सीमाबद्ध बाजार-डेटा सॉकेट के विपरीत, विकिमीडिया की सार्वजनिक स्ट्रीम किसी भी क्षेत्र से कनेक्शन स्वीकार करती है: दोनों proxyCountry="US" और proxyCountry="DE" हाथ मिलाते हैं और इस सत्र के लाइव रनों में फ़्रेम वितरित करना शुरू करते हैं, बिना किसी बंद कोड या कनेक्शन विफलता के। proxyCountry अभी भी कई वास्तविक लक्ष्यों के लिए महत्वपूर्ण है - एक विशिष्ट बाजार में गेटेड स्ट्रीम इस श्रृंखला में कहीं और एक वास्तविक विफलता मोड है - यह बस इस विशेष सार्वजनिक फीड पर प्रतिबंध नहीं है। इसे अपना खुद का लक्ष्य बनाम किसी भी परिणाम की धारणा के अनुसार पुष्टि करें।

प्लेव्राइट की प्रतिक्रिया स्ट्रीमिंग के साथ फ़्रेम कैप्चर करें

प्लेव्राइट का प्रतिक्रिया घटना एपीआई तुरंत page.on("response") को फायर करता है जब प्रतिक्रिया के हेडर आते हैं, बिना शरीर के समाप्त होने की प्रतीक्षा किए - जो यहाँ महत्वपूर्ण है, क्योंकि SSE प्रतिक्रिया अपने आप समाप्त नहीं होती। इसे पृष्ठ के अपने fetch() और ReadableStream रीडर के साथ मिलाएं, और आप चunks के उतरने के साथ शरीर को पढ़ सकते हैं, बजाय इसके कि आपको किसी पूर्णता घटना की प्रतीक्षा करनी पड़े जो कभी नहीं आती:

python Copy
import json
import os
from urllib.parse import urlencode

from playwright.sync_api import sync_playwright

API_KEY = os.environ["SCRAPELESS_API_KEY"]
FRAME_LIMIT = 5
STREAM_URL = "https://stream.wikimedia.org/v2/stream/recentchange"

def scraping_browser_url(proxy_country="US", session_ttl=60):
    params = urlencode({"token": API_KEY, "sessionTTL": session_ttl, "proxyCountry": proxy_country})
    return f"wss://browser.scrapeless.com/api/v2/browser?{params}"

responses_seen = []

def handle_response(response):
    if response.url == STREAM_URL:
        responses_seen.append((response.status, response.headers.get("content-type")))

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(scraping_browser_url())
    page = browser.new_page()
    page.on("response", handle_response)

    page.goto("about:blank")
    frames = page.evaluate(
        f"""async () => {{
            const resp = await fetch("{STREAM_URL}", {{ headers: {{ "Accept": "text/event-stream" }} }});
            const reader = resp.body.getReader();
            const decoder = new TextDecoder();
            let buffer = "";
            const out = [];
            while (out.length < {FRAME_LIMIT}) {{
                const {{ done, value }} = await reader.read();
                if (done) break;
                buffer += decoder.decode(value, {{ stream: true }});
                let idx;
                while ((idx = buffer.indexOf("\\n\\n")) !== -1 && out.length < {FRAME_LIMIT}) {{
                    const rawEvent = buffer.slice(0, idx);
                    buffer = buffer.slice(idx + 2);
                    const dataLine = rawEvent.split("\\n").find(l => l.startsWith("data:"));
                    if (dataLine) out.push(dataLine.slice(5).trim());
                }}
            }}
            await reader.cancel();
            return out;
        }}"""
    )
    browser.close()

print(f"response seen via page.on('response'): status={responses_seen[0][0]}, content-type={responses_seen[0][1]}")
print(f"captured {len(frames)} frames via in-page fetch() stream reader")
print(json.dumps(json.loads(frames[0]), indent=2))

इसे लाइव फ़ीड के खिलाफ चलाने पर एक वास्तविक विकिमीडिया संपादन घटना का प्रिंट होता है, साथ ही प्रतिक्रिया प्लेव्राइट ने स्वयं देखी:

text Copy
response seen via page.on('response'): status=200, content-type=text/event-stream; charset=utf-8
captured 5 frames via in-page fetch() stream reader
{
  "$schema": "/mediawiki/recentchange/1.0.0",
  "meta": {
    "uri": "https://de.wikipedia.org/wiki/Liste_der_Kulturdenkmale_in_Oschatz",
    "domain": "de.wikipedia.org",
    "stream": "mediawiki.recentchange",
    "dt": "2026-07-28T14:16:13.757Z"
  },
  "id": 382817780,
  "type": "edit"
}

page.on("response") यह पुष्टि करता है कि प्लेव्राइट की अपनी नेटवर्क परत ने बाहरी HTTP प्रतिक्रिया पर SSE सामग्री प्रकार के साथ 200 देखा - इसका प्रमाण यह है कि यह एक कनेक्शन है, न कि पांच अलग-अलग अनुरोध। पृष्ठ में लूप फिर उसी कनेक्शन के शरीर को एक बार में एक चंक पढ़ता है, उस खाली पंक्ति पर विभाजित करता है जो इवेंट-स्ट्रीम स्वरूप रिकॉर्ड को अलग करने के लिए उपयोग करता है, और प्रत्येक में से data: लाइन को बाहर खींचता है। reader.cancel() उस क्षण संबंध को बंद कर देता है जब पांच फ़्रेम हाथ में होते हैं, इसलिए यहाँ कुछ भी विकिमीडिया की स्ट्रीम को उस समय से आगे नहीं बढ़ाता है जो सबूत की आवश्यकता है।

CDP नेटवर्क डोमेन से कच्चे फ़्रेम कैप्चर करें

fetch() स्ट्रीम को हाथ से पढ़ना काम करता है, लेकिन इससे Chrome के अपने नेटवर्क स्टैक को कनेक्शन को एक EventSource के रूप में पहचानने में मदद नहीं मिलती - वही पहचान CDP eventSourceMessageReceived इवेंट को ट्रिगर करती है, और यह केवल तभी ट्रिगर होता है जब पृष्ठ स्ट्रीम को ब्राउज़र के नेटिव EventSource ऑब्जेक्ट के साथ खोलता है न कि एक साधारण fetch() कॉल के साथ। जब आप स्ट्रीम के लिए ब्राउज़र के अपने लेखा-जोखा को चाहें, तो कच्चे CDP इवेंट के लिए पहुँचें, इसके बजाय कि हाथ से तैयार किया गया पार्सर: यह eventName, eventId, और data को अलग-अलग फ़ील्ड के रूप में लौटाता है, न कि कच्चे पाठ के रूप में जिसे आपको स्वयं विभाजित करना है।

python Copy
import json
import os
from urllib.parse import urlencode

from playwright.sync_api import sync_playwright

API_KEY = os.environ["SCRAPELESS_API_KEY"]
FRAME_LIMIT = 5
STREAM_URL = "https://stream.wikimedia.org/v2/stream/recentchange"

def scraping_browser_url(proxy_country="US", session_ttl=60):
    params = urlencode({"token": API_KEY, "sessionTTL": session_ttl, "proxyCountry": proxy_country})
    return f"wss://browser.scrapeless.com/api/v2/browser?{params}"

cdp_events = []

def on_sse_message(event):
    cdp_events.append(event)

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(scraping_browser_url())
    page = browser.new_page()

    cdp = page.context.new_cdp_session(page)
    cdp.send("Network.enable")
    cdp.on("Network.eventSourceMessageReceived", on_sse_message)

    page.goto("about:blank")
    page.evaluate(
        f"""() => {{
            window.__count = 0;
            const es = new EventSource("{STREAM_URL}");
            window.__es = es;
            es.onmessage = () => {{
                window.__count += 1;
                if (window.__count >= {FRAME_LIMIT}) {{ es.close(); }}
            }};
        }}"""
    )
    page.wait_for_function(f"window.__count >= {FRAME_LIMIT}", timeout=20000)
    page.wait_for_timeout(300)
    browser.close()

print(f"captured {len(cdp_events)} raw CDP eventSourceMessageReceived events")
event = cdp_events[0]
print(f"eventName={event['eventName']}")
print(f"eventId={event['eventId']}")
print(json.dumps(json.loads(event["data"]), indent=2))

कच्चा प्रोटोकॉल इवेंट उसी संपादित पेलोड को ले जाता है, इसके साथ उन फ़ील्ड को जो fetch-आधारित पार्सर को छोड़ना पड़ा:

text Copy
captured 5 raw CDP eventSourceMessageReceived events
eventName=message
eventId=[{"topic":"eqiad.mediawiki.recentchange","partition":0,"timestamp":1785248186774},{"topic":"codfw.mediawiki.recentchange","partition":0,"offset":-1}]
{
  "$schema": "/mediawiki/recentchange/1.0.0",
  "meta": {
    "uri": "https://www.wikidata.org/wiki/Q100886493",
    "domain": "www.wikidata.org",
    "stream": "mediawiki.recentchange",
    "dt": "2026-07-28T14:16:26.773Z"
  },
  "id": 2602974671,
  "type": "edit"
}

page.context.new_cdp_session(page) एक सत्र खोलता है जिसकी CDPSession इंटरफेस प्रोटोकॉल कमांड के लिए send() और प्रोटोकॉल इवेंट के लिए on() को उजागर करती है; Network.enable इवेंट रिपोर्टिंग को चालू करता है, और हर बाद का eventSourceMessageReceived इवेंट बिलकुल वही eventName/eventId/data आकार के साथ ट्रिगर होता है जैसा कि डेवलपर टूल्स नेटवर्क पैनल EventSource पंक्ति के लिए पढ़ता है। यहाँ eventId कोई साधारण काउंटर नहीं है - Wikimedia ने इसमें Kafka टॉपिक, पार्टिशन और ऑफ़सेट मेटाडेटा को एन्कोड किया है, क्योंकि वह मान एक रिस्यूम कर्सर के रूप में दोगुना होता है: इसके साथ फिर से कनेक्ट करें जैसा कि Last-Event-ID अनुरोध हेडर के रूप में, और सेवा उस सटीक स्थिति से उठाती है बजाय इसके कि प्रारंभ से फिर से खेला जाए।

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

दोनों रास्ते इस स्ट्रीम के लिए समान स्थायी संपादित इवेंट लौटाते हैं, क्योंकि दोनों एक ही खुले कनेक्शन के धकेले गए रिकॉर्ड को पढ़ रहे हैं - एक सामान्य fetch के ऊपर एक हाथ से तैयार किए गए पार्सर के माध्यम से, एक प्रोटोकॉल के अपने SSE लेखा-जोखा से सीधे।

फ़ील्ड स्रोत मतलब
event / eventName SSE फ़्रेम इवेंट प्रकार; इस स्ट्रीम पर प्रत्येक रिकॉर्ड के लिए "message"
id / eventId SSE फ़्रेम रिस्यूम कर्सर - कनेक्ट करने पर Last-Event-ID के रूप में पास करें
data SSE फ़्रेम JSON पेलोड स्वयं
$schema पेलोड इस रिकॉर्ड के आकार के लिए स्कीमा URI
meta.domain पेलोड किस Wikimedia प्रोजेक्ट पर संपादन हुआ
meta.dt पेलोड परिवर्तन का ISO 8601 टाइमस्टैम्प
type पेलोड edit, new, log, या categorize

Wikimedia का अपना दस्तावेज़ उन रिकॉर्ड को फ़िल्टर करने की सिफारिश करता है जहाँ meta.domain "canary" के बराबर है - सिंथेटिक हार्टबीट इवेंट्स जो सेवा अपने स्वयं की निगरानी के लिए इंजेक्ट करती है, असली संपादन नहीं। इस फ़ीड का कोई भी उपभोक्ता रिकॉर्ड को उपयोगकर्ता गतिविधि के रूप में मानने से पहले उन्हें हटा देना चाहिए, उसी तरह जैसे आप एक कीप-एलाइव टिप्पणी लाइनों को हटाते हैं (: keepalive\n\n) जो कुछ SSE सर्वर Idle कनेक्शन को खोलने के लिए भेजते हैं; प्रारूप एक SSE पंक्ति को शुरू करने की अनुमति देता है जो : के साथ एक टिप्पणी के रूप में हो सकती है जिसमें कोई data: फ़ील्ड नहीं होता, और उपरोक्त दोनों कैप्चर पथ पहले से ही स्वाभाविक रूप से इसे छोड़ देते हैं क्योंकि कोई भी वहाँ सामग्री की तलाश नहीं करता है।
अपने मुफ्त योजना पर अपना एपीआई कुंजी प्राप्त करें: app.scrapeless.com

SSE और WebSocket प्रैक्टिस में

दोनों प्रोटोकॉल ओवरलैपिंग समस्याओं को विभिन्न व्यापारिक समझौतों के साथ हल करते हैं, और गलत को चुनने पर असली डिबगिंग समय बर्बाद होता है। एक WebSocket को कोई फ्रेम स्थानांतरित करने से पहले 101 Switching Protocols हैंडशेक की आवश्यकता होती है और यह पूर्ण-द्विवर्णीय रूप से खुला रहता है; इस श्रृंखला में WebSocket कैप्चर गाइड के अनुसार, कोई भी ड्रॉप किया गया WebSocket अपने आप फिर से कनेक्ट नहीं करता है। SSE एक साधारण GET को 200 और Content-Type: text/event-stream के साथ जवाब देता है, केवल सर्वर ही लिखता है, और ब्राउज़र का मूल EventSource वस्तु अपने आप फिर से कनेक्ट करता है। WHATWG स्पेक के अनुसार, यदि कनेक्शन बंद हो जाता है तो उपयोगकर्ता एजेंट एक कार्यान्वयन-निर्धारित विलंब (सामान्यत: कुछ सेकंड) की प्रतीक्षा करता है, फिर अनुरोध को फिर से खोलता है, स्वचालित रूप से Last-Event-ID संलग्न करता है ताकि सर्वर फिर से शुरू कर सके बजाय सब कुछ फिर से चलाने के।

यह पुन: कनेक्शन व्यवहार इस गाइड में CDP-स्तरीय भेद के व्यावहारिक महत्व का कारण है: एक कच्चा fetch() रीडर को पुन: कनेक्शन और Last-Event-ID ट्रैकिंग को हाथ से फिर से लागू करना पड़ता है, जबकि एक असली EventSource वस्तु इसे ब्राउज़र से मुफ्त में प्राप्त करती है - नए कनेक्शन के खुलने के समय पर सीधे नियंत्रण खोने की कीमत पर। जब आप ब्राउज़र को पुन: कनेक्शन को प्रबंधित करने के लिए चाहते हैं, तो एक असली EventSource के साथ लक्ष्य के ट्रैफिक को पढ़ें; जब आपको खुद कनेक्शन जीवनकाल को नियंत्रित करने की आवश्यकता होती है, जैसे कि रिकॉर्ड की सीमित संख्या के बाद रद्द करना, तो इसे fetch() और एक स्ट्रीम रीडर के साथ पढ़ें जैसे कि दोनों उदाहरण ऊपर करते हैं।

पहले से खुले असली पृष्ठ से SSE कनेक्शन पढ़ना

ऊपर दोनों कैप्चर विधियाँ एक अन्यथा खाली पृष्ठ से कनेक्शन खोलती हैं, क्योंकि इससे लक्ष्य छोटा और सार्वजनिक रहता है। एक लाइव डैशबोर्ड, एक चैट UI, या एक सूचना फीड उसी तरह अपने अपने बंडल किए गए जावास्क्रिप्ट से अपना EventSource या स्ट्रीम की गई fetch() खोलता है, जैसे ही प्रासंगिक घटक माउंट होता है - और page.on("response") और CDP Network डोमेन दोनों तरिके से एक समान तरीके से कार्य करते हैं। सच्चे लक्ष्य पर page.goto() कॉल करने से पहले समान हैंडलर संलग्न करें, बजाय about:blank पर, और फ़्रेम तब आते हैं जब पृष्ठ की अपनी स्क्रिप्ट उन्हें प्राप्त करती है; कैप्चर तर्क के संबंध में कुछ भी नहीं बदलता है क्योंकि कनेक्शन पृष्ठ से संबंधित होता है बजाय एक page.evaluate() कॉल के। जो कुछ बदलता है वह खोज है - लक्ष्य के अपने नेटवर्क पैनल को एक बार खोलें, EventSource या Fetch/XHR द्वारा फ़िल्टर करें, और उसके चारों ओर एक हैंडलर लिखने से पहले अंत बिंदु और इसके Content-Type की पुष्टि करें, बजाय पहले से स्ट्रीम URL अनुमान लगाने के।

निष्कर्ष

एक SSE कनेक्शन एक ब्राउज़र द्वारा खोला जा सकने वाला तीन वास्तविक समय परिवहन में सबसे सरल है - एक GET, एक खुला उत्तर, कोई हैंडशेक नहीं - और यही सरलता है कि एक टूल जो केवल DOM को पढ़ता है या प्रतिक्रिया के समाप्त होने की प्रतीक्षा करता है, इसे कभी नहीं देखता। Playwright का page.on("response") एक इन-पृष्ठ स्ट्रीम रीडर के साथ और कच्चा CDP Network.eventSourceMessageReceived इवेंट दोनों वही धकेला हुआ डेटा पढ़ते हैं, एक हाथ से बना पार्सर के माध्यम से और एक सीधे प्रोटोकॉल से, और दोनों ने Scrapeless Scraping Browser के CDP कनेक्शन पर असली सार्वजनिक Wikimedia संपादन स्ट्रीम के खिलाफ एक समान तरीके से काम किया। कनेक्शन बंद करने से पहले आप कितने रिकॉर्ड कैप्चर करते हैं, इस पर सीमा लगाएं, यदि आप फिर से कनेक्ट करते हैं तो स्ट्रीम के id: क्षेत्र में जो रेज़्यूमे कर्सर होता है, उसका सम्मान करें, और स्क्रिप्ट का बाकी हिस्सा वही कुछ Playwright कॉल है जो यह गाइड पहले ही पार कर चुका है। Scraping Browser उत्पाद पृष्ठ पर वर्तमान सत्र और निकासी सीमाएँ पढ़ें और प्राइसिंग पृष्ठ पर योजना सीमाएँ चेक करें। इस गाइड पर आधारित कनेक्शन की यांत्रिकी के लिए, Chrome DevTools प्रोटोकॉल व्याख्याकार बताता है कि CDP नेटवर्क डोमेन के पार क्या एक्सपोज करता है।

एक मुफ्त योजना का दावा करने और अन्य डेवलपर्स के साथ नोट्स की तुलना करने के लिए हमारे समुदाय में शामिल हों: Discord · Telegram.

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

प्रश्न: क्या मुझे इस तरीके से SSE फ्रेम कैप्चर करने के लिए Selenium या WebDriver की आवश्यकता है?
नहीं। स्क्रेपलेस स्क्रैपिंग ब्राउज़र केवल क्रोम डेवलपर्स प्रोटोकॉल के माध्यम से पहुँच योग्य है, इसलिए कोई भी क्लाइंट जो सीडीपी बोल सकता है - यहाँ प्ले राइट या पपेटियर - कनेक्ट कर सकता है और स्ट्रीम पढ़ सकता है। कोई वेबड्राइवर एंडपॉइंट नहीं है, इसलिए सेलेनियम इस कनेक्शन को नहीं चला सकता।

प्रश्न: साधारण fetch() रीडर के द्वारा CDP eventSourceMessageReceived इवेंट को क्यों सक्रिय नहीं किया जाता?

क्रोम का नेटवर्क स्टैक केवल तभी एक कनेक्शन को इवेंटस्रोत के रूप में वर्गीकृत करता है और उस समर्पित इवेंट के माध्यम से इसकी रिपोर्ट करता है, जब पृष्ठ इसे मूल EventSource ऑब्जेक्ट के साथ खोलता है। एक fetch() कॉल उच्चारण स्तर पर उसी तरह बाइट्स को स्ट्रीम करता है, लेकिन क्रोम इसे एसएसई के रूप में पार्स या टैग नहीं करता है, इसलिए इसे पढ़ने के लिए आपको स्वयं text/event-stream प्रारूप को पार्स करना होगा, जैसा कि इस गाइड में प्रतिक्रियात्मक-स्ट्रीमिंग उदाहरण में किया गया है।

प्रश्न: क्या एसएसई कनेक्शन अपने आप पुन: कनेक्ट हो जाता है यदि यह टूट जाता है?

केवल जब इसे मूल EventSource ऑब्जेक्ट के साथ खोला जाता है। WHATWG विनिर्देशन के अनुसार, ब्राउज़र एक कार्यान्वयन-परिभाषित विलंब का इंतज़ार करता है और फिर आखिरी id: मान सेट करके अनुरोध को फिर से खोलता है जिसे उसने देखा था, ताकि एक अच्छी तरह से काम करने वाला सर्वर फिर से शुरू कर सके बजाय इसके कि शुरुआत से पुनः चलाए। एक fetch()-आधारित रीडर को इनमें से कोई भी स्वचालित रूप से प्राप्त नहीं होता है - पुनः कनेक्शन और Last-Event-ID ट्रैकिंग को हाथ से लिखा जाना चाहिए।

प्रश्न: यह इस श्रृंखला में नेटवर्क-रिक्वेस्ट-इंटरसेप्शन तकनीक से कैसे भिन्न है?

इंटरसेप्शन विशिष्ट अनुरोध/प्रतिक्रिया युग्मों को पढ़ता है - एक पृष्ठ हर बार ताजा डेटा की आवश्यकता होने पर एक नया एचटीटीपी अनुरोध करता है, और आप प्रत्येक को पकड़ते हैं। एक एसएसई कनेक्शन एकल अनुरोध है जिसका प्रतिक्रिया शरीर कभी समाप्त नहीं होता; इसमें बार-बार इंटरसेप्ट करने के लिए कुछ नहीं है, क्योंकि सर्वर पहले से भेजे गए एक प्रतिक्रिया में लगातार लिखता रहता है।

प्रश्न: : टिप्पणी लाइनों या विकिमीडिया की स्ट्रीम पर canary इवेंट के साथ क्या होता है?

एसएसई प्रारूप में : के साथ शुरू होने वाली एक पंक्ति एक टिप्पणी होती है जिसमें कोई data: फ़ील्ड नहीं होता, जो कुछ सर्वरों द्वारा एक निष्क्रिय कनेक्शन को जीवित रखने के लिए भेजी जाती है; इस गाइड में दोनों कैप्चर पथ केवल data: लाइनों की तलाश करते हैं, इसलिए टिप्पणी पंक्ति स्वचालित रूप से छोड़ी जाती है। विकिमीडिया भी अपनी निगरानी के लिए कृत्रिम meta.domain: "canary" रिकॉर्ड जोड़ता है - एक रिकॉर्ड को वास्तविक संपादन के रूप में मानने से पहले इन्हें फ़िल्टर करें।

प्रश्न: क्या किसी भी एसएसई एंडपॉइंट के खिलाफ इसे चलाना सुरक्षित है?

केवल सार्वजनिक, बिना प्रामाणीकरण वाले एंडपॉइंट्स के खिलाफ जिन्हें पढ़ने की अनुमति है, और केवल उस मात्रा में जो एंडपॉइंट के अपने दस्तावेज़ में अनुमति है। यहाँ का उदाहरण विकिमीडिया के प्रलेखित सार्वजनिक संपादन स्ट्रीम को लक्षित करता है, किसी कुंजी की आवश्यकता नहीं है और यह पांच रिकॉर्ड के बाद कनेक्शन बंद कर देता है बजाय इसके कि इसे अनिश्चितकाल के लिए खुला रखा जाए।

प्रश्न: एसएसई कनेक्शन कितनी देर तक खुला रह सकता है?

sessionTTL प्रश्न प्रस्तुतकर्ता ब्राउज़र सत्र को सेकंड में सीमित करता है। कम मूल्य एक सीमित कैप्चर के लिए पर्याप्त है जैसा कि इस गाइड में है; एक लंबा मूल्य सत्र – और कोई भी खुली स्ट्रीम कनेक्शन – को लंबे समय तक चलने वाले उपभोक्ता के लिए जीवित रखता है।

प्रश्न: क्या मैं बिना किसी ब्राउज़र के एसएसई स्ट्रीम पढ़ सकता हूँ?

हाँ, एक सार्वजनिक बिना प्रामाणीकरण वाले एंडपॉइंट के लिए जैसे कि यह — एक साधारण HTTP क्लाइंट जो चंक रीड का समर्थन करता है उसी text/event-stream प्रारूप को सीधे पार्स कर सकता है। इस गाइड में ब्राउज़र सत्र तब मूल्यवान है जब लक्ष्य पहले स्थान पर कनेक्शन स्थापित करने के लिए एक वास्तविक क्रोमियम फिंगरप्रिंट की आवश्यकता होती है, या जब एक पृष्ठ स्वयं अपने जावास्क्रिप्ट के एक साइड इफेक्ट के रूप में स्ट्रीम खोलता है बजाय इसके कि यह एक दस्तावेजित स्टैंडअलोन एंडपॉइंट को उजागर करे।

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

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

सूची