MechanicalSoup में reCAPTCHA हल करके फॉर्म कैसे सबमिट करें

mechanicalsoup captcha - How to Solve reCAPTCHA in MechanicalSoup and Submit the Form

MechanicalSoup कभी कैप्चा widget नहीं चलाता, इसलिए जो फ़ील्ड widget भरता, वह आमतौर पर फॉर्म में मौजूद नहीं होता, और उसे सेट करने पर LinkNotFoundError उठता है। MechanicalSoup कैप्चा का हल तीन स्टेप में होता है। जिस पेज को MechanicalSoup पहले ही पार्स कर चुका है, उससे sitekey पढ़ें, और उसे अपनी ही Windows मशीन पर चल रहे CapSkip से हल करें। फिर submit_selected कॉल करने से पहले g-recaptcha-response फ़ील्ड को new_control से खुद जोड़ें। बाकी काम MechanicalSoup संभालता है: session cookie, छिपा हुआ CSRF फ़ील्ड, और वह Referer header जिसे कोई सख़्त सर्वर जाँचता है। यह गाइड reCAPTCHA checkbox, invisible बटन, v3, Enterprise, CapSkip से हल होने वाले बाकी widgets, एक साथ कई लॉगिन, और सॉल्वर को किसी दूसरी मशीन पर चलाना कवर करती है।

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

  • किसी Windows मशीन पर चल रहा CapSkip, जिसमें reCAPTCHA सॉल्वर चालू हो।
  • Python 3.10 या उसके बाद का संस्करण, capskip पैकेज और MechanicalSoup। सैंपल capskip 1.3.0 और MechanicalSoup 1.4.0 के साथ चलाए गए थे, जिनके साथ requests, Beautiful Soup और lxml भी install हो जाते हैं।
  • फॉर्म वाले पेज का URL। sitekey, फॉर्म फ़ील्ड और widget का टाइप, सब उसी पेज के HTML से मिलते हैं।
  • सॉल्वर के लिए एक पता। Local मोड में CapSkip केवल उसी डिवाइस के लिए 127.0.0.1 पर जवाब देता है। Server मोड में वह आपके नेटवर्क पते या पब्लिक IP पर सुनता है, ताकि किसी VPS या दूसरे PC पर चल रही script API के ज़रिए उसे कॉल कर सके। दोनों यहाँ सेट किए जाते हैं: कनेक्शन सेटिंग्स.
# Python 3.10 or later; MechanicalSoup brings requests, bs4 and lxml
pip install capskip mechanicalsoup

स्टेप 1: पेज खोलें और sitekey ढूँढें

StatefulBrowser एक requests session है, जिसके साथ एक पार्स किया हुआ पेज जुड़ा होता है। यह साइट की सेट की हुई हर cookie रखता है, redirects फ़ॉलो करता है, और याद रखता है कि आख़िर में किस पते पर पहुँचा। लॉगिन पेज खोलें और reCAPTCHA element ढूँढें:

# pip install capskip mechanicalsoup
import mechanicalsoup

PAGE_URL = "https://example.com/login"

# One StatefulBrowser per session: it keeps cookies, follows redirects
# and sends the current page as Referer when it submits a form.
browser = mechanicalsoup.StatefulBrowser(raise_on_404=True)
browser.open(PAGE_URL)

# The checkbox div, or the button on invisible reCAPTCHA, holds the key.
widget = browser.page.select_one(".g-recaptcha[data-sitekey]")
if widget is None:
    raise RuntimeError("No reCAPTCHA widget in the HTML; see the v3 section.")
sitekey = widget["data-sitekey"]

# The form around the widget; a page often has a search form first.
browser.select_form(widget.find_parent("form") or 'form[method="post" i]')
print(sitekey, browser.url)

