Camoufox में Main World Eval से कैप्चा कैसे हल करें

camoufox captcha - How to Solve CAPTCHA in Camoufox Using Main World Eval

Camoufox में कैप्चा वाला step बाक़ी हर जगह जैसा ही दिखता है: sitekey पढ़ें, उसे किसी सॉल्वर को भेजें, फिर token को पेज में लिख दें। तीसरी चाल पर ही Camoufox अलग निकलता है। आप evaluate को जो JavaScript सौंपते हैं, वह उसे एक isolated scope में चलाता है जिसे पेज देख नहीं सकता, और कोई isolated scope पेज के DOM को बदल नहीं सकता। आपका write बिना किसी error के लौट आता है, textarea ख़ाली ही रहता है, और form validation में विफल हो जाता है। इसका इलाज एक launch विकल्प और दो अक्षरों का एक prefix है।

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

  • Python 3.10 या उससे नया। Camoufox 0.5 ख़ुद ही Playwright को pin करता है, इसलिए उसे pip को हल करने दें।
  • Camoufox browser, जो fetch कमांड से एक बार डाउनलोड किया गया हो। यह एक Firefox build है, Chromium नहीं।
  • सुरक्षित form का पेज URL। sitekey runtime पर पढ़ी जाती है।
  • CapSkip, Local mode में अगर script और सॉल्वर एक ही मशीन साझा करते हैं, या Server mode में अगर नहीं करते। दोनों की जानकारी यहाँ है: कनेक्शन सेटिंग्स.
# The geoip extra is optional and worth having if you use proxies.
pip install -U capskip "camoufox[geoip]"

# Downloads the browser itself. Run once per machine.
camoufox fetch

यहाँ आम token injection कुछ क्यों नहीं करता

Camoufox एक patched Firefox है जिसके ऊपर Playwright के इर्द-गिर्द एक पतला Python wrapper है। wrapper class एक Playwright context manager है, इसलिए उससे आपको वापस मिलता है एक साधारण Playwright browser object, और हर locator, क्लिक तथा navigation जो आप पहले से जानते हैं, वैसे ही काम करता है। यही अच्छी ख़बर है और यही इस library का ज़्यादातर हिस्सा है।

अपवाद है script चलाना। Camoufox सारा JavaScript एक isolated scope में चलाता है जो पेज को दिखाई नहीं देता, और उसके होने की पूरी वजह ही यही है: कोई साइट यह नहीं देख सकती कि ऑटोमेशन उसे कुरेद रहा है। पढ़ने पर इसका कोई असर नहीं होता, क्योंकि Playwright के अपने locator methods उस scope से गुज़रने के बजाय browser protocol के रास्ते चलते हैं। लिखने पर असर होता है, और याद रखने लायक वाक्य यही है: कोई isolated scope DOM को बदल नहीं सकता। वहाँ किया गया token assignment चुपचाप फेंक दिया जाता है।

Camoufox का जवाब एक main world escape hatch है। launch करते समय main_world_eval पास करें, फिर जिस भी script को असली पेज को छूना है उसके आगे mw: लगा दें, और वह पेज के अपने scope में चलेगी। इसके साथ दो बातें आती हैं। साइट वहाँ चल रहे code को पहचान सकती है, इसलिए इसे सिर्फ़ injection के लिए इस्तेमाल करें और किसी काम के लिए नहीं। और आप main world से element references बाहर नहीं लौटा सकते, सिर्फ़ वे मान लौटा सकते हैं जो JSON के रूप में बचे रहें।

चरण 1: main world चालू करके launch करें

यह विकल्प डिफ़ॉल्ट रूप से बंद रहता है और इसे launch के समय ही सेट करना पड़ता है। बाद में किसी एक call के लिए इसे चालू करने का कोई तरीक़ा नहीं है।

# pip install camoufox[geoip]
from camoufox.sync_api import Camoufox

with Camoufox(main_world_eval=True, headless=True) as browser:
    page = browser.new_page()
    page.goto("https://example.com/page-with-recaptcha")

    # browser is a normal Playwright Browser from here on.
    print(page.title())

आगे बढ़ने से पहले दो पड़ोसी विकल्प जान लेना ठीक रहेगा। humanize, cursor को इंसान जैसे रास्ते पर चलाता है और window पार करने में क़रीब 1.5 सेकंड तक ले लेता है, जो तब मायने रखता है जब आप token डालने के बजाय widget पर ख़ुद क्लिक करते हैं। और disable_coop, Cross-Origin-Opener-Policy को हटा देता है ताकि cross-origin iframes के भीतर के elements पर, जिनमें Cloudflare Turnstile का checkbox भी है, क्लिक हो ही सके।

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

sitekey host document पर होती है, widget के iframe के अंदर नहीं। Google का markup उसे एक container पर data-sitekey attribute के रूप में रखता है, और एक साधारण Playwright locator उसे पढ़ लेता है। इसके लिए किसी main world prefix की ज़रूरत नहीं, क्योंकि यह रास्ता कभी isolated scope से गुज़रता ही नहीं।

