प्ले राइट + स्क्रेपलेस स्क्रैपिंग ब्राउज़र: एक छिपी हुई GraphQL API को कैप्चर और रीप्ले करें
Web Data Collection Specialist
नेटवर्क टैब को rickandmortyapi.com/graphql पर खोलें और इसे एक मिनट तक देखें: स्कीमा-इंट्रोस्पेक्शन कॉल GraphiQL उस पल होती है जब पृष्ठ लोड होता है, और जो क्वेरी आप टाइप करते हैं और थोड़ी देर बाद स्वयं चलाते हैं, दोनों बिल्कुल एक ही URL पर पहुँचते हैं। एक REST API अपने व्यवहार को पथों पर फैलाता है — /characters, /episodes, /locations/1 — इसलिए URL अकेले ही आपको बताता है कि अनुरोध किसके लिए है। एक GraphQL API इन सभी को एक एंडपॉइंट में समाहित करता है और वास्तविक अनुरोध को POST बॉडी में स्थानांतरित करता है: एक query स्ट्रिंग जो उन फ़ील्डों को नामित करती है जिन्हें आप चाहते हैं, एक variables ऑब्जेक्ट जो तर्क प्रदान करता है, कभी-कभी एक operationName टैग जो पहचानता है कि यह कौन सा है। उस ट्रैफिक को पढ़ना बॉडी को पढ़ने का मतलब है, URL को नहीं, क्योंकि URL ने संकेत भेजना बंद कर दिया है।
यह गाइड Playwright को Scrapeless Scraping Browser से CDP के माध्यम से जोड़ता है, एक वास्तविक सार्वजनिक GraphQL प्लेग्राउंड को एक क्वेरी फायर करने के लिए ड्राइव करता है, और परिणामी POST को दो स्वतंत्र तरीकों से रोकता है — Playwright के अपने प्रतिक्रिया इवेंट्स, और रॉ CDP Network डोमेन उनके नीचे — उसके बाद उस ठीक उसी अनुरोध को एक साधारण HTTP ग्राहक के साथ बिना किसी ब्राउज़र्स के फिर से चलाने से पहले। नीचे दिए गए प्रत्येक आदेश जीवित लक्ष्य के खिलाफ चलाया गया।
एक एंडपॉइंट, हर ऑपरेशन
https://rickandmortyapi.graphcdn.app/ वह पता है जो Rick और Morty API का अपना GraphiQL प्लेग्राउंड वास्तव में कॉल करता है, एक स्तर पिछले मित्रवत rickandmortyapi.com/graphql उपनाम के दस्तावेज़ में विज्ञापित किया गया; यह उस उपनाम के लिए अनुरोधों और सीधे CDN पते के लिए अनुरोधों दोनों का उत्तर देता है, एक समान डेटा के साथ। वह एकल पता हर ऑपरेशन की सेवा करता है जिसे प्लेग्राउंड भेज सकता है: इंट्रोस्पेक्शन क्वेरी जो इसे अपने लोड पर अपने स्कीमा एक्सप्लोरर को भरने के लिए स्वचालित रूप से फायर करती है, और कोई भी क्वेरी जो आप टाइप करते हैं और स्वयं निष्पादित करते हैं। उस URL के खिलाफ लिखी गई नेटवर्क फ़िल्टर (page.route("**/graphcdn.app/**", ...), या एक CDP श्रोता जो केवल होस्टनेम पर कुंजीबद्ध है) दोनों को बिना किसी भेदभाव के पकड़ लेगी — बिल्कुल वही समस्या GraphQL की अपनी HTTP-सेवा देने की परंपरा डिज़ाइन द्वारा उत्पन्न करती है: एक URL, एक विधि, हर ऑपरेशन को अनुरोध के अंदर की सामग्री के द्वारा भेदित किया जाता है न कि इसे भेजने के स्थान द्वारा। वास्तव में महत्वपूर्ण एक क्वेरी को अलग करना POST बॉडी के operationName फ़ील्ड या query पाठ को पढ़ने का मतलब है, न कि जिस पते पर यह गया।
प्लेग्राउंड स्वयं दूसरे भाग के अंतर को स्पष्ट करता है: एक स्क्रॉल-प्रेरित REST एंडपॉइंट के विपरीत जो एक पृष्ठ लोड होने पर या एक उपयोगकर्ता के स्क्रॉल करते ही फायर होता है, GraphiQL का क्वेरी संपादक खाली शुरू होता है। जब तक आप एक क्वेरी टाइप नहीं करते और निष्पादित नहीं करते तब तक कुछ भी महत्वपूर्ण नहीं होता — यहाँ तकनीक को उस अंतःक्रिया को चलाना होता है, न कि केवल इसके लिए इंतजार करना होता है।
पूर्वापेक्षाएँ
आपको Python 3.9 या नया चाहिए — playwright 1.59.0 Requires-Python >=3.9 को PyPI पर घोषित करता है — playwright पैकेज, और app.scrapeless.com पर मुफ्त योजना से एक Scrapeless API कुंजी। लक्षित GraphQL एंडपॉइंट को अपनी कोई कुंजी या खाता की आवश्यकता नहीं है; यह सार्वजनिक, गैर-प्रमाणीकृत डेटा है। Scrapeless कुंजी को आपके स्क्रिप्ट में एक शाब्दिक के बजाय एक पर्यावरण चर में रखें, चूंकि यह Scraping Browser के CDP एंडपॉइंट पर token क्वेरी पैरामीटर के रूप में यात्रा करता है।
स्थापित करें
bash
pip install playwright
bash
export SCRAPELESS_API_KEY="your_scrapeless_api_key"
CDP के माध्यम से कनेक्ट करें
हर Playwright-to-Scraping-Browser स्क्रिप्ट में यही URL-निर्माता पैटर्न पुन: उपयोग करें जो इस श्रृंखला में उपयोग करता है: एक WSS एंडपॉइंट पर तीन क्वेरी पैरामीटर।
python
import os
from urllib.parse import urlencode
API_KEY = os.environ["SCRAPELESS_API_KEY"]
def scraping_browser_url(proxy_country="US", session_ttl=120):
params = urlencode({
"token": API_KEY,
"sessionTTL": session_ttl,
"proxyCountry": proxy_country,
})
return f"wss://browser.scrapeless.com/api/v2/browser?{params}"
chromium.connect_over_cdp(scraping_browser_url()) एक मानक Playwright Browser ऑब्जेक्ट वापस करता है, कोई स्थानीय Chrome इंस्टॉल आवश्यक नहीं। नीचे दिए गए दो अवरोधन तकनीकों में से कुछ भी Scraping-Browser विशिष्ट नहीं है — वे किसी भी CDP-पहुंच योग्य Chromium के खिलाफ चलती हैं — लेकिन Scraping Browser के बुनियादी ढाँचे पर रेंडर करना एक GraphQL फ्रंटेंड का मतलब है जो अपने स्वयं के क्लाइंट के फ़िंगरप्रिंट बनाता है जो अभी भी सामान्य तरीके से हाइड्रेट और क्वेरी करता है।
क्वेरी को ट्रिगर करें और इसे एक प्रतिक्रिया श्रोता के साथ कैप्चर करें
page.expect_response() उस क्रिया से इंतज़ार को बांधता है जो इसे ट्रिगर करती है, इसलिए यह काम करता है चाहे वह क्रिया एक page.goto() हो या, यहाँ, एक UI इंटरैक्शन जिसे आप स्वयं चलाते हैं। GraphiQL के संपादक में एक वास्तविक क्वेरी और इसके वेरिएबल टाइप करें, expect_response संदर्भ के अंदर Execute पर क्लिक करें, और कैप्चर किया गया Response ऑब्जेक्ट बिल्कुल वही देता है जो साइट के अपने JavaScript ने भेजा और प्राप्त किया:
python
import json
import os
from urllib.parse import urlencode
from playwright.sync_api import sync_playwright
API_KEY = os.environ["SCRAPELESS_API_KEY"]
QUERY = (
"query GetCharacters($page: Int, $name: String) { "
"characters(page: $page, filter: { name: $name }) { "
"info { count pages } "
"results { id name status species } } }"
)
VARIABLES = '{"page": 1, "name": "rick"}'
def scraping_browser_url(proxy_country="US", session_ttl=120):
params = urlencode({
"token": API_KEY,
"sessionTTL": session_ttl,
"proxyCountry": proxy_country,
})
return f"wss://browser.scrapeless.com/api/v2/browser?{params}"
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(scraping_browser_url())
page = browser.new_page()
page.goto("https://rickandmortyapi.com/graphql", wait_until="domcontentloaded")
query_editor = page.locator(".graphiql-query-editor .CodeMirror").first
query_editor.click()
page.keyboard.press("Control+A")
page.keyboard.insert_text(QUERY)
page.locator("button:has-text('Variables')").first.click()
variables_editor = page.locator(".graphiql-editor-tool .CodeMirror").first
variables_editor.click()
page.keyboard.press("Control+A")
page.keyboard.insert_text(VARIABLES)
with page.expect_response(lambda r: "graphcdn.app" in r.url and r.request.method == "POST") as run:
page.locator("button.graphiql-execute-button").click()
resp = run.value
sent = json.loads(resp.request.post_data)
data = resp.json()["data"]["characters"]
print("POST target:", resp.request.url)
print("operationName:", sent["operationName"])
print("variables sent:", sent["variables"])
print("info:", data["info"])
print("first result:", data["results"][0])
print("result count in this page:", len(data["results"]))
browser.close()
इसे लाइव प्लेग्राउंड के खिलाफ चलाने से प्रिंट होता है:
text
POST target: https://rickandmortyapi.graphcdn.app/
operationName: GetCharacters
variables sent: {'page': 1, 'name': 'rick'}
info: {'count': 107, 'pages': 6}
first result: {'id': '1', 'name': 'Rick Sanchez', 'status': 'Alive', 'species': 'Human'}
result count in this page: 20
sent["variables"] उसी Python dict है जो संपादक के वेरिएबल पैनल ने धारण किया — {"page": 1, "name": "rick"} — यह पुष्टि करते हुए कि अवरोधन ने वास्तविक अनुरोध बॉडी को पढ़ा, न कि यह अनुमान कि क्वेरी में क्या हो सकता है। यहाँ page.keyboard.insert_text() के बजाय page.keyboard.type() महत्वपूर्ण है: CodeMirror, जो संपादक GraphiQL उपयोग करता है, जैसे ही आप उन्हें चरित्र दर चरित्र टाइप करते हैं, स्वतः ब्रैकेट बंद कर देता है, इसलिए { और } से भरी एक क्वेरी के लिए व्यक्तिगत कीस्ट्रोक्स का अनुकरण करने से बंद करने वाले ब्रैसेस की डुप्लीकेट्स और एक सिंटैक्स त्रुटि उत्पन्न होती है। insert_text() पूरे स्ट्रिंग को एक साथ जोड़ता है, जिस तरह एक पेस्ट करेगा, और पूरी तरह से प्रति-कीस्ट्रोक ऑटो-क्लोजिंग लॉजिक को छोड़ देता है।
कच्चे CDP नेटवर्क डोमेन में सही अनुरोध से मेल करें
Playwright की प्रतिक्रिया घटनाएँ Chrome DevTools Protocol Network domain पर स्थित हैं, जिसे सीधे CDPSession के माध्यम से पहुंचा जा सकता है उन मामलों में जहां आप बिल्कुल भी Playwright को संचालित नहीं कर रहे हैं — एक सामान्य CDP क्लायंट, या एक उपकरण जो केवल प्रोटोकॉल घटनाएँ प्रस्तुत करता है। क्योंकि केवल अंत बिंदु URL परिचालन को अलग नहीं करता है, CDP-स्तरीय फ़िल्टर को postData का निरीक्षण करना होगा जैसे कि उच्च-स्तरीय कैप्चर स्वाभाविक रूप से ट्रिगर करने वाले क्लिक से मेल खाता है:
python
import json
import os
from urllib.parse import urlencode
from playwright.sync_api import sync_playwright
API_KEY = os.environ["SCRAPELESS_API_KEY"]
QUERY = (
"query GetCharacters($page: Int, $name: String) { "
"characters(page: $page, filter: { name: $name }) { "
"info { count pages } "
"results { id name status species } } }"
)
VARIABLES = '{"page": 2, "name": "rick"}'
captured = {}
def scraping_browser_url(proxy_country="US", session_ttl=120):
params = urlencode({
"token": API_KEY,
"sessionTTL": session_ttl,
"proxyCountry": proxy_country,
})
return f"wss://browser.scrapeless.com/api/v2/browser?{params}"
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")
def on_request(event):
# The playground also fires a schema-introspection POST to this same
# URL on load. Matching on operationName in the body -- not the URL
# -- is what separates it from the query this script triggers.
request = event["request"]
if "graphcdn.app" in request["url"] and "GetCharacters" in request.get("postData", ""):
captured[event["requestId"]] = None
def on_finished(event):
request_id = event["requestId"]
if request_id in captured and captured[request_id] is None:
body = cdp.send("Network.getResponseBody", {"requestId": request_id})
captured[request_id] = json.loads(body["body"])
cdp.on("Network.requestWillBeSent", on_request)
cdp.on("Network.loadingFinished", on_finished)
page.goto("https://rickandmortyapi.com/graphql", wait_until="domcontentloaded")
query_editor = page.locator(".graphiql-query-editor .CodeMirror").first
query_editor.click()
page.keyboard.press("Control+A")
page.keyboard.insert_text(QUERY)
page.locator("button:has-text('Variables')").first.click()
variables_editor = page.locator(".graphiql-editor-tool .CodeMirror").first
variables_editor.click()
page.keyboard.press("Control+A")
page.keyboard.insert_text(VARIABLES)
page.locator("button.graphiql-execute-button").click()
for _ in range(30):
if captured and all(v is not None for v in captured.values()):
break
page.wait_for_timeout(300)
data = next(iter(captured.values()))["data"]["characters"]
print("requests matched by body content:", len(captured))
print("info:", data["info"])
print("first result:", data["results"][0])
browser.close()
text
requests matched by body content: 1
info: {'count': 107, 'pages': 6}
first result: {'id': '218', 'name': 'Mechanical Rick', 'status': 'unknown', 'species': 'Robot'}
Network.requestWillBeSent अपने साथ जाना अनुरोध के अपने postData फ़ील्ड पहले से ही जुड़ा हुआ है, इससे पहले कि प्रतिक्रिया मौजूद हो — यह तय करने के लिए प्राकृतिक बिंदु है कि क्या यह विशेष POST ट्रैक करने के लायक है। Network.loadingFinished पुष्टि करता है कि मेल खाती प्रतिक्रिया स्थानांतरण समाप्त हो गई है, और केवल तब getResponseBody बाइट्स लौटाती है। पृष्ठ 2 पृष्ठ 1 की तुलना में एक अलग पहला परिणाम लौटाता है, जो कि बिंदु है: कच्चा-CDP पथ और प्रतिक्रिया-श्रवण पथ एक ही तार पढ़ रहे हैं, दो अलग-अलग तरीकों से मिलकर, और दोनों एक ही सक्रिय प्रश्न से वास्तविक, विशिष्ट डेटा पर उतरते हैं।
आपको क्या वापस मिलता है
दोनों कैप्चर पथ इस प्रश्न के लिए समान Character आकार लौटाते हैं, क्योंकि दोनों समान मौलिक प्रतिक्रिया पढ़ते हैं।
| फ़ील्ड | प्रकार | अर्थ |
|---|---|---|
info.count |
पूर्णांक | प्रत्येक पृष्ठ पर फ़िल्टर से मेल खाने वाले कुल वर्ण |
info.pages |
पूर्णांक | वर्तमान पृष्ठ आकार पर कुल पृष्ठों की संख्या |
results[].id |
स्ट्रिंग | चरित्र ID, जिसका उपयोग सीधे एक फॉलो-अप character(id: ...) प्रश्न में किया जा सकता है |
results[].name |
स्ट्रिंग | चरित्र नाम |
results[].status |
स्ट्रिंग | "Alive", "Dead", या "unknown" |
results[].species |
स्ट्रिंग | प्रजाति वर्गीकरण |
filter: { name: $name } को filter: { status: "Alive" } के लिए स्वैप करें या फ़िल्टर तर्क को पूरी तरह से छोड़ दें, और वही दो कैप्चर स्क्रिप्ट बिना संशोधन के काम करती रहेंगी — केवल variables पेलोड और परिणामस्वरूप info.count बदलता है, क्योंकि तार-स्तरीय तकनीक इस पर निर्भर नहीं करती कि एक विशेष प्रश्न किस फ़ील्ड या तर्क का उपयोग करता है।
खुद का एक GraphQL अंत बिंदु के खिलाफ ऊपर दी गई दोनों कैप्चर स्क्रिप्ट चलाकर app.scrapeless.com पर साइन अप करके मुफ्त स्क्रेपिंग ब्राउज़र रनटाइम प्राप्त करें।
बिना ब्रोज़र के प्रश्न को फिर से चलाएं
ऊपर दोनों इंटरसेप्शन ने एक ही चीज साबित की: https://rickandmortyapi.graphcdn.app/ एक साधारण JSON POST को query, variables, और operationName के साथ स्वीकार करता है, कोई प्रमाणीकरण नहीं, और वही Character डेटा लौटाता है जिसे पहले कैप्चर ने दिखाया था। एक बार जब वह आकार ज्ञात हो जाता है, तो एक ब्रोज़र को समान प्रश्न पूछने की आवश्यकता नहीं होती है — हालांकि अनुरोध को सामान्य User-Agent घोषित करना होगा, या गेटवे का किनारा इसे बिना किसी पेलोड के सीधे अस्वीकार कर देता है; नीचे की सीमाएँ अनुभाग बताती हैं कि ऐसा क्यों है:
python
import json
import urllib.error
import urllib.request
ENDPOINT = "https://rickandmortyapi.graphcdn.app/"
QUERY = (
"query GetCharacters($page: Int, $name: String) { "
"characters(page: $page, filter: { name: $name }) { "
"info { count pages } "
"results { id name status species } } }"
)
payload = json.dumps({
"query": QUERY,
"variables": {"page": 1, "name": "rick"},
"operationName": "GetCharacters",
}).encode("utf-8")
req = urllib.request.Request(
ENDPOINT,
data=payload,
# A default urllib request declares "Python-urllib/x.y" as its User-Agent
# and the gateway's edge rejects that outright -- see "When the Browser
# Stays in the Loop" below for what's actually being checked.
headers={
"Content-Type": "application/json",
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
),
},
method="POST",
)
with urllib.request.urlopen(req, timeout=10) as resp:
if resp.status != 200:
raise urllib.error.HTTPError(ENDPOINT, resp.status, "unexpected status", resp.headers, None)
body = json.loads(resp.read())
data = body["data"]["characters"]
print("status: 200, no browser process involved")
print("info:", data["info"])
print("first result:", data["results"][0])
print("result count in this page:", len(data["results"]))
text
status: 200, no browser process involved
info: {'count': 107, 'pages': 6}
first result: {'id': '1', 'name': 'Rick Sanchez', 'status': 'Alive', 'species': 'Human'}
result count in this page: 20
समान info, समान पहला परिणाम, प्रतिक्रिया-श्रवण कैप्चर के समान 20-रो पृष्ठ — क्योंकि यह बिल्कुल वही अनुरोध है, जो urllib द्वारा भेजा गया है, GraphiQL के अपने फ़ेच कॉल के बजाय। इस कार्यप्रवाह में ब्रोज़र का पूरा काम अंत बिंदु, प्रश्न का आकार, और वेरिएबल फ़ॉर्मेट को प्रकट करना था; एक बार जब ये ज्ञात हो जाते हैं, तो GraphQL POST केवल HTTP पर JSON है जो एक अनुरोध दस्तावेज़ ले जाता है जो GraphQL विनिर्देशन स्वयं परिभाषित करता है, और समान प्रश्न फिर से पूछने का सबसे तेज़ तरीका आम तौर पर एक पृष्ठ को रेंडर करना बंद करना और इसे सीधे पूछना होता है।
जब ब्रोज़र लूप में रहता है
हर GraphQL अंत बिंदु इस सहयोगी नहीं होता है, GraphQL गेटवे आमतौर पर कैसे तैनात किए जाते हैं, इसके विशिष्ट कारणों के लिए। बहुत से एक Authorization हेडर की आवश्यकता करते हैं जिसमें एक टोकन होता है जो फ्रंटेंड का अपना जावास्क्रिप्ट स्थानीय संग्रहण या कुकी से जोड़ता है, कुछ ऐसा जो आप फिर से निर्माण नहीं कर सकते जब तक कि आपने इसे वास्तविक सत्र से कैप्चर नहीं किया — वही सीमा REST-आकार के छुपे हुए APIs को साझा करती है। कुछ और आगे बढ़ते हैं और स्वचालित स्थायी प्रश्न को लागू करते हैं, जहां क्लाइंट प्रश्न के पाठ के बजाय प्रश्न का SHA-256 हैश भेजता है; एक सर्वर जो केवल पूर्व-रजिस्टर्ड हैश को स्वीकार करता है, केवल प्रश्न स्ट्रिंग से निर्मित दोबारा चलाए गए अनुरोध को अस्वीकार करता है, क्योंकि हैश उस क्लाइंट से कभी भी पंजीकृत नहीं होता। दोनों मामलों में, इंटरसेप्शन कदम यहां दिखाए गए रूप में बिल्कुल काम करता है: page.expect_response() और CDP Network डोमेन जो कुछ भी ब्रोज़र वास्तव में भेजता है, पढ़ते हैं, प्रमाणीकरण हेडर या स्थायी-परीक्षण हैश सहित। केवल प्रत्यक्ष-पुनःप्रयोजित लाभ रुकता है, क्योंकि यह फिर से निर्माण करना कि ब्रोज़र ने क्या जोड़ा, कठिन भाग बन जाता है।
एक अधिक सूक्ष्म सीमा इस लेख की जांच के दौरान सामने आई है और इसे सीधे नामित करना महत्वपूर्ण है: एक सार्वजनिक, बिना प्रमाणीकरण वाला GraphQL एंडपॉइंट अभी भी फिंगरप्रिंट आधारित बॉट रोकथाम के पीछे हो सकता है जिसका क्वेरी से कोई लेना-देना नहीं है। ऊपर दिए गए एंडपॉइंट पर एक साधारण urllib POST, जिसे कोई User-Agent हेडर (Python का अपना डिफ़ॉल्ट, सीधे Python-urllib/3.12 स्ट्रिंग) के बिना भेजा गया था, ने HTTP 403 लौटाया Cloudflare त्रुटि 1010: "इस वेबसाइट के मालिक ने आपके ब्राउज़र के हस्ताक्षर के आधार पर आपके एक्सेस पर प्रतिबंध लगा दिया है।" यह हर बार हुआ, दोहराया जाने योग्य था, भले ही गेटवे के अपने क्वेरी-लागत दर-सीमा हेडर ने शेष बजट की रिपोर्ट की। एक साधारण ब्राउज़र User-Agent स्ट्रिंग जोड़ने से, और अनुरोध के बारे में और कुछ भी नहीं, ने प्रत्येक बाद वाले कॉल पर वही चेक पास किया। यह ब्लॉक ग्राहक की घोषित पहचान पर आधारित था, न कि अनुरोध की सामग्री या कितनी बार यह आया था। एक क्लाउड ब्राउज़र सत्र जो एक वास्तविक क्रोमियम हस्ताक्षर प्रस्तुत करता है, जिस तरह का Scraping Browser का CDP एंडपॉइंट प्रदान करता है, कभी भी पहले स्थान पर उस असमानता को नहीं ले जाता।
निष्कर्ष
एक GraphQL API REST के अनेक आत्म-वर्णन करने वाले URL को एक एंडपॉइंट और एक अनुरोध बॉडी के लिए व्यापार करता है जिसे यह जानने के लिए पढ़ा जाना आवश्यक है कि यह क्या मांग रहा है। page.expect_response() और कच्चा CDP Network डोमेन दोनों उस बॉडी को पढ़ते हैं, सामग्री के बजाय पते से मिलाते हैं, और एक साधारण HTTP क्लाइंट वही JSON पुनःप्रक्षिप्त करता है जब आकार की पुष्टि हो जाती है। क्वेरी फ़िल्टर को operationName या क्वेरी टेक्स्ट पर की किया जाना चाहिए न कि URL पर, एक खाली क्वेरी संपादक को दिलचस्प कुछ शुरू होने से पहले वास्तविक टाइप किए गए इनपुट की आवश्यकता होती है, और एक सार्वजनिक एंडपॉइंट की बॉट-मिटिगेशन परत को इसके प्रमाणीकरण से अलग चिंता के रूप में देखा जाना चाहिए। CDP यांत्रिकों के लिए दोनों कैप्चर पथ निर्माण करते हैं, Chrome DevTools Protocol व्याख्याता प्रोटोकॉल को उस नेटवर्क डोमेन के अलावा क्या उजागर करता है, के माध्यम से चलाता है।
app.scrapeless.com पर मुफ्त Scraping Browser रनटाइम के लिए साइन अप करें, या Scraping Browser उत्पाद पृष्ठ और मूल्य निर्धारण के विभिन्न रन के लिए देखें।
हमारे समुदाय में शामिल हों ताकि अन्य डेवलपर्स के साथ ब्राउज़र स्वचालन के निर्माण के लिए नोट्स तुलना कर सकें: Discord · Telegram।
अक्सर पूछे जाने वाले प्रश्न
प्रश्न: वेब स्क्रैपिंग में GraphQL इंटरसेप्शन क्या है?
यह उस एकल POST अनुरोध को पढ़ना है जो GraphQL-संबंधित पृष्ठ का अपना जावास्क्रिप्ट डेटा लाने के लिए भेजता है — उस अनुरोध बॉडी में query और variables — HTML में निर्माण के लिए प्रतिक्रिया का इंतजार करने के बजाय और मार्कअप को फिर से बाहर निकालने के लिए।
प्रश्न: आप केवल अनुरोध URL से यह क्यों नहीं बता सकते कि कौन सा GraphQL ऑपरेशन चला?
क्योंकि एक GraphQL गेटवे आमतौर पर एक निश्चित एंडपॉइंट से हर ऑपरेशन को सेवा देता है। REST API की तुलना में, जहां विभिन्न पथ विभिन्न संसाधनों के लिए होते हैं, एक GraphQL अनुरोध की पहचान इसके POST बॉडी में रहती है — operationName फ़ील्ड या query टेक्स्ट में — न कि उस पते में जहां इसे भेजा गया था।
प्रश्न: क्या आपको एक ब्राउज़र की आवश्यकता है जब आप क्वेरी, वेरिएबल और एंडपॉइंट को जानते हैं?
केवल यदि एंडपॉइंट को कुछ चाहिए जो ब्राउज़र प्रदान करता है, जैसे एक प्राधिकरण हेडर या एक पंजीकृत पर्सिस्टेड-क्वेरी हैश। एक सार्वजनिक एंडपॉइंट जो बिना प्रमाणीकरण के एक पूर्ण क्वेरी स्ट्रिंग स्वीकार करता है, जैसे कि इस मार्गदर्शिका में, एक साधारण HTTP क्लाइंट के साथ दोबारा भेजा जा सकता है, जैसा कि डायरेक्ट-रीप्ले उदाहरण दिखाता है।
प्रश्न: यहाँ page.expect_response() और कच्चा CDP Network डोमेन के बीच क्या अंतर है?
page.expect_response() Playwright का उच्च-स्तरीय रैपर है, जो अनुरोध को प्रेरित करने वाली क्रिया से बंधा है और एक पार्स किया हुआ Response ऑब्जेक्ट लौटाता है। CDP Network डोमेन उसके तहत का प्रोटोकॉल है — Network.requestWillBeSent, Network.loadingFinished, और Network.getResponseBody — जिसे Playwright बाइंडिंग के बिना उपयोगी होता है, या जब एक फ़िल्टर को प्रतिक्रिया के अस्तित्व से पहले आउटगोइंग अनुरोध बॉडी का निरीक्षण करने की आवश्यकता होती है।
प्रश्न: क्या एक सार्वजनिक GraphQL प्लेग्राउंड के अपने क्वेरी का इंटरसेप्ट करना कानूनी है?
पढ़ने की प्रतिक्रियाएं आपके अपने ब्राउज़र सत्र द्वारा पहले से प्राप्त की गई सार्वजनिक पृष्ठ पर जाने के दौरान उस डेटा तक पहुँचने की तुलना में विभिन्न विचार रखते हैं जो प्रमाणित या गैर-सार्वजनिक हैं। किसी भी कार्यप्रवाह को सार्वजनिक पृष्ठों तक सीमित करें, लक्षित के सेवा की शर्तों और रोबोट्स निदेशों का सम्मान करें, और अनुरोध की मात्रा सीमित रखें - इंटरसेप्शन ट्रैफिक को सही ढंग से पढ़ने का एक तरीका है, न कि एक्सेस नियमों की अनदेखी करने की अनुमति।
प्रश्न: प्रमाणित या केवल स्थायी-प्रश्न वाली GraphQL API के साथ क्या होता है?
इंटरसेप्शन चरण अभी भी काम करता है - दोनों कैप्चर पथ जो ब्राउज़र ने वास्तव में भेजा है, उसे पढ़ते हैं, जिसमें प्राधिकरण हेडर या स्थायी-प्रश्न हैश शामिल है। डायरेक्ट-रीप्ले चरण ही टूटता है, क्योंकि एक हैश-केवल सर्वर एक कच्चे प्रश्न स्ट्रिंग से निर्मित अनुरोध को अस्वीकार करता है जो कभी पंजीकृत नहीं हुआ था, और एक प्रमाणित एंडपॉइंट उस अनुरोध को अस्वीकार करता है जिसमें वह हेडर गायब होता है जिसे मूल सत्र ले गया था।
प्रश्न: यदि यह एक ब्राउज़र नहीं है तो डायरेक्ट-रीप्ले उदाहरण User-Agent हेडर क्यों सेट करता है?
क्योंकि गेटवे के एज बिना एक के अनुरोध को अस्वीकार करता है। एक साधारण urllib POST जो Python के डिफ़ॉल्ट User-Agent स्ट्रिंग का उपयोग करता है, प्रत्येक प्रयास पर Cloudflare त्रुटि 1010 लौटाता है, भले ही प्रश्न-लागत दर-सीमा हेडर बजट शेष दिखाते हैं - अवरोध क्लाइंट की घोषित पहचान पर आधारित होता है, न कि प्रश्न या कितनी बार इसे भेजा गया। एक साधारण ब्राउज़र User-Agent स्ट्रिंग, जिसमें अनुरोध के बारे में कुछ और नहीं बदला गया है, पारित करने के लिए पर्याप्त है।
प्रश्न: क्या यह तकनीक विशेष रूप से Scrapeless Scraping Browser की आवश्यकता है, या क्या यह किसी भी CDP-पहुंच योग्य Chromium के साथ काम करती है?
इंटरसेप्शन मैकेनिक्स सामान्य CDP व्यवहार हैं और किसी भी Chromium के खिलाफ काम करते हैं जो connect_over_cdp के माध्यम से पहुंचा जा सकता है, स्थानीय या दूरस्थ। उन्हें Scrapeless Scraping Browser पर चलाने से एक वास्तविक ब्राउज़र हस्ताक्षर के साथ एक क्लाउड Chromium सत्र जोड़ता है, जो एक फ्रंटेंड के लिए महत्वपूर्ण होता है जो अपने क्लाइंट को पहचानने से पहले एक प्रश्न को शुरू करने की अनुमति देता है।
प्रश्न: यदि लक्षित अपनी स्कीमा या प्रश्न आकृति बदलता है तो क्या होता है?
इंटरसेप्शन कोड काम करता रहता है जब तक एंडपॉइंट URL अभी भी मेल खाता है - यह वह कोई भी बॉडी पढ़ता है जो ब्राउज़र भेजता है, चाहे प्रश्न के क्षेत्र कुछ भी हों। एक नामित क्षेत्र या पुनर्गठित प्रकार data["characters"]["results"] पढ़ने वाले कोड को तोड़ देता है, उसी तरह जैसे एक CSS चयनकर्ता एक वर्ग नाम बदलने पर टूट जाता है; एक GraphQL स्कीमा सामान्यतः मार्कअप की तुलना में अधिक स्थिर होती है, लेकिन यह एक बाधित परिवर्तन के प्रति प्रतिरक्षित नहीं होती है।
स्क्रैपलेस में, हम केवल सार्वजनिक रूप से उपलब्ध डेटा का उपयोग करते हैं, जबकि लागू कानूनों, विनियमों और वेबसाइट गोपनीयता नीतियों का सख्ती से अनुपालन करते हैं। इस ब्लॉग में सामग्री केवल प्रदर्शन उद्देश्यों के लिए है और इसमें कोई अवैध या उल्लंघन करने वाली गतिविधियों को शामिल नहीं किया गया है। हम इस ब्लॉग या तृतीय-पक्ष लिंक से जानकारी के उपयोग के लिए सभी देयता को कोई गारंटी नहीं देते हैं और सभी देयता का खुलासा करते हैं। किसी भी स्क्रैपिंग गतिविधियों में संलग्न होने से पहले, अपने कानूनी सलाहकार से परामर्श करें और लक्ष्य वेबसाइट की सेवा की शर्तों की समीक्षा करें या आवश्यक अनुमतियाँ प्राप्त करें।



