Async Python में CapSkip से Pydoll कैप्चा कैसे हल करें

pydoll captcha - How to Solve a Pydoll CAPTCHA With CapSkip in Async Python

Pydoll कैप्चा हल करने के लिए पेज से sitekey पढ़ें, उसी asyncio event loop पर CapSkip के AsyncCapSkip client से उसे हल करें, token को g-recaptcha-response वाले box में लिखें और पेज का callback चलाएँ या form submit करें। Pydoll 3.0 में reCAPTCHA का अपना कोई कोड नहीं है, इसलिए token किसी सॉल्वर से ही आना होगा। CapSkip आपके अपने Windows PC पर चलता है, इसलिए कई पेज खोलने वाले crawl पर प्रति-हल कोई शुल्क नहीं लगता। यह गाइड पूरा Pydoll कैप्चा flow करीब 80 लाइनों में बनाती है और उन गड़बड़ियों को भी बताती है जिनमें Windows पर समय जाता है।

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

  • किसी Windows PC पर चल रहा CapSkip, जिसमें reCAPTCHA सॉल्वर चालू हो। पहली बार शुरू करने के चरण सेटअप गाइड में समझाए गए हैं।
  • Python 3.10 या उसके बाद का वर्ज़न। Pydoll 3.0.0 और capskip 1.3.0 package, दोनों को यही चाहिए। नीचे सब कुछ Windows 11 पर Python 3.14.0 के साथ चलाया गया, और पूरी script Python 3.11.7 पर भी चली। Python 3.10, 3.12 और 3.13 पर नहीं चलाया गया।
  • Google Chrome, अपने default Program Files फ़ोल्डर में इंस्टॉल किया हुआ। गाइड में Chrome 155 इस्तेमाल हुआ, और पूरी script Chrome for Testing 146 पर भी चली।
  • एक PowerShell prompt। कमांड PowerShell syntax में लिखी गई हैं।
  • आपका अपना पेज, जिस पर reCAPTCHA v2 का checkbox हो। उदाहरण का form एक सादा sign-up form है। selectors को अपने पेज के हिसाब से बदलें।
  • सॉल्वर के लिए एक पता। Local mode में CapSkip केवल उसी डिवाइस के लिए 127.0.0.1 पर जवाब देता है। Server mode में वह आपके नेटवर्क पते या पब्लिक IP पर सुनता है, ताकि दूसरे PC, VPS या hosted platform पर चल रहा Pydoll API के ज़रिए उसे कॉल कर सके। दोनों यहाँ सेट किए जाते हैं: कनेक्शन सेटिंग्स.

नीचे के माप उसी PC पर चल रहे mock CapSkip और mock sign-up पेज से आए हैं, जिनके साथ असली Pydoll, असली SDK, असली Chrome और Google की असली api.js script इस्तेमाल हुई। browser और SDK का व्यवहार आपकी अपनी साइट पर भी यही रहेगा। हल का समय और पेज के accept तथा reject के नियम mock के अपने हैं। चल रहे CapSkip के सामने Google की public test sitekey और default settings के साथ एक हल में 9.0 सेकंड लगे और 1,764 अक्षरों का token मिला। हल का समय हर run में बदलता रहता है। उस token का कहीं इस्तेमाल नहीं हुआ, और किसी live साइट पर कोशिश नहीं की गई।

Pydoll कहाँ रुकता है और CapSkip कहाँ शुरू होता है

Pydoll, DevTools protocol के ज़रिए Chrome चलाता है, बीच में कोई WebDriver नहीं होता। वह आपको browser, पेज के elements और JavaScript चलाने का तरीक़ा देता है। 3.0.0 में उसका कैप्चा वाला इकलौता कोड Cloudflare Turnstile के checkbox के लिए एक helper है। वह helper checkbox ढूँढकर उस पर क्लिक करता है। वह कभी token नहीं पढ़ता, और कुछ न होने पर भी कोई error नहीं उठाता। reCAPTCHA को संभालने वाला कुछ भी नहीं है। reCAPTCHA v2 का checkbox ऐसे सॉल्वर से हल होता है जो token लौटाता है, और फिर पेज को वह token सही जगह चाहिए। यहाँ यही काम CapSkip करता है, और इस गाइड का बाकी हिस्सा जोड़ने वाला कोड है।