checkbox widget, g-recaptcha class और data-sitekey attribute वाला एक div होता है। Invisible reCAPTCHA आमतौर पर यही class और attribute इसके बजाय submit बटन पर रखता है, इसलिए एक ही selector दोनों को ढूँढ लेता है। जो पेज api.js को render=explicit के साथ लोड करता है, वह widget को अपनी script से बनाता है और key को grecaptcha.render को पास करता है, इसलिए उसे DevTools में उसी कॉल से पढ़ें। पेज के पहले फॉर्म के बजाय widget के आसपास वाला फॉर्म चुनें। कई साइटें header में एक search फॉर्म रखती हैं, और select_form डिफ़ॉल्ट रूप से वही चुनता है। जब widget अपने फॉर्म के बाहर हो, तो fallback पोस्ट करने वाला पहला फॉर्म ले लेता है, इसलिए अगर पेज पर दो ऐसे फॉर्म हों, तो इसके बजाय लॉगिन फॉर्म की id पास करें।

browser.url वह पता है जिससे पेज सभी redirects के बाद आया। सॉल्वर को यही URL भेजना है, क्योंकि sitekey अपनी साइट के डोमेन के लिए रजिस्टर होती है और गलत पेज के लिए किया गया solve बेकार जाता है। raise_on_404 की वजह से गायब पेज आगे जाकर नहीं, बल्कि open पर ही साफ़ error के साथ फेल होता है।

स्टेप 2: CapSkip से reCAPTCHA हल करें

capskip पैकेज की एक ही कॉल हल करने का काम करती है। CapSkip आपकी Windows मशीन पर कैप्चा हल करता है और वह token लौटाता है जिसे widget फॉर्म में रखता:

from capskip import CapSkip

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

# enterprise.js in place of api.js means reCAPTCHA Enterprise.
enterprise = int("recaptcha/enterprise.js" in str(browser.page))
if widget.name == "button" and widget.get("data-action"):
    # Google's v3 button binding: a g-recaptcha button with an action is v3.
    result = solver.recaptcha(sitekey=sitekey, url=browser.url, version="v3",
                              action=widget["data-action"], enterprise=enterprise)
else:
    # A g-recaptcha button, or data-size="invisible", is the invisible widget.
    invisible = widget.name == "button" or widget.get("data-size") == "invisible"
    result = solver.recaptcha(sitekey=sitekey, url=browser.url,
                              invisible=int(invisible), enterprise=enterprise)

token = result["code"]
print(token[:40])   # what the widget would have written into the form

checkbox सीधी कॉल है, और invisible reCAPTCHA वही कॉल है, बस invisible को 1 पर सेट करके। जिस g-recaptcha बटन पर data-action भी हो, वह Google की v3 button binding है, इसलिए script उसे उसी action के साथ v3 के रूप में हल करती है। जो पेज api.js के बजाय enterprise.js लोड करते हैं, वे reCAPTCHA Enterprise चलाते हैं, और enterprise=1 यही बात CapSkip को बताता है। token result["code"] में होता है। यह लगभग दो मिनट तक वैध रहता है और सिर्फ़ एक बार verify होता है, इसलिए सबमिट करने से ठीक पहले हल करें और हर लॉगिन के लिए फिर से हल करें।

recaptcha ज़्यादा से ज़्यादा recaptchaTimeout तक पोलिंग करता है, जो डिफ़ॉल्ट रूप से 300 सेकंड है। कोई दूसरी वैल्यू constructor पर सेट करें, क्योंकि हर कॉल पर पास किया गया timeout TypeError उठाता है: method पहले से ही अपना एक timeout पास करता है। CapSkip की अपनी समय सीमाएँ भी होती हैं। कोई टास्क किसी खाली reCAPTCHA thread के लिए 250 सेकंड (Wait Timeout) तक इंतज़ार करता है, और फिर solve की भी अपनी एक सीमा होती है: Row Timeout, जो डिफ़ॉल्ट रूप से 250 सेकंड है। जब इनमें से कोई सीमा खत्म हो जाती है, तो CapSkip टास्क को फेल कर देता है और SDK, ERROR_CAPTCHA_UNSOLVABLE के साथ ApiException उठाता है। यहाँ इस्तेमाल हुआ CapSkip क्लाइंट अपनी HTTP कॉल पर कोई timeout सेट नहीं करता, इसलिए अगर CapSkip कनेक्शन स्वीकार कर ले और कभी जवाब न दे, तो कॉल खुली ही रहती है।

