Patchright में Stealth तोड़े बिना कैप्चा कैसे हल करें

patchright captcha - How to Solve CAPTCHA in Patchright Without Breaking Stealth

Patchright में कैप्चा वाला flow बाक़ी हर जगह जैसी वही तीन चालें हैं: sitekey पढ़ें, उसे किसी सॉल्वर को भेजें, token को वापस पेज में डाल दें। तीसरी चाल पर ही यह library लोगों को चौंकाती है। Patchright डिफ़ॉल्ट रूप से आपका JavaScript एक isolated context में चलाता है, और कोई isolated context पेज का DOM तो साझा करता है, पर पेज का JavaScript global नहीं। इसलिए token की लिखाई पहुँच जाती है, textarea वाक़ई वह मान रखता है, और साइट का अपना callback कभी चलता ही नहीं, क्योंकि जिस scope में आपका code चल रहा है उसमें वह मौजूद ही नहीं है। एक अतिरिक्त argument इसे ठीक कर देता है।

आपको क्या चाहिए

  • Python 3.10 या उससे नया, या Node.js अगर आप JavaScript package पसंद करते हैं। Patchright दोनों runtimes के लिए उपलब्ध है और दोनों में ही Playwright का drop-in replacement है।
  • Patchright के अपने installer से डाउनलोड किया गया एक Chromium browser। Firefox और WebKit patched नहीं हैं और समर्थित नहीं हैं।
  • सुरक्षित form का पेज URL। sitekey runtime पर पढ़ी जाती है।
  • CapSkip, Local mode में अगर script और सॉल्वर एक ही मशीन साझा करते हैं, या Server mode में अगर नहीं करते। दोनों की जानकारी यहाँ है: कनेक्शन सेटिंग्स.
# pip install patchright
pip install -U capskip patchright

# Pulls the browser. Real Chrome is the recommended channel,
# and the maintainers say so explicitly.
patchright install chrome

Patchright असल में क्या बदलता है

Patchright वही Playwright है जिसमें से ऑटोमेशन के ज़ाहिर निशान हटा दिए गए हैं। सबसे बड़ा patch यह है कि यह कभी Runtime.enable नहीं बुलाता, जो किसी सादे Playwright session का सबसे तेज़ आवाज़ वाला अकेला संकेत है, और यह वहाँ तक इसलिए पहुँचता है क्योंकि आपकी scripts को इसके बजाय isolated ExecutionContexts में चलाता है। यह Console API को भी पूरी तरह बंद कर देता है, वह flag जोड़ता है जो navigator.webdriver को छिपाता है, और Playwright के कई ऐसे defaults हटा देता है जो किसी session पर ऑटोमेटेड होने का ठप्पा लगाते हैं: ख़ुद automation flag, popup blocker override, component update block, और वे switches जो default apps तथा extensions को बंद कर देते थे।

इनमें से दो का कैप्चा वाले काम पर सीधा असर पड़ता है, और दोनों पर ठोकर खाना आसान है।

console बंद होने का मतलब है कि पेज के अंदर से आप जो कुछ भी log करते हैं वह आप तक नहीं पहुँचता। Patchright में console की कार्यक्षमता बिल्कुल काम नहीं करती, इसलिए किसी evaluate call में एक log line डालकर उसे driver से पढ़ने की आम आदत यहाँ मर चुकी है। इसके बजाय call से एक मान लौटाइए। वैसे भी यह बेहतर तरीक़ा है और आपके पास यही एकमात्र विकल्प है।

extensions का फिर से चालू होना ज़्यादा दोस्ताना बात है। Playwright आम तौर पर extensions बंद करके launch होता है, और Patchright वह switch हटा देता है, इसलिए किसी persistent profile में लोड किया गया browser extension वाक़ई चलता है। अगर आप यह सारा code लिखना ही नहीं चाहते, तो CapSkip browser extension पेज में widget को संभाल लेता है और आप form को ऐसे चलाते हैं मानो किसी इंसान ने उसे पास कर दिया हो।

जानने लायक़ एक और क्षमता: Patchright साधारण locators और XPaths से बंद shadow roots के अंदर तक पहुँच जाता है। जो widgets अपना markup किसी closed root के पीछे छिपाते हैं, उन्हें बिना किसी ख़ास इंतज़ाम के address किया जा सकता है।

चरण 1: इसे उसी तरह launch करें जैसा maintainers सुझाते हैं

Patchright की stealth जितनी patches पर टिकी है उतनी ही launch configuration पर भी। दस्तावेज़ों में बताया गया setup है असली Chrome channel पर एक persistent context, एक दिखने वाली window और कोई viewport override नहीं, और कोई custom user agent या headers बिल्कुल नहीं। ये आख़िरी दो बातें मायने रखती हैं: हाथ से सेट किया गया user agent बाक़ी fingerprint से टकराता है और सारी मेहनत बेकार कर देता है।