चरण 1: प्रोजेक्ट बनाएँ

खाली folder में virtual environment इस्तेमाल करें। कमांड environment का अपना Python उसके path से चलाते हैं, इसलिए उसे activate करने की ज़रूरत नहीं, और PowerShell की पाबंदी वाली script policy भी आड़े नहीं आती। नीचे की हर script environment के Python से चलाएँ, जैसे .\.venv\Scripts\python.exe solve.py। Pydoll का पुराना इंस्टॉल, जैसे 2.27.0, नीचे वाली script के पहले import पर ही ImportError: cannot import name ‘Chrome’ from ‘pydoll’ के साथ फेल हो जाएगा। Pydoll 3.0 Chrome को top level से export करता है।

# Windows PowerShell, in an empty folder
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install pydoll-python capskip

चरण 2: Chrome शुरू करें और पेज खोलें

इसे solve.py के नाम से सेव करें। यह सबसे छोटी Pydoll script है जो आपका पेज खोलती है और उसका title प्रिंट करती है। browser एक async with block के अंदर शुरू होता है, इसलिए block ख़त्म होते ही बंद भी हो जाता है।

# pip install pydoll-python capskip
import asyncio

from pydoll import Chrome
from pydoll.browser.options import ChromiumOptions

PAGE = 'https://example.com/page-with-recaptcha'


async def main():
    options = ChromiumOptions()
    options.headless = True  # set to False to watch the window
    async with Chrome(options=options) as browser:
        tab = await browser.start()
        await tab.go_to(PAGE)
        print(await tab.title())


asyncio.run(main())

Pydoll, Chrome को सिर्फ़ दो जगह ढूँढता है: Program Files और Program Files (x86), Google, Chrome, Application के नीचे। AppData के नीचे, किसी एक user के लिए इंस्टॉल किया गया Chrome नहीं मिलता, और script InvalidBrowserPath के साथ रुक जाती है। इसे ठीक करने के लिए options.binary_location को chrome.exe के पूरे path पर सेट करें। जो path मौजूद ही नहीं, वह इसके बजाय FailedToStartBrowser के साथ फेल होता है। यही एक लाइन Pydoll को Chrome for Testing की ओर भी मोड़ती है, और वह भी चला। Pydoll browser के वर्ज़न किसी चीज़ से मिलाकर नहीं जाँचता, इसलिए 146 और 155 दोनों चले।

Windows पर शुरू होने में आपकी उम्मीद से ज़्यादा समय लगता है। टेस्ट वाले PC पर default settings और headless Chrome 155 के साथ browser.start() में 2.8 सेकंड लगे। बाहर की script के बिना वाले पेज पर पहले go_to में 2.1 सेकंड और लगे, और sign-up पेज पर, जो Google की script लोड करता है, करीब 3.1 सेकंड। Pydoll localhost से जुड़ता है, और localhost पहले IPv6 पता आज़माता है। Chrome का debugging port IPv6 पर नहीं सुनता, और Windows को loopback connection मना करने में करीब 2 सेकंड लगते हैं, इसलिए Chrome से हर नया connection IPv4 के चलने से पहले करीब 2 सेकंड भरता है। इसका मतलब है browser पर और आपके खोले हर अतिरिक्त tab पर। Pydoll में इसके लिए कोई setting नहीं है। Python process के अंदर localhost को 127.0.0.1 पर resolve कराने से टेस्ट में यह देरी हट जाती है, लेकिन वह standard library को patch करता है, इसलिए यह गाइड उसे छोड़ देती है। run का समय नापते वक़्त इसका हिसाब रखें।

with block सामान्य exit पर, उसके अंदर किसी exception के बाद और cancel हुए task के बाद Chrome को बंद करता है, और temporary profile folder हटा देता है। Python process को taskkill /F से ख़त्म करने पर यह सब छूट जाता है। टेस्ट में इससे 13 chrome.exe processes चलते रह गए और profile folder डिस्क पर बचा रहा। console में Ctrl+C का टेस्ट नहीं किया गया। अगर आपने कोई run मारा था, तो अगला run शुरू करने से पहले Task Manager में chrome.exe processes देख लें।