स्टेप 3: वह फ़ील्ड जोड़ें जो widget लिखता, फिर सबमिट करें

हर MechanicalSoup कैप्चा script इसी स्टेप पर अटकती है। असली ब्राउज़र में Google की script, g-recaptcha-response नाम का एक छिपा हुआ textarea बनाती है और कैप्चा हल होने पर उसे भरती है। MechanicalSoup सर्वर से आया HTML पार्स करता है और कोई JavaScript नहीं चलाता, इसलिए ज़्यादातर पेजों पर वह फ़ील्ड मौजूद नहीं होता। तब उसमें सामान्य तरीके से वैल्यू डालने पर LinkNotFoundError उठता है, जिसका मैसेज No valid element named g-recaptcha-response होता है। new_control चुने हुए फॉर्म में यह फ़ील्ड जोड़ता है:

# This line raises LinkNotFoundError on most pages: the widget script
# creates the field in a real browser, so the parsed HTML lacks it.
# browser["g-recaptcha-response"] = token

# Add the field the widget would have written, replacing any copy.
browser.new_control("hidden", "g-recaptcha-response", token)
browser["email"] = "YOUR_EMAIL"
browser["password"] = "YOUR_PASSWORD"

response = browser.submit_selected()
print(response.status_code, browser.url)

new_control अपना फ़ील्ड जोड़ने से पहले उसी नाम का हर फ़ील्ड हटा देता है, और यह उन पेजों पर मायने रखता है जिनमें Google का noscript fallback मौजूद रहता है। उस fallback में उसी नाम का एक textarea होता है, और MechanicalSoup का parser उसे फॉर्म का हिस्सा मानकर पढ़ता है। ऐसे पेजों पर सीधा assignment भी काम करता है, लेकिन new_control एक ही लाइन में दोनों तरह के पेज संभाल लेता है।

submit_selected फॉर्म का हर फ़ील्ड, छिपे हुए CSRF token समेत, उसी session पर भेजता है जिसने पेज लोड किया था। यह पेज के पते को Referer header के रूप में भी भेजता है। सादा requests कोई Referer नहीं भेजता, और न ही MechanicalSoup का निचले स्तर वाला Browser.submit method। कुछ frameworks, जिनमें Django भी है, Referer या Origin के बिना आई HTTPS फॉर्म पोस्ट ठुकरा देते हैं, cookie और token चाहे कितने भी सही हों। इसलिए उसी browser पर submit_selected का इस्तेमाल करें जिसने पेज खोला था। अगर फॉर्म में कई submit बटन हैं, तो जिसे चाहिए उसका नाम btnName के रूप में पास करें। और 200 मिलना इस बात का सबूत नहीं कि लॉगिन हो गया। कई साइटें मना किए गए कैप्चा का जवाब उसी फॉर्म और 200 के साथ देती हैं, इसलिए browser.page में ऐसी चीज़ देखें जो सिर्फ़ साइन-इन किया हुआ पेज दिखाता है।

reCAPTCHA v3 में ढूँढने के लिए कोई widget नहीं होता

v3 यूज़र से कुछ नहीं पूछता। साइट Google की script को ऐसे पते से लोड करती है जिसमें render= के बाद उसकी sitekey होती है, और साइट की अपनी script login जैसे action नाम के साथ token माँगती है। ज़्यादातर v3 पेज फिर token को खुद एक छिपे हुए input में लिखते हैं। दोनों वैल्यू HTML से पढ़ें, ध्यान दें कि script api.js है या enterprise.js, और उन्हें version को v3 पर सेट करके आगे पास करें। जब कोई पेज इसके बजाय v3 को किसी बटन से बाँधता है, तो स्टेप 2 उसे पहले ही संभाल लेता है।