# Locators wait by default, 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

कुछ साइटें sitekey को host पेज पर कभी दिखाती ही नहीं और उसे सिर्फ़ widget के iframe URL में भेजती हैं। ऐसे में उसे query string से पढ़ लें।

# 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]

इस पर एक solve खर्च करने से पहले मान की जाँच कर लें। एक ख़ाली sitekey पूरे रास्ते सॉल्वर तक पहुँच जाती है और जवाब में मिलता है ERROR_GOOGLEKEY, यानी उस locator से बहुत दूर जो असल में विफल हुआ था।

चरण 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 भी लौटाता है जिसके साथ हल किया गया था, और कोई challenge पेज उस token को तब तक ठुकरा देगा जब तक आप वही user agent उसके साथ वापस न भेजें। पूरी parameter सूचियाँ यहाँ मिलती हैं: CapSkip API डॉक्यूमेंटेशन.

Camoufox एक async class भी देता है, और Python SDK का AsyncCapSkip synchronous वाले का कोई उपनाम नहीं, बल्कि सचमुच का asyncio implementation है। जब आप एक साथ कई contexts चलाएँ तो इन दोनों को जोड़ लें, ताकि एक पेज का हल बाक़ी पेजों को अटकाए नहीं।

चरण 4: token को main world में inject करें

यहीं वह हिस्सा है जो इसी browser के लिए ख़ास है। response textarea display:none से छिपा होता है, इसलिए कोई भी ऑटोमेशन टूल उसमें टाइप नहीं कर सकता और मान को JavaScript से ही assign करना पड़ता है। script के आगे mw: लगाएँ ताकि वह पेज के अपने scope में चले, और string को f-string के बजाय json.dumps से बनाएँ, क्योंकि JSON का string literal एक वैध JavaScript string literal भी होता है, quoting और escaping समेत।

# The mw: prefix is what makes this write land.
import json

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

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

यह जाँचने में एक पेच है कि token पहुँचा या नहीं। आप main world से कोई element बाहर नहीं सौंप सकते, इसलिए node के बजाय कोई मान लौटाएँ। एक length काफ़ी है और वह आपको ठीक वही बता देती है जो आप जानना चाहते हैं।

# Return a number, never the element itself.
length = page.evaluate(
    "mw:document.getElementById('g-recaptcha-response').value.length"
)
print(length)   # 0 means the injection did not land

अगर साइट form submit होने पर textarea पढ़ने के बजाय कोई success callback तय करती है, तो मान सेट करने के बाद उस callback को कॉल करें। यही वह हालत है जहाँ main world सिर्फ़ सुविधाजनक नहीं, बल्कि ज़रूरी है, क्योंकि पेज का तय किया हुआ कोई function isolated scope में मौजूद ही नहीं होता। function का नाम अंदाज़े से लेने के बजाय पेज के अपने markup से पढ़ें। दोनों में से कोई भी submission शैली नीचे से सामान्य reCAPTCHA v2 ही है, और दोनों को विस्तार से यहाँ लिखा गया है: reCAPTCHA v2 सॉल्वर पेज.

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

ऊपर की हर चीज़ एक ही script में। सॉल्वर एक बार बनाया जाता है, block ख़त्म होने पर browser ख़ुद बंद हो जाता है, और solve खर्च करने से पहले sitekey जाँच ली जाती है।

# pip install capskip camoufox[geoip]
import json
from camoufox.sync_api import Camoufox
from capskip import CapSkip

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

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

with Camoufox(main_world_eval=True, headless=True) as browser:
    page = browser.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.")

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

    page.click("button[type=submit]")
    page.wait_for_load_state("networkidle")
    print(page.url)   # where you land after submitting

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

Camoufox अक्सर उससे बड़ी मशीन पर जा पहुँचता है जिस पर आपने script लिखी थी, और ऐसे में platform की एक बारीकी सही रखनी होती है। headless को string virtual के रूप में पास करने पर एक Xvfb display शुरू होता है, जो सिर्फ़ Linux वाली सुविधा है: Windows और macOS पर वही मान not-supported error देता है। सादा headless हर जगह चलता है, इसलिए जब तक आप जानबूझकर Linux पर न हों, boolean ही इस्तेमाल करें।

सॉल्वर का browser के साथ सफ़र करना ज़रूरी नहीं। CapSkip में दो connection modes हैं। Local, 127.0.0.1 से bind होता है और सिर्फ़ उसी डिवाइस को जवाब देता है, और जब आप उसी मशीन पर script लिख रहे हों जिस पर ऐप चलता है तो यही सही सेटिंग है। Server, आपके network या public IP से bind होता है, जिससे कोई scraping VM, कोई दूसरा workstation या Camoufox चलाने वाला कोई Linux box उसी Windows मशीन को API के ज़रिए कॉल करता है। एक static public IP उस पते को स्थिर रखता है। कोड में 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 और उनका मतलब