चरण 3: जाँचें कि CapSkip जवाब देता है

browser जोड़ने से पहले पक्का कर लें कि सॉल्वर की call अकेले चलती है। इसे first_solve.py के नाम से सेव करें। AsyncCapSkip उसी package का async client है। SDK कोई environment variable नहीं पढ़ता, इसलिए host और port client में ही लिखे जाते हैं। ये placeholders सिर्फ़ जगह भरने के लिए हैं। YOUR_SITEKEY और पेज का पता ऐसे पेज से आने चाहिए जिस पर सचमुच reCAPTCHA v2 widget हो, वरना CapSkip error से जवाब देता है। sitekey को पेज के source से कॉपी करें, जहाँ वह data-sitekey का मान होती है। चरण 4 उसे आपके लिए पेज से पढ़ता है। फ़ाइल को .\.venv\Scripts\python.exe first_solve.py से चलाएँ।

# pip install capskip
import asyncio

from capskip import AsyncCapSkip


async def main():
    solver = AsyncCapSkip(host='127.0.0.1', port=8080)
    result = await solver.recaptcha(
        sitekey='YOUR_SITEKEY',
        url='https://example.com/page-with-recaptcha',
    )
    print(result['code'][:30])


asyncio.run(main())

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

sitekey पेज पर ही होती है, और Pydoll उसे पढ़ सकता है। सामान्य widget एक div होता है जिसकी class g-recaptcha है और जिस पर data-sitekey attribute होती है। जो widget पेज grecaptcha.render() से खींचता है, उसमें ऐसा div नहीं होता। वह एक iframe छोड़ता है, जिसके पते में sitekey k parameter में रहती है। यह function एक ही selector से दोनों को संभालता है, और tag का नाम बताता है कि कौन-सा मिला। इसे solve.py में async def main() के ऊपर जोड़ें।

from urllib.parse import parse_qs, urlparse


async def find_sitekey(tab):
    # A normal widget carries data-sitekey. A widget drawn by
    # grecaptcha.render() only leaves its iframe, with k=<sitekey>.
    found = await tab.query(
        '.g-recaptcha[data-sitekey], '
        'iframe[src*="recaptcha/api2/anchor"]',
        timeout=10,
    )
    if found.tag_name == 'iframe':
        src = found.get_attribute('src')
        return parse_qs(urlparse(src).query)['k'][0]
    return found.get_attribute('data-sitekey')

यहाँ Pydoll की दो बातें मायने रखती हैं। get_attribute एक सादा function है, coroutine नहीं, इसलिए उसे await न करें, और attribute न हो तो वह None लौटाता है। और query कुछ न मिलने पर ElementNotFound उठाता है। timeout देने पर वह इंतज़ार करता है और इसके बजाय WaitElementTimeout उठाता है, जो उस widget के लिए सही चुनाव है जो पेज लोड होने के कुछ पल बाद दिखता है। लोड के 3 सेकंड बाद डाला गया widget find_sitekey के 10 सेकंड के timeout के साथ करीब 3 सेकंड बाद मिल गया।

चरण 5: उसी event loop पर हल करें

Pydoll asyncio पर चलता है, और आपकी solve call उसी loop पर चलती है। इसीलिए client का चुनाव मायने रखता है। इसे भी async def main() के ऊपर जोड़ें, और चरण 6 के function के साथ भी यही करें।

from capskip import AsyncCapSkip

solver = AsyncCapSkip(host='127.0.0.1', port=8080)


async def solve(sitekey, url):
    result = await solver.recaptcha(sitekey, url)
    return result['code']

sync CapSkip client भी चलेगा, लेकिन वह पूरे हल के दौरान loop को block रखता है, और इंतज़ार के समय Pydoll, Chrome को जवाब देना बंद कर देता है। एक mock run ने गिना कि पेज से हर 50 मिलीसेकंड पर समय पूछने पर वह कितनी बार जवाब देता है, जबकि 3 सेकंड का mock हल चल रहा था। सबसे लंबा gap दो जवाबों के बीच का सबसे लंबा समय है:

क्लाइंटहल का समयकाउंटर टिकसबसे लंबा gap
async function के अंदर सीधे call किया गया sync CapSkip client3.76 s53.78 s
asyncio.to_thread में CapSkip3.76 s650.06 s
AsyncCapSkip5.0 s680.3 s

पहले मामले में पेज ख़ुद चलता रहा। सिर्फ़ Python की तरफ़ सब ठप रहा: हल ख़त्म होने तक Python पेज की किसी भी हरकत पर प्रतिक्रिया नहीं दे सकता, इसलिए दूसरे tab के events देर से संभाले जाते हैं। AsyncCapSkip यहाँ करीब एक सेकंड ज़्यादा लगा, पर यह इस पर निर्भर करता है कि हल के बाद उसका अगला poll कहाँ पड़ता है, और 2, 4 और 8 सेकंड के mock हलों में वह thread वाले संस्करण से पहले पूरा हुआ। उसकी कीमत हर request पर करीब 0.15 सेकंड का blocked loop है, क्योंकि वह हर बार नया HTTP client बनाता है। फिर भी यह जमे हुए loop से बेहतर है।

चरण 6: token को पेज में लिखें

हल किया गया reCAPTCHA एक token होता है। पेज उसे g-recaptcha-response नाम के textarea में चाहता है, और अगर widget में callback है तो पेज चाहता है कि callback उस token के साथ चले। यह function दोनों करता है। यह Pydoll के execute_script का इस्तेमाल करता है और callback मिलने और चलने पर True लौटाता है। इसे भी async def main() के ऊपर जोड़ें।

import json

FILL_JS = """
(function (token) {
  var seen = new Set(), fn = null;
  document.querySelectorAll('textarea[name="g-recaptcha-response"]')
    .forEach(function (box) { box.value = token; });
  (function walk(o, depth) {
    if (!o || typeof o !== 'object' || seen.has(o) || depth > 6) return;
    seen.add(o);
    for (var k in o) {
      var v; try { v = o[k]; } catch (e) { continue; }
      if (k === 'callback' && !fn) {
        fn = typeof v === 'string' ? window[v] : v;
      }
      if (v && typeof v === 'object' && !(v instanceof Node)) {
        walk(v, depth + 1);
      }
    }
  })(window.___grecaptcha_cfg, 0);
  if (typeof fn === 'function') fn(token);
  return typeof fn === 'function';
})(%s)
"""


async def fill_token(tab, token):
    reply = await tab.execute_script(
        FILL_JS % json.dumps(token), return_by_value=True)
    outcome = reply['result']
    if 'exceptionDetails' in outcome:
        error = outcome['exceptionDetails']['exception']
        raise RuntimeError(error['description'])
    return outcome['result']['value']  # True if a callback ran

JavaScript, Google के अपने configuration object में घूमकर callback ढूँढती है, क्योंकि उसके अंदर की keys छोटे नाम हैं जिन्हें Google बदल सकता है। data-callback attribute वहाँ किसी function के नाम के रूप में रखी जाती है, इसलिए यह खोज उस नाम को window पर देखती है। grecaptcha.render() को दिया गया callback वहाँ ख़ुद function के रूप में रखा जाता है। दोनों मामले Google की असली api.js पर चले।

Pydoll, execute_script से raw DevTools reply लौटाता है, इसलिए मान reply[‘result’][‘result’][‘value’] में रहता है, और call को await करना ज़रूरी है। return_by_value=True न दें तो object एक reference बनकर आता है। JavaScript error Python में raise नहीं होता। वह reply में exceptionDetails जोड़ देता है, इसीलिए function उसे जाँचता है और अपना RuntimeError उठाता है।

चरण 7: सब कुछ जोड़ें