import re

# A v3 page has no widget element; the key sits in the script tags.
html = str(browser.page)
key = re.search(r"recaptcha/(api|enterprise)\.js\?[^\"'<>\s]*?\brender=(?!explicit\b)([\w-]+)", html)
action = re.search(r"execute\(\s*[^,()]+,\s*\{\s*[\"']?action[\"']?\s*:\s*[\"']([\w/]+)", html)
if key is None:
    raise RuntimeError("No v3 render key in the HTML; check DevTools.")

v3 = solver.recaptcha(sitekey=key.group(2), url=browser.url, version="v3",
                      enterprise=int(key.group(1) == "enterprise"),
                      **({"action": action.group(1)} if action else {}))

browser.select_form('form[method="post" i]')
# The field the page's own script fills in; read its name in DevTools.
browser.new_control("hidden", "g-recaptcha-response", v3["code"])

v3 पेज पर, स्टेप 1 को सिर्फ़ browser.open तक चलाएँ, फिर स्टेप 2 की वे लाइनें जो सॉल्वर बनाती हैं, फिर यह block, फिर स्टेप 3 के क्रेडेंशियल और submit_selected। action इसलिए मायने रखता है क्योंकि साइट का backend आमतौर पर जाँचता है कि token उसी action के लिए जारी हुआ था जिसकी उसे उम्मीद थी। जब pattern को कोई action नहीं मिलता, तो कॉल बिना action के जाती है और CapSkip, verify इस्तेमाल करता है, जिसे login की उम्मीद रखने वाली साइट ठुकरा देती है। अगर execute कॉल किसी बाहरी script में है, तो DevTools में action पढ़ें और उसे खुद पास करें। फ़ील्ड का नाम वही होता है जो पेज की script भरती है, अक्सर g-recaptcha-response, पर हमेशा नहीं, इसलिए एक बार हाथ से सबमिट करके DevTools में उसे जाँच लें। फिर क्रेडेंशियल भरें और ऊपर की तरह submit_selected कॉल करें। Google के reCAPTCHA v3 डॉक्युमेंटेशन में वे render और execute कॉल दिखाए गए हैं जिनसे ये patterns मैच करते हैं। Python में reCAPTCHA v3 हल करने की गाइड इस कॉल को और गहराई से कवर करती है।

बाकी widgets भी इसी तरह काम करते हैं

CapSkip जो भी widget हल करता है, MechanicalSoup में वह इसी pattern पर चलता है: key पढ़ें, हल करें, वह फ़ील्ड जोड़ें जो widget बनाता, सबमिट करें। बदलते सिर्फ़ selector, फ़ील्ड का नाम और method हैं:

विजेटkey कहाँ हैजोड़ने वाला फ़ील्डcapskip का method
reCAPTCHA v2 checkboxdiv.g-recaptcha, data-sitekeyg-recaptcha-responsesolver.recaptcha()
reCAPTCHA v2 invisibleg-recaptcha class वाला बटन, या data-size=invisible वाला divg-recaptcha-responseinvisible=1 के साथ solver.recaptcha()
reCAPTCHA v3script के पते में api.js?render= या enterprise.js?render=, या data-action वाला g-recaptcha बटनवह input जिसे पेज की script भरती हैversion="v3" और action के साथ solver.recaptcha()
reCAPTCHA Enterpriseapi.js की जगह enterprise.jsv2 या v3 की तरहenterprise=1 के साथ solver.recaptcha()
Cloudflare Turnstile विजेटdiv.cf-turnstile, data-sitekeycf-turnstile-responsesolver.turnstile()
Friendly Captchadiv.frc-captcha, data-sitekeyv2 पर frc-captcha-response, v1 पर frc-captcha-solutionversion="v1" या "v2" के साथ solver.friendly_captcha()
CaptchaFoxdiv.captchafox, data-sitekeycf-captcha-responsesolver.captchafox()