# pip install patchright
from patchright.sync_api import sync_playwright

with sync_playwright() as p:
    context = p.chromium.launch_persistent_context(
        user_data_dir="C:\\profiles\\scraper",
        channel="chrome",
        headless=False,
        no_viewport=True,
        # Do not set user_agent or extra headers here.
    )
    page = context.new_page()
    page.goto("https://example.com/page-with-recaptcha")

दिखने वाली window पर ध्यान दें। detection का ज़्यादातर बजट headless पर ही ख़र्च होता है, और सुझाया गया configuration उसे इस्तेमाल नहीं करता। Windows पर इसका मतलब है कि script चलाने वाले account को एक interactive desktop session चाहिए, और इसे किसी server पर डालने से पहले इसकी योजना बना लेना ठीक रहता है।

चरण 2: पेज से sitekey पढ़ें

sitekey host document पर होती है, widget के iframe के अंदर नहीं, और एक साधारण locator उसे पढ़ लेता है। Locators किसी execution context से गुज़रने के बजाय browser protocol पर सफ़र करते हैं, इसलिए isolated world की कोई बात इस चरण पर असर नहीं डालती।

# Locators auto-wait, so this doubles as a wait condition
# for a widget that renders late.
holder = page.locator("div.g-recaptcha")
holder.wait_for(state="attached", timeout=15000)

sitekey = holder.get_attribute("data-sitekey")
print(sitekey)   # 6Lxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

कुछ साइटें key को host page पर रखती ही नहीं और उसे सिर्फ़ widget के iframe URL में भेजती हैं। ऐसे में उसे query string से पढ़ लें, और कोई solve ख़र्च करने से पहले उसे जाँच लें, क्योंकि कोई ख़ाली मान सॉल्वर तक पूरा सफ़र तय करता है और वापस इस रूप में लौटता है: ERROR_GOOGLEKEY, उस read से बहुत दूर जो असल में नाकाम हुआ था।

# Fallback: the k= parameter on the anchor iframe.
from urllib.parse import urlparse, parse_qs

src = page.locator("iframe[src*='recaptcha/api2/anchor']").get_attribute("src")
sitekey = parse_qs(urlparse(src).query)["k"][0]

चरण 3: उसे अपनी ही मशीन पर हल करें

Python SDK, CapSkip से port 8080 पर बात करता है और token को एक सादे string के रूप में लौटाता है। एक ही method reCAPTCHA v2, Invisible, Enterprise और v3 को कवर करता है, और variants अलग calls के बजाय options के रूप में भेजे जाते हैं।

# pip install capskip
from capskip import CapSkip

solver = CapSkip(host="127.0.0.1", port=8080)

# Invisible v2 takes invisible=1, Enterprise takes enterprise=1,
# and v3 takes version="v3" with an action.
result = solver.recaptcha(sitekey=sitekey, url=PAGE_URL)

token = result["code"]   # the g-recaptcha-response value

Turnstile और GeeTest के अपने methods हैं और उनका ढाँचा भी वैसा ही है। Turnstile वह user agent भी लौटाता है जिसके साथ solve किया गया था, और कोई challenge पेज उस token को तब तक ठुकरा देता है जब तक वही user agent उसके साथ वापस न भेजा जाए। उसे सिर्फ़ form वाली request पर भेजें। उसे browser launcher को न सौंपें, क्योंकि हाथ से सेट किया गया user agent ठीक वही चीज़ है जिससे बचने को stealth configuration कहता है। पूरी parameter सूचियाँ यहाँ हैं: CapSkip API डॉक्यूमेंटेशन.

चरण 4: token को वहाँ रखें जहाँ पेज उसे इस्तेमाल कर सके

अब वह हिस्सा जो इसी library के लिए ख़ास है। response textarea display:none से छिपा होता है, इसलिए उसमें कुछ type नहीं किया जा सकता और मान JavaScript से ही assign करना पड़ता है। वह assignment isolated context से भी काम करता है, क्योंकि DOM साझा है। string को f-string के बजाय json.dumps से बनाएँ, क्योंकि कोई JSON string literal एक वैध JavaScript string literal भी होता है, quoting और escaping समेत।

# A DOM write is fine from the isolated context.
import json

page.evaluate(
    "document.getElementById('g-recaptcha-response').value = "
    + json.dumps(token)
)

page.click("button[type=submit]")