solve.py के आख़िर से async def main() और asyncio.run(main()) वाली लाइन हटाएँ, उनके ऊपर के functions रखें, और उनकी जगह यह चिपकाएँ। यह पेज लोड होने के बाद हल करता है, क्योंकि sitekey पेज से आती है, और token इस्तेमाल होने से ठीक पहले हल करता है। अगर कोई callback चला, तो आगे पेज संभाल लेता है। नहीं तो script form का submit बटन दबाती है और navigation का इंतज़ार करती है।

async def main():
    options = ChromiumOptions()
    options.headless = True  # set to False to watch the window
    async with Chrome(options=options) as browser:
        tab = await browser.start()
        await tab.go_to(PAGE)
        sitekey = await find_sitekey(tab)
        token = await solve(sitekey, await tab.current_url())
        if not await fill_token(tab, token):
            button = await tab.query('form button[type="submit"]')
            async with tab.expect_navigation():
                await button.click()
        print(await tab.title())


asyncio.run(main())

जिस पेज पर callback भेजने का काम करता है, वहाँ क्लिक करने को कुछ नहीं होता। यह संस्करण main() को उसी तरह बदलता है। आगे क्या होता है यह पेज पर निर्भर है, इसलिए किसी ऐसी चीज़ का इंतज़ार करें जिसे पेज ख़ुद बदलता है। यह संस्करण टेस्ट पेज पर एक result element के waiting से कुछ और हो जाने का इंतज़ार करता है। selector को उस चीज़ में बदलें जिसे आपका पेज अपडेट करता है।

async def main():
    options = ChromiumOptions()
    options.headless = True  # set to False to watch the window
    async with Chrome(options=options) as browser:
        tab = await browser.start()
        await tab.go_to(PAGE)
        sitekey = await find_sitekey(tab)
        token = await solve(sitekey, await tab.current_url())
        if await fill_token(tab, token):
            # The page's own callback takes over. What it does next is
            # page specific: this page writes its answer into #result.
            await tab.wait_for_script(
                "var r = document.querySelector('#result');"
                "return r && r.textContent !== 'waiting';"
            )
        print(await (await tab.query('#result')).text())


asyncio.run(main())

पूरी script

यह रही solve.py एक ही टुकड़े में, सारे imports सबसे ऊपर। यह उन blocks की जगह लेती है जो आपने एक-एक करके जोड़े थे, इसलिए पहले उन्हें हटा दें। सादे form पेज पर चलाने पर इसने Welcome प्रिंट किया और पेज ने token को एक बार स्वीकार किया। जिस पेज में callback भेजने का काम करता है, वहाँ यह संस्करण पेज बदलने से पहले पुराना title प्रिंट कर सकता है, इसलिए वहाँ ऊपर वाला callback संस्करण इस्तेमाल करें।

# pip install pydoll-python capskip
import asyncio
import json
from urllib.parse import parse_qs, urlparse

from capskip import AsyncCapSkip
from pydoll import Chrome
from pydoll.browser.options import ChromiumOptions

PAGE = 'https://example.com/page-with-recaptcha'

solver = AsyncCapSkip(host='127.0.0.1', port=8080)

# Put the token in every response box, then run the page's callback.
FILL_JS = """
(function (token) {
  var seen = new Set(), fn = null;
  document.querySelectorAll('textarea[name="g-recaptcha-response"]')
    .forEach(function (box) { box.value = token; });
  (function walk(o, depth) {
    if (!o || typeof o !== 'object' || seen.has(o) || depth > 6) return;
    seen.add(o);
    for (var k in o) {
      var v; try { v = o[k]; } catch (e) { continue; }
      if (k === 'callback' && !fn) {
        fn = typeof v === 'string' ? window[v] : v;
      }
      if (v && typeof v === 'object' && !(v instanceof Node)) {
        walk(v, depth + 1);
      }
    }
  })(window.___grecaptcha_cfg, 0);
  if (typeof fn === 'function') fn(token);
  return typeof fn === 'function';
})(%s)
"""


async def find_sitekey(tab):
    # A normal widget carries data-sitekey. A widget drawn by
    # grecaptcha.render() only leaves its iframe, with k=<sitekey>.
    found = await tab.query(
        '.g-recaptcha[data-sitekey], '
        'iframe[src*="recaptcha/api2/anchor"]',
        timeout=10,
    )
    if found.tag_name == 'iframe':
        src = found.get_attribute('src')
        return parse_qs(urlparse(src).query)['k'][0]
    return found.get_attribute('data-sitekey')