CaptchaFox ही वह widget है जिसमें एक अतिरिक्त नियम है। उसका token उसी ब्राउज़र का होता है जिसने उसे बनाया। इसलिए जब नतीजे में userAgent हो, तो सबमिट करने से पहले उसे set_user_agent से browser पर सेट करें, क्योंकि वरना MechanicalSoup, python-requests और अपना नाम भेजता है। Python में CaptchaFox हल करने की गाइड widget build और दोबारा कोशिशों को कवर करती है।

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

यह रहा एक ही फ़ाइल में पूरा MechanicalSoup कैप्चा लॉगिन। login एक नए browser में पेज खोलता है, checkbox, invisible या v3 reCAPTCHA ढूँढता है, उसे हल करता है, फ़ील्ड जोड़ता है और सबमिट करता है:

# pip install capskip mechanicalsoup
# login.py: run with python login.py
import os
import re

import mechanicalsoup
from capskip import CapSkip, CapSkipError, TimeoutException

PAGE_URL = "https://example.com/login"

# render= carries a v3 key on api.js or enterprise.js; render=explicit is a v2 page.
V3_KEY = re.compile(r"recaptcha/(api|enterprise)\.js\?[^\"'<>\s]*?\brender=(?!explicit\b)([\w-]+)")
# The action passed to execute, whether the key is a string or a variable.
V3_ACTION = re.compile(r"execute\(\s*[^,()]+,\s*\{\s*[\"']?action[\"']?\s*:\s*[\"']([\w/]+)")

solver = CapSkip(
    apiKey=os.getenv("CAPSKIP_API_KEY", "capskip"),
    host=os.getenv("CAPSKIP_HOST", "127.0.0.1"),
    port=int(os.getenv("CAPSKIP_PORT", "8080")),
)


def solve_page(browser):
    """Solve the page's reCAPTCHA and select its form. Returns the token."""
    html = str(browser.page)
    enterprise = int("recaptcha/enterprise.js" in html)
    widget = browser.page.select_one(".g-recaptcha[data-sitekey]")
    if widget is not None:
        browser.select_form(widget.find_parent("form") or 'form[method="post" i]')
        if widget.name == "button" and widget.get("data-action"):
            # Google's v3 button binding: plain api.js, the action on the button.
            return solver.recaptcha(sitekey=widget["data-sitekey"], url=browser.url, version="v3",
                                    action=widget["data-action"], enterprise=enterprise)["code"]
        invisible = widget.name == "button" or widget.get("data-size") == "invisible"
        return solver.recaptcha(sitekey=widget["data-sitekey"], url=browser.url,
                                invisible=int(invisible), enterprise=enterprise)["code"]
    key = V3_KEY.search(html)
    if key is None:
        raise RuntimeError(f"No reCAPTCHA found on {browser.url}")
    action = V3_ACTION.search(html)
    browser.select_form('form[method="post" i]')
    return solver.recaptcha(sitekey=key.group(2), url=browser.url, version="v3",
                            enterprise=int(key.group(1) == "enterprise"),
                            **({"action": action.group(1)} if action else {}))["code"]


def login(email, password, page_url=PAGE_URL):
    # A new browser per login, so sessions never share cookies.
    with mechanicalsoup.StatefulBrowser(raise_on_404=True) as browser:
        browser.open(page_url)
        token = solve_page(browser)
        browser.new_control("hidden", "g-recaptcha-response", token)
        browser["email"] = email
        browser["password"] = password
        response = browser.submit_selected()
        return response.status_code, browser.url