इससे वे साइटें कवर हो जाती हैं जिनका form submit पर textarea पढ़ता है। बहुत सी साइटें ऐसा नहीं करतीं। वे widget के साथ एक success callback register करती हैं और textarea की तरफ़ देखती तक नहीं, इसलिए token को पेज के तय किए हुए किसी function को सौंपना पड़ता है। Patchright के isolated context का अपना JavaScript global होता है, यानी पेज के functions और reCAPTCHA client configuration उसमें मौजूद ही नहीं होते। वह call एक reference error के साथ नाकाम होती है और कितनी भी बार दोहराने से यह नहीं बदलता।

Patchright का जवाब एक अतिरिक्त argument है। evaluate, evaluate_handle और evaluate_all, ये तीनों methods isolated_context लेते हैं, इसका डिफ़ॉल्ट True है, और इसे False करने पर script पेज के अपने main world में चलती है।

# The main world is where the page's own globals live.
page.evaluate(
    "token => window.onRecaptchaSuccess(token)",
    token,
    isolated_context=False,
)

callback का नाम अंदाज़े से लगाने के बजाय पेज के markup से पढ़ें। main world सिर्फ़ injection के लिए इस्तेमाल करें और किसी और काम के लिए नहीं: वहाँ चल रहा code साइट को दिखता है, और इसी वजह से isolated context डिफ़ॉल्ट है। submit करने की दोनों शैलियाँ भीतर से सामान्य reCAPTCHA v2 ही हैं, और दोनों के बारे में यहाँ लिखा गया है: reCAPTCHA v2 सॉल्वर पेज.

पूरा चलने वाला उदाहरण

ऊपर की हर चीज़ एक ही script में। सॉल्वर एक बार बनाया जाता है, solve ख़र्च करने से पहले sitekey जाँच ली जाती है, और token की पुष्टि पेज के अंदर log करने के बजाय एक लंबाई लौटाकर की जाती है।

# pip install capskip patchright
import json
from patchright.sync_api import sync_playwright
from capskip import CapSkip

PAGE_URL = "https://example.com/page-with-recaptcha"

solver = CapSkip(host="127.0.0.1", port=8080)

with sync_playwright() as p:
    context = p.chromium.launch_persistent_context(
        user_data_dir="C:\\profiles\\scraper",
        channel="chrome",
        headless=False,
        no_viewport=True,
    )
    page = context.new_page()
    page.goto(PAGE_URL)

    holder = page.locator("div.g-recaptcha")
    holder.wait_for(state="attached", timeout=15000)
    sitekey = holder.get_attribute("data-sitekey")
    if not sitekey:
        raise RuntimeError("Widget found but data-sitekey was empty.")

    token = solver.recaptcha(sitekey=sitekey, url=PAGE_URL)["code"]
    page.evaluate(
        "document.getElementById('g-recaptcha-response').value = "
        + json.dumps(token)
    )

    length = page.evaluate(
        "document.getElementById('g-recaptcha-response').value.length"
    )
    print(length)   # 0 means the injection did not land

    page.click("button[type=submit]")
    page.wait_for_load_state("networkidle")
    context.close()

सॉल्वर को किसी दूसरी मशीन पर चलाना

Patchright आम तौर पर उस मशीन से बड़ी किसी मशीन पर पहुँच जाता है जिस पर आपने script लिखी थी, और दिखने वाली window वाला सुझाया गया setup लोगों को काफ़ी जल्दी एक अलग VM की तरफ़ धकेल देता है। सॉल्वर को उसके साथ जाने की ज़रूरत नहीं।

CapSkip में दो connection modes हैं। Local, 127.0.0.1 से bind होता है और सिर्फ़ उसी डिवाइस को जवाब देता है, और जब आप उसी मशीन पर script लिख रहे हों जिस पर ऐप चलता है तो यही सही रहता है। Server, आपके network या public IP से bind होता है, जिससे कोई scraping VM, कोई दूसरा workstation या उनका पूरा pool उसी Windows मशीन को API के ज़रिए कॉल करता है। एक static public IP उस address को स्थिर रखता है। code में host के अलावा कुछ नहीं बदलता, और लागत के बारे में भी कुछ नहीं बदलता, क्योंकि यह अब भी आपका ही hardware है।

# Same SDK, same call. Only the host moves.
solver = CapSkip(host="10.0.0.12", port=8080, apiKey="YOUR_API_KEY")

जैसे ही सॉल्वर किसी network पते पर सुनने लगे, key validation चालू कर दें, और हर मशीन को उसकी अपनी key दें ताकि किसी एक को बाक़ी को छुए बिना रद्द किया जा सके। दोनों modes को यहाँ विस्तार से समझाया गया है: CapSkip सेटअप गाइड.

आम errors और उनका मतलब