async def solve(sitekey, url):
    result = await solver.recaptcha(sitekey, url)
    return result['code']


async def fill_token(tab, token):
    reply = await tab.execute_script(
        FILL_JS % json.dumps(token), return_by_value=True)
    outcome = reply['result']
    if 'exceptionDetails' in outcome:
        error = outcome['exceptionDetails']['exception']
        raise RuntimeError(error['description'])
    return outcome['result']['value']  # True if a callback ran


async def main():
    options = ChromiumOptions()
    options.headless = True  # set to False to watch the window
    async with Chrome(options=options) as browser:
        tab = await browser.start()
        await tab.go_to(PAGE)
        sitekey = await find_sitekey(tab)
        token = await solve(sitekey, await tab.current_url())
        if not await fill_token(tab, token):
            button = await tab.query('form button[type="submit"]')
            async with tab.expect_navigation():
                await button.click()
        print(await tab.title())


asyncio.run(main())

दो tabs एक साथ हल करें

दो tabs asyncio.gather से एक साथ हल कर सकते हैं। एक साथ शुरू हुए दो 3 सेकंड के हल 4 सेकंड से कम में साथ-साथ पूरे हुए, और token अलग-अलग थे। जाल cookies का है। default browser context के tabs एक ही cookie jar साझा करते हैं, इसलिए दूसरा पेज लोड वह session या CSRF cookie बदल सकता है जिससे पहला tab submit करने वाला है। mock साइट हर पेज लोड पर नई session cookie सेट करती है, जैसा कुछ असली साइटें करती हैं, इसलिए दूसरे लोड ने पहले tab की cookie बदल दी और एक साथ किए गए दो submits में से एक reject हो गया। हर tab को उसका अपना browser context देने से यह ठीक हो गया, और दोनों submits स्वीकार हुए। यह संस्करण main() को उसी तरह बदलता है, और यह submit पर क्लिक करता है, इसलिए सादे form के लिए ठीक है।

async def solve_page(tab):
    sitekey = await find_sitekey(tab)
    token = await solve(sitekey, await tab.current_url())
    await fill_token(tab, token)
    button = await tab.query('form button[type="submit"]')
    async with tab.expect_navigation():
        await button.click()
    return await tab.title()


async def main():
    options = ChromiumOptions()
    options.headless = True
    async with Chrome(options=options) as browser:
        await browser.start()
        # One browser context per tab: tabs in the default context
        # share cookies, and a second page load can replace the first
        # tab's session or CSRF cookie.
        contexts = [await browser.create_browser_context() for _ in range(2)]
        tabs = [
            await browser.new_tab(PAGE, browser_context_id=context)
            for context in contexts
        ]
        print(await asyncio.gather(*[solve_page(tab) for tab in tabs]))
        for context in contexts:
            await browser.delete_browser_context(context)


asyncio.run(main())

किसी दूसरी मशीन पर चल रहे CapSkip के सामने Pydoll चलाएँ

Pydoll और सॉल्वर को एक ही PC पर होना ज़रूरी नहीं है। कनेक्शन सेटिंग्स में CapSkip को Server mode पर करें, और वह आपके नेटवर्क पते या पब्लिक IP पर सुनेगा। स्थिर रिमोट एक्सेस के लिए स्टैटिक पब्लिक IP सुझाया जाता है। फिर सिर्फ़ client की लाइन बदलती है। सॉल्वर अब भी आपकी अपनी Windows मशीन है, और हल अब भी बिना किसी शुल्क के हैं।

# CapSkip on another Windows PC, in Server mode
solver = AsyncCapSkip(
    host='192.168.1.50',  # that PC's network address
    port=8080,
    apiKey='YOUR_API_KEY',  # only if API key validation is on
)