if __name__ == "__main__":
    try:
        print(login("YOUR_EMAIL", "YOUR_PASSWORD"))
    except TimeoutException:
        raise SystemExit("gave up after recaptchaTimeout")
    except CapSkipError as exc:
        raise SystemExit(f"solve failed: {type(exc).__name__}: {exc}")
    except mechanicalsoup.LinkNotFoundError as exc:
        # A 404, or a form or field the page does not have.
        raise SystemExit(f"not found: {str(exc) or PAGE_URL}")
    except RuntimeError as exc:
        raise SystemExit(str(exc))

login की हर कॉल अपना अलग StatefulBrowser बनाती है और काम खत्म होने पर उसे बंद कर देती है, इसलिए दो लॉगिन कभी cookies साझा नहीं करते। with block कोई स्टेप फेल होने पर भी session बंद कर देता है। ज़्यादातर failures खुद बता देते हैं कि कहाँ देखना है। LinkNotFoundError का मतलब है 404, गायब फॉर्म, या ऐसा फ़ील्ड नाम जो पेज से मेल नहीं खाता। RuntimeError का मतलब है कि पेज पर ऐसा कोई reCAPTCHA नहीं दिखता जिसे script पहचानती हो, इसलिए उसे किसी असली ब्राउज़र में खोलें और देखें कि क्या लोड होता है। अगर वहाँ वह ठीक से लोड होता है, तो browser.open(page_url).status_code प्रिंट करें: 403 का मतलब है कि साइट ने डिफ़ॉल्ट user agent को मना कर दिया। CapSkipError खुद solve से आता है, इसलिए नीचे दी गई errors की टेबल लागू होती है।

एक साथ कई लॉगिन, और किसी दूसरी मशीन पर सॉल्वर

MechanicalSoup synchronous है, और यहाँ इस्तेमाल हुआ CapSkip क्लाइंट भी। इसलिए जब किसी MechanicalSoup कैप्चा job में कई लॉगिन निपटाने हों, तो उन्हें साथ-साथ चलाने का सबसे आसान तरीका threads हैं। हर thread के लिए एक StatefulBrowser रखें, जो login पहले से करता है, और pool को दस पर सीमित रखें, जो reCAPTCHA के लिए CapSkip का डिफ़ॉल्ट Max. Threads है। इससे आगे queue में लगा टास्क किसी खाली thread का इंतज़ार करता है, और लंबी queue उन 300 सेकंड में से समय खा जाती है जिन तक SDK पोलिंग करता है। max_workers बढ़ाने से पहले CapSkip की reCAPTCHA सेटिंग्स में Max. Threads बढ़ाएँ। पूरे उदाहरण को login.py के रूप में सेव करें और उसके बगल में रखी दूसरी फ़ाइल से यह चलाएँ:

# pool.py: save next to login.py, the full example, and run this file
from concurrent.futures import ThreadPoolExecutor

from login import login

accounts = [("YOUR_EMAIL_1", "YOUR_PASSWORD_1"), ("YOUR_EMAIL_2", "YOUR_PASSWORD_2")]


def attempt(account):
    # One failed login must not stop the others.
    try:
        return login(*account)
    except Exception as exc:
        return f"{type(exc).__name__}: {exc}"


# Ten at a time, to match CapSkip's default Max. Threads for reCAPTCHA.
with ThreadPoolExecutor(max_workers=10) as pool:
    for account, outcome in zip(accounts, pool.map(attempt, accounts)):
        print(account[0], outcome)

pool.map नतीजे उसी क्रम में लौटाता है जिसमें खाते दिए गए थे, और attempt किसी failure को एक वैल्यू में बदल देता है, इसलिए एक खराब लॉगिन बाकियों को नहीं रोकता। एक mock सॉल्वर के साथ इस pool से गुज़रे पच्चीस लॉगिन में कभी भी एक समय में दस से ज़्यादा solve चालू नहीं रहे।