आप जो देखते हैंकारणफिक्स
textarea में token है पर form fail हो जाता हैसाइट callback इस्तेमाल करती है और textarea कभी पढ़ती ही नहींcallback को isolated_context=False के साथ कॉल करें
पेज के किसी function का नाम लेता reference errorपेज के globals isolated context में मौजूद नहीं होतेवही उपाय: उस एक call को main world में चलाएँ
पेज में किए गए console log से कुछ नहीं आताPatchright, Console API को पूरी तरह बंद कर देता हैlog करने के बजाय evaluate से एक मान लौटाएँ
Firefox या WebKit सादे Playwright जैसा व्यवहार करता हैसिर्फ़ Chromium browsers patched हैंChromium या Chrome channel इस्तेमाल करें
patches के बावजूद ब्लॉककोई custom user agent या header fingerprint से टकराता हैउन्हें हटाएँ और Chrome पर persistent context इस्तेमाल करें
ERROR_GOOGLEKEYएक ख़ाली sitekey सॉल्वर तक पहुँच गईrecaptcha कॉल करने से पहले मान की पुष्टि करें
NetworkExceptionCapSkip चल नहीं रहा, या host ग़लत हैऐप शुरू करें, या host को सर्वर पते पर point करें
TimeoutExceptionsolve recaptchaTimeout से ज़्यादा लंबा चलाउसे 300 सेकंड के डिफ़ॉल्ट से ऊपर बढ़ाएँ

FAQ

क्या मैं अपनी Playwright script बिना बदले यहाँ ला सकता हूँ?

import बदल दीजिए और उसका ज़्यादातर हिस्सा चल जाता है। Locators, contexts, routes, waits और navigation सब पहले जैसा ही व्यवहार करते हैं। तीन चीज़ों पर दोबारा नज़र डालनी होगी: कोई भी ऐसी evaluate call जो पेज की तय की हुई किसी चीज़ को छूती है, जिसे अब वह अतिरिक्त argument चाहिए; ऐसी कोई भी चीज़ जो console output पर टिकी थी, जो अब है ही नहीं; और कोई भी Firefox या WebKit target, जो unpatched है। Playwright की व्यापक तस्वीर यहाँ है: Playwright कैप्चा सॉल्वर पेज.

क्या main world इस्तेमाल करने से मैं पकड़ा जाऊँगा?

सिद्धांत रूप में हाँ, क्योंकि पेज वहाँ चल रहे code को देख सकता है। व्यवहार में यह जोखिम माइक्रोसेकंड भर चलने वाली एक function call जितना ही है, और यह वैसा ही दिखता है जैसा किसी इंसान के challenge पास करने पर widget की अपनी script करती है। बाक़ी सब कुछ isolated context में रखें, injection को कई calls के बजाय एक ही call में करें, और यह सतह छोटी बनी रहती है।

क्या मुझे token inject करने के बजाय Turnstile widget पर क्लिक करना चाहिए?

कभी-कभी। managed mode में Turnstile का checkbox तब ख़ुद ही पास हो सकता है जब browser भरोसेमंद लगे, और Patchright इसी काम के लिए है, इसलिए पहले क्लिक आज़माना और फिर solve पर लौटना फ़ायदे का सौदा है। reCAPTCHA के checkbox पर क्लिक करने से सिर्फ़ एक image challenge खुलता है, इसलिए वहाँ कुछ हासिल नहीं होता। widget वाला पहलू यहाँ कवर किया गया है: Cloudflare Turnstile सॉल्वर पेज.

मेरा crawler Linux पर चलता है। CapSkip कहाँ रखूँ?

किसी ऐसी Windows मशीन पर जो आपके नियंत्रण में हो, और Server mode चालू करके। तब Linux मशीन उसे किसी भी दूसरी internal service की तरह API के ज़रिए कॉल करती है, इसलिए Patchright और सॉल्वर का एक ही operating system या एक ही network segment साझा करना ज़रूरी नहीं। host argument को उसी address पर लगाएँ, key validation चालू करें, और crawler को उसकी अपनी key दें।

संक्षेप में

Patchright इंस्टॉल करें, Chrome channel पर एक persistent context को दिखने वाली window और बिना किसी custom user agent के launch करें, sitekey को एक साधारण locator से पढ़ें, और उसे 127.0.0.1:8080 पर CapSkip से हल कराएँ। token को डिफ़ॉल्ट isolated context से सीधे textarea में लिख दें, और isolated_context=False तक तभी जाएँ जब साइट किसी callback को बुलाना चाहती हो। बाक़ी Python दुनिया के लिए, जिसमें Selenium और Playwright शामिल हैं, देखें: Python कैप्चा सॉल्वर पेज.

क्रॉल को बड़ा करने से पहले एक बात जान लेना ज़रूरी है। CapSkip एक असीमित कैप्चा सॉल्वर है जो पहले से आपके अपने hardware पर चलता है, इसलिए हज़ार पेज दोबारा आज़माने की लागत ठीक उतनी ही है जितनी दस को दोबारा आज़माने की।