दो बातों का ध्यान रखें। SDK, CapSkip से सादे HTTP पर बात करता है, इसलिए API key सादे text में जाती है। उस link को private network या VPN पर रखें, और API key validation चालू करें। और Server mode शुरू होते ही CapSkip अपना port हर पते के लिए खोल देता है, इसलिए जिन पतों की आपको उम्मीद नहीं उनके लिए Windows Firewall का block rule ही उसे सीमित करने का तरीक़ा है। AsyncCapSkip के साथ, पहुँच से बाहर वाला पता करीब 5 सेकंड बाद NetworkException के साथ फेल होता है, जिसका संदेश ख़ाली होता है। इस गाइड के लिए remote setup चलाया नहीं गया, सिर्फ़ constructor पढ़ा गया।

Timeouts और फेल होने पर क्या दिखता है

जल्दी हल करें, फिर जल्दी submit करें। reCAPTCHA token एक बार चलता है और expire हो जाता है, इसलिए पेज लोड होने के बाद और submit से ठीक पहले हल करें। सख़्त mock साइट ने उसी token का दूसरा submit 403 के साथ ठुकरा दिया, जो mock का अपना नियम है। समय की जानकारी reCAPTCHA टोकन की एक्सपायरी वाले पेज पर मिलेगी। नीचे दिखाया है कि mock के सामने AsyncCapSkip और default settings के साथ हर विफलता कैसी दिखी:

मामलाआप जो देखते हैंकितने बाद
CapSkip चल नहीं रहाNetworkException: All connection attempts failed2.2 s
CapSkip call स्वीकार करता है और चुप हो जाता हैखाली संदेश वाला NetworkException5.3 s
हल हो नहीं सकताApiException: ERROR_CAPTCHA_UNSOLVABLE0.7 s
recaptchaTimeout को 6 पर सेट करके 30 s का हलTimeoutException: timeout 6.0 exceeded8.7 s
वही हल asyncio.wait_for के भीतर 4 s के साथPython का अपना TimeoutError (Python 3.10 पर asyncio.TimeoutError)4.0 s

recaptchaTimeout को client पर सेट करें, call पर नहीं। call पर timeout argument देने से TypeError उठता है, जिसके संदेश में got multiple values for keyword argument ‘timeout’ लिखा होता है। SDK अपनी सीमा सिर्फ़ polls के बीच जाँचता है, इसीलिए 6 सेकंड की सीमा 8.7 सेकंड पर चली। अगर कठोर सीमा चाहिए तो call को asyncio.wait_for में लपेटें। इससे सिर्फ़ आपकी तरफ़ का काम रद्द होता है। CapSkip job को अपनी सीमाओं तक रखता है, जिसे असली app के साथ टेस्ट नहीं किया गया। Pydoll के अपने errors हैं। go_to की इजाज़त से ज़्यादा लगने वाला navigation PageLoadTimeout उठाता है, और refused connection NavigationError उठाता है, जिसके संदेश में net::ERR_CONNECTION_REFUSED होता है। Pydoll में TimeoutException नाम की class भी है, और CapSkip में उसी नाम की एक class है। ये अलग classes हैं, इसलिए modules को import करें और सॉल्वर वाली को पकड़ते समय capskip.TimeoutException लिखें।

Pydoll कैप्चा के आम errors