जब तक script और CapSkip एक ही Windows PC पर हों, 127.0.0.1 सही है। script को किसी VPS, किसी दूसरी मशीन पर चलने वाले scheduled job या कंटेनर में ले जाएँ, तो loopback गलत मशीन की ओर इशारा करता है, इसलिए पहला ही solve NetworkException उठाता है। CapSkip को Server मोड पर कर दें और वह आपके नेटवर्क पते या पब्लिक IP पर सुनने लगता है, ताकि इनमें से कोई भी उसी API के ज़रिए उसे कॉल कर सके। जब रास्ता इंटरनेट से होकर जाए तो स्टैटिक पब्लिक IP इस्तेमाल करें। Server मोड शुरू होते ही CapSkip अपना पोर्ट हर पते के लिए खोल देता है, इसलिए उसे सिर्फ़ अपेक्षित मशीनों तक सीमित रखने के लिए बाकी पतों के लिए Windows Firewall में एक block नियम जोड़ें। यह अब भी आपकी अपनी मशीन है, और solve अब भी बिना मीटर वाले हैं। क्लाइंट एनवायरनमेंट वेरिएबल खुद नहीं पढ़ता, इसीलिए पूरा उदाहरण CAPSKIP_HOST पढ़ता है और उसे पास करता है।

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

आप जो देखते हैंकारणफिक्स
LinkNotFoundError: No valid element named g-recaptcha-responsewidget की script कभी चली ही नहीं, इसलिए उसका बनाया फ़ील्ड पार्स किए गए फॉर्म में नहीं हैसबमिट करने से पहले उसे new_control से जोड़ें
token search फॉर्म के साथ पोस्ट हो जाता है, या select_form, LinkNotFoundError उठाता हैपेज पर पहले कोई दूसरा फॉर्म है, या widget अपने फॉर्म के बाहर हैwidget के आसपास वाला फॉर्म चुनें, या लॉगिन फॉर्म को उसकी id से चुनें
नए token के बावजूद 403 या CSRF errorपोस्ट किसी नए session पर, requests.post से, या Browser.submit से गई, इसलिए cookie या Referer मौजूद नहीं थाजिस browser ने पेज खोला था, उसी पर फॉर्म भरें और submit_selected से सबमिट करें
CapSkip के token लौटाने के बावजूद साइट कहती है कि कैप्चा फेल हुआinvisible widget को checkbox की तरह हल किया गया, v3 का action पेज के action से अलग है, Enterprise पेज को enterprise=1 के बिना हल किया गया, या token दो मिनट से पुराना थाinvisible=1, enterprise=1 या पेज का action पास करें, और सबमिट करने से ठीक पहले हल करें
TypeError: capskip.solver.CapSkip.solve() got multiple values for keyword argument timeoutrecaptcha की कॉल में timeout पास किया गयाइसके बजाय CapSkip constructor पर recaptchaTimeout सेट करें
ERROR_CAPTCHA_UNSOLVABLE वाला ApiExceptionCapSkip ने अपनी समय सीमा पर टास्क फेल कर दिया, या sitekey और पेज URL एक-दूसरे से मेल नहीं खातेbrowser.url और पेज की अपनी sitekey भेजें, और एक समय में ज़्यादा से ज़्यादा दस solve चालू रखें
300 सेकंड बाद TimeoutExceptionकिसी खाली thread का इंतज़ार और solve, दोनों मिलकर SDK की सीमा से आगे निकल गएpool को Max. Threads तक सीमित रखें, या constructor पर recaptchaTimeout बढ़ाएँ
असली ब्राउज़र में ठीक से लोड होने वाले पेज पर कोई reCAPTCHA नहीं मिलासाइट ने 403 से जवाब दिया, क्योंकि वह डिफ़ॉल्ट user agent को मना करती है, जिसमें python-requests और MechanicalSoup का नाम होता हैStatefulBrowser बनाते समय किसी असली ब्राउज़र का user agent, user_agent के रूप में पास करें
पहले हल पर NetworkExceptionCapSkip चल नहीं रहा, host या port गलत है, या script किसी दूसरी मशीन पर चलती हैCapSkip शुरू करें, फिर देखें कि उसे Local मोड में होना चाहिए या Server मोड में