आप जो देखते हैंकारणफिक्स
token सेट है फिर भी form विफल हो जाता हैwrite, isolated scope में चला और फेंक दिया गयाmain_world_eval के साथ launch करें और script के आगे mw: लगाएँ
evaluate किसी असमर्थित return को लेकर error उठाता हैmain world से एक node reference बाहर लौटाया गयाelement के बजाय कोई length या string लौटाएँ
callback function undefined हैपेज के globals isolated scope में होते ही नहींउसे उसी prefix के साथ main world से कॉल करें
Turnstile checkbox पर क्लिक नहीं हो पातावह एक cross-origin iframe में बैठा हैdisable_coop के साथ launch करें, या क्लिक करने के बजाय token inject करें
ERROR_GOOGLEKEYएक ख़ाली sitekey सॉल्वर तक पहुँच गईrecaptcha कॉल करने से पहले मान की पुष्टि करें
NetworkExceptionCapSkip चल नहीं रहा, या host ग़लत हैऐप शुरू करें, या host को सर्वर पते पर point करें
TimeoutExceptionsolve recaptchaTimeout से ज़्यादा लंबा चलाउसे 300 सेकंड के डिफ़ॉल्ट से ऊपर बढ़ाएँ
Virtual display समर्थित नहींLinux के बाहर headless को virtual पर सेट किया गयाइसके बजाय headless=True इस्तेमाल करें

FAQ

क्या main world eval मुझे पकड़ में आने लायक ज़्यादा आसान बना देता है?

main world में जो कुछ चलता है वह पेज को दिखाई देता है, इसलिए सैद्धांतिक रूप से हाँ। व्यवहार में यह exposure बस एक assignment जितना है जो कुछ माइक्रोसेकंड चलता है और बिल्कुल वैसा ही दिखता है जैसा कोई इंसान चुनौती पार करे तो widget की अपनी script करती है। बाक़ी हर script isolated scope में रखें, injection कई calls के बजाय एक ही call में करें, और आप ज़्यादा कुछ नहीं सौंप रहे होते।

क्या मैं अपना मौजूदा Playwright code दोबारा इस्तेमाल कर सकता हूँ?

लगभग पूरा। launcher एक असली Playwright browser लौटाता है, इसलिए locators, contexts, routes और waits वैसे ही बर्ताव करते हैं जैसे हमेशा करते थे। दो चीज़ें दोबारा देखनी होंगी: वह हर call जो evaluate के ज़रिए DOM में लिखती है, जिसे prefix चाहिए, और वह सब कुछ जो Chromium मानकर चल रहा था, क्योंकि यह Firefox है। उस engine की व्यापक तस्वीर यहाँ है: Playwright कैप्चा सॉल्वर पेज.

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

सिर्फ़ Turnstile के लिए, और वह भी कभी-कभी। managed mode वाला Turnstile checkbox ख़ुद ही पास हो सकता है बशर्ते browser भरोसेमंद लगे, और Camoufox इसी के लिए है, और उस पर क्लिक करने के लिए पहले Cross-Origin-Opener-Policy हटानी पड़ती है। reCAPTCHA के checkbox पर क्लिक करने से तो बस एक image challenge खुल जाता है, इसलिए वहाँ हासिल करने को कुछ नहीं। widget वाले पहलू का विवरण यहाँ है: Cloudflare Turnstile सॉल्वर पेज.

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

अपनी किसी Windows मशीन पर, Server mode चालू करके। इसके बाद VPS उसे API के ज़रिए ठीक वैसे ही कॉल करता है जैसे किसी भी internal service को करता, इसलिए Camoufox और सॉल्वर को न एक ही ऑपरेटिंग सिस्टम चाहिए और न एक ही network segment। host argument को उसी पते पर लगाएँ, key validation चालू करें, और VPS को उसकी अपनी key दें।

संक्षेप में

Camoufox को main_world_eval चालू करके launch करें, sitekey को एक साधारण locator से पढ़ें, उसे 127.0.0.1:8080 पर CapSkip से हल करें, फिर mw: लगे evaluate call के ज़रिए token inject करके submit कर दें। असली तरकीब यही prefix है, क्योंकि इसके बिना आपका write उस scope में जा गिरता है जिसे पेज कभी देखता ही नहीं। Selenium और Playwright समेत बाक़ी Python दुनिया के लिए देखें: Python कैप्चा सॉल्वर पेज.

crawl को बड़ा करने से पहले एक और बात जानने लायक है। CapSkip एक लोकल captcha सॉल्वर है जो आपके पास पहले से मौजूद hardware पर चलता है, इसलिए हज़ार पेज दोबारा आज़माने वाला run उतना ही खर्च कराता है जितना दस पेज दोबारा आज़माने वाला।