आप जो देखते हैंकारणफिक्स
ImportError: cannot import name ‘Chrome’ from ‘pydoll’Pydoll 2.x इंस्टॉल हैpydoll-python 3 पर अपग्रेड करें
InvalidBrowserPath: No valid browser path foundChrome किसी एक user के लिए इंस्टॉल है, Program Files में नहींoptions.binary_location सेट करें
FailedToStartBrowser: The browser exited with code 21कोई तयशुदा user-data-dir किसी दूसरे browser के इस्तेमाल में हैकोई दूसरा profile folder इस्तेमाल करें, या कोई नहीं
ArgumentAlreadyExistsInOptionsदूसरे browser के लिए वही ChromiumOptions object दोबारा इस्तेमाल हुआ, या आपने ऐसा flag जोड़ा जिसे Pydoll ख़ुद जोड़ता हैहर browser के लिए नया options object बनाएँ
sitekey query पर WaitElementTimeoutपेज पर अभी widget नहीं है, या selector उससे मेल नहीं खातापेज को दिखने वाली window में जाँचें, फिर timeout बढ़ाएँ
NetworkException: All connection attempts failedCapSkip चल नहीं रहा, या port गलत हैCapSkip शुरू करें और कनेक्शन सेटिंग्स जाँचें
ApiException: ERROR_CAPTCHA_UNSOLVABLECapSkip हल पूरा नहीं कर सकाsitekey और पेज का पता जाँचें, फिर एक बार दोबारा आज़माएँ। देखें: ERROR_CAPTCHA_UNSOLVABLE
RuntimeError: ReferenceError, कोई नाम परिभाषित नहीं हैfill_token की JavaScript ने पेज के अंदर exception उठायावही script browser console में चलाएँ
पेज कहता है कि कैप्चा फेल हुआ, Python में कोई error नहींtoken दो बार इस्तेमाल हुआ, या callback नहीं चलाहर submit से ठीक पहले दोबारा हल करें
FailedToStartBrowser: Could not launchoptions.binary_location ऐसी फ़ाइल की ओर इशारा करता है जो वहाँ है ही नहींchrome.exe का path जाँचें
हर run काम से करीब 4 सेकंड ज़्यादा लेता हैChrome से हर नया connection करीब 2 सेकंड की localhost देरी भरता हैएक browser और एक tab दोबारा इस्तेमाल करें: उसी tab पर दूसरा go_to 0.2 सेकंड से कम में हुआ

FAQ

क्या Pydoll reCAPTCHA अपने आप हल कर देता है?

नहीं। Pydoll 3.0.0 में reCAPTCHA का कोई कोड नहीं है। उसका इकलौता कैप्चा helper Cloudflare Turnstile का checkbox ढूँढकर उस पर क्लिक करता है, जो हल नहीं है और कोई token नहीं लौटाता।

क्या यह headless Chrome के साथ काम करता है?

पूरी script mock पेज के सामने headless और headed, दोनों में पास हुई, Chrome 155 और Chrome for Testing 146 पर। live साइट headless browser के साथ कैसा बर्ताव करती है, यह टेस्ट नहीं किया गया, और वह token से अलग सवाल है।

क्या मैं Pydoll किसी दूसरे PC पर और CapSkip घर पर चला सकता हूँ?

हाँ, अगर वह PC CapSkip तक पहुँच सके। CapSkip को Server mode में रखें, और client में host तथा port सेट करें। इंटरनेट पर स्थिर रिमोट एक्सेस के लिए स्टैटिक पब्लिक IP सुझाया जाता है। key और पेज का पता सादे HTTP पर सादे text में जाते हैं, इसलिए VPN या private network को चुनें, और बाक़ी पतों के लिए Windows Firewall का block rule जोड़ें। इस गाइड के लिए कोई remote setup टेस्ट नहीं किया गया।

invisible reCAPTCHA, v3 और Enterprise का क्या?

SDK की recaptcha call इनके लिए options लेती है: invisible को 1 पर, enterprise को 1 पर और version को v3 पर सेट करना। Pydoll की तरफ़ पेज को अलग ढंग से पढ़ना पड़ता है, जैसे v3 के लिए action का नाम, और इनमें से कोई variant इस गाइड के लिए चलाया नहीं गया। v3 सॉल्वर call के लिए देखें: Python में reCAPTCHA v3 हल करना.

संक्षेप में

Pydoll कैप्चा flow के पाँच हिस्से हैं: sitekey को data-sitekey या iframe के पते से पढ़ें, AsyncCapSkip से हल करें ताकि event loop चलता रहे, token को g-recaptcha-response में लिखें, पेज का callback चलाएँ या submit करें, और हर समानांतर tab को उसका अपना browser context दें। submit से ठीक पहले हल करें, और client पर recaptchaTimeout सेट करें, साथ में call के चारों ओर asyncio.wait_for को कठोर सीमा की तरह रखें।

browser crawl महँगा होने से बहुत पहले धीमा हो जाता है, और अपनी ही Windows मशीन पर चल रहे लोकल captcha सॉल्वर के साथ हल करने पर कोई बिल बनता ही नहीं।