FAQ

सादे requests के बजाय MechanicalSoup क्यों इस्तेमाल करें?

इससे आपको फॉर्म दोबारा नहीं बनाना पड़ता। requests के साथ आप छिपे हुए फ़ील्ड खुद इकट्ठा करते हैं, session संभालते हैं, फॉर्म का action resolve करते हैं और Referer सेट करते हैं, और इनमें से कोई एक भी छूटा तो सही token भी ठुकरा दिया जाता है। MechanicalSoup ये चारों काम पार्स किए गए पेज से कर देता है, इसलिए कैप्चा के लिए आपको बस वही एक फ़ील्ड जोड़ना होता है। जो पेज अपने फॉर्म JavaScript से बनाते हैं, उनके लिए दोनों में से कोई भी काफ़ी नहीं है, और वहाँ ब्राउज़र वाला कोई टूल बेहतर रहता है।

क्या कैप्चा के बाद session लॉग इन रहता है?

हाँ। StatefulBrowser लॉगिन के बाद साइट की सेट की हुई cookies रखता है, इसलिए उसी browser पर आप आगे जो पेज खोलते हैं, वे लॉग इन रहते हैं। पूरा उदाहरण login के लौटते ही अपना browser बंद कर देता है, इसलिए अपनी अगली रिक्वेस्ट उसी with block के अंदर ले जाएँ। या browser को with के बिना बनाएँ, उसे लौटाएँ, और काम पूरा होने पर browser.close() कॉल करें।

क्या VPS पर चल रही MechanicalSoup script मेरे Windows PC पर चल रहे CapSkip को इस्तेमाल कर सकती है?

हाँ। कनेक्शन सेटिंग्स में CapSkip को Server मोड पर कर दें, ताकि वह loopback के बजाय किसी नेटवर्क पते पर सुने। फिर जहाँ script चलती है, वहाँ CAPSKIP_HOST सेट करें, जिसे पूरा उदाहरण पढ़ता है। VPS, cron job और कंटेनर, सब उसी HTTP API के ज़रिए जुड़ते हैं। जब रास्ता इंटरनेट से होकर जाए, तो स्टैटिक पब्लिक IP इस्तेमाल करें, और जिन पतों को पोर्ट तक नहीं पहुँचना चाहिए, उनके लिए Windows Firewall में एक block नियम जोड़ें। सॉल्वर आपके अपने हार्डवेयर पर ही रहता है, इसलिए ज़्यादा लॉगिन से आपका ख़र्च कभी नहीं बदलता।

संक्षेप में

MechanicalSoup कैप्चा का हल एक गायब फ़ील्ड पर आकर टिकता है, क्योंकि MechanicalSoup वह widget नहीं चला सकता जो उसे लिखता है। पेज को StatefulBrowser से खोलें और widget के आसपास वाला फॉर्म चुनें। sitekey को g-recaptcha element से पढ़ें, या v3 पेज की scripts से render key और action, और browser.url के साथ CapSkip से हल करें। g-recaptcha-response फ़ील्ड को new_control से जोड़ें, बाकी फ़ील्ड भरें और उसी browser पर submit_selected कॉल करें, जो cookies, CSRF फ़ील्ड और Referer साथ ले आता है। सबमिट करने से ठीक पहले हल करें, एक समय में ज़्यादा से ज़्यादा दस लॉगिन चालू रखें, और जब script कहीं और चले, तो CapSkip को Server मोड पर कर दें।

MechanicalSoup स्क्रैपर को हल्का रखता है, क्योंकि यह कभी कोई ब्राउज़र शुरू नहीं करता। इसके साथ अपनी ही Windows मशीन पर चलने वाला कैप्चा सॉल्वर जोड़ें, तो भारी काम आपके अपने नियंत्रण वाले हार्डवेयर पर रहता है, और हर solve का कोई बिल नहीं आता।