APScheduler में बिना स्किप हुए रन के कैप्चा कैसे हल करें

apscheduler captcha - How to Solve CAPTCHAs in APScheduler Without Skipped Runs

APScheduler कैप्चा जॉब एक सामान्य function है जो CapSkip को कॉल करता है और interval trigger से शेड्यूल होता है। हल खुद सिर्फ़ एक लाइन का है। समस्या उसके आसपास के डिफ़ॉल्ट हैं। APScheduler एक समय में किसी जॉब का सिर्फ़ एक instance चलाता है। CapSkip Python SDK का synchronous क्लाइंट अपनी HTTP कॉल पर कोई timeout सेट नहीं करता। मिलकर इनका मतलब है कि कभी न लौटने वाला एक हल बाद के हर रन को रोक देता है, और इसका इकलौता संकेत हर दस मिनट पर आने वाली एक warning है। एक सेकंड से ज़्यादा देर से शुरू होने वाला रन छोड़ दिया जाता है। और Windows पर Python 3.13 या उससे पुराने वर्ज़न में, अगले रन का समय आने तक Ctrl+C कुछ नहीं करता। यह गाइड एक ऐसी जॉब में ये तीनों समस्याएँ ठीक करती है जो हर दस मिनट पर reCAPTCHA से सुरक्षित एक साइट में लॉग इन करती है।

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

  • किसी Windows मशीन पर चल रहा CapSkip, जिसमें reCAPTCHA सॉल्वर चालू हो।
  • Python 3.10 या उसके बाद का वर्ज़न। नीचे का कोड Python 3.13.7 और 3.14.0 पर चलाया गया।
  • APScheduler 3.11, जो मौजूदा stable लाइन है, साथ में capskip और requests पैकेज। APScheduler को 4 से नीचे पिन करें: 4.0 की pre-releases का API अलग है, और नीचे के imports उन पर फेल हो जाते हैं।
  • सॉल्वर के लिए एक पता। Local मोड में CapSkip केवल उसी डिवाइस के लिए 127.0.0.1 पर जवाब देता है। Server मोड में वह आपके नेटवर्क पते या पब्लिक IP पर सुनता है, ताकि किसी दूसरी मशीन पर चल रहा शेड्यूलर API के ज़रिए उसे कॉल कर सके। दोनों यहाँ सेट किए जाते हैं: कनेक्शन सेटिंग्स.

चरण 1: कैप्चा हल करने वाली जॉब

APScheduler जॉब कोई भी function हो सकती है। यह जॉब हर दस मिनट पर CapSkip से एक reCAPTCHA token माँगती है। localhost के बजाय 127.0.0.1 लिखें: Windows पर localhost पहले IPv6 पता आज़माता है, और हमारे रन में इससे न चल रहे सॉल्वर की रिपोर्ट आने का समय दोगुना हो गया, 2 सेकंड से 4।

# pip install "apscheduler<4" capskip
from apscheduler.schedulers.blocking import BlockingScheduler
from capskip import CapSkip

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


def solve_login_captcha():
    result = solver.recaptcha(sitekey="YOUR_SITEKEY", url="https://example.com/login")
    print("token:", result["code"][:20], "...")


scheduler = BlockingScheduler()
scheduler.add_job(solve_login_captcha, "interval", minutes=10)
scheduler.start()

BlockingScheduler मुख्य थ्रेड में चलता है और हर रन को 10 worker threads वाले पूल में भेजता है। पहला रन शुरू होते ही नहीं, बल्कि शुरू होने के एक interval बाद होता है। यह तब तक ठीक है जब तक कोई हल गड़बड़ न करे, और इस गाइड का बाकी हिस्सा इसी के बारे में है। छोटे उदाहरण छोटे रहने के लिए BlockingScheduler इस्तेमाल करते हैं। Windows पर Python 3.13 या उससे पुराने वर्ज़न में, इसकी जगह नीचे की पूरी script वाला लूप इस्तेमाल करें।

धीमा हल अगला रन क्यों स्किप करवा देता है

हर जॉब में max_instances डिफ़ॉल्ट रूप से 1 पर सेट होता है। जब किसी रन का समय हो और पिछला रन अब भी चल रहा हो, तो APScheduler दूसरा रन शुरू नहीं करता। इसके बजाय वह apscheduler.scheduler logger के तहत WARNING स्तर पर एक warning log करता है:

Execution of job "solve_login_captcha (trigger: interval[0:10:00], next run at: 2026-10-07 13:10:00 +08)" skipped: maximum number of running instances reached (1)

कैप्चा जॉब के लिए यह डिफ़ॉल्ट सही है। आप कभी नहीं चाहेंगे कि एक ही लॉगिन के लिए दो हल एक साथ चलें। जो हल interval से थोड़ा ज़्यादा समय लेता है, उसकी कीमत एक स्किप हुआ रन है, और जॉब अगले शेड्यूल किए गए समय पर जारी रहती है। 5 सेकंड के interval और 12.8 सेकंड के हल वाले एक टेस्ट में, हर हल पर दो रन स्किप हुए, और अगला रन हल के तुरंत बाद नहीं, बल्कि उसके बाद वाले शेड्यूल किए गए समय पर शुरू हुआ।

मुसीबत तब शुरू होती है जब कोई हल कभी खत्म ही नहीं होता। तब इकलौता अनुमत instance हमेशा के लिए व्यस्त रहता है, और बाद का हर रन उसी warning के साथ स्किप हो जाता है। जॉब शेड्यूल्ड दिखती है, log ज़िंदा दिखता है, और होता कुछ नहीं।

चरण 2: SDK को HTTP timeout दें

CapSkip Python SDK, requests.post और requests.get को बिना timeout के कॉल करता है। अगर CapSkip कनेक्शन स्वीकार कर ले और कभी जवाब न दे, उदाहरण के लिए क्योंकि ऐप फ़्रीज़ हो गया, तो कॉल हमेशा इंतज़ार करती रहती है। SDK का recaptchaTimeout, जो डिफ़ॉल्ट रूप से 300 सेकंड है, मदद नहीं करता, क्योंकि उसे सिर्फ़ दो पोल के बीच जाँचा जाता है, और अटका हुआ पोल कभी खत्म नहीं होता। हमारे टेस्ट में, इस तरह अटकी जॉब सॉल्वर के ठीक होने के बाद भी अटकी रही, और सिर्फ़ स्किप वाली warnings log करती रही।

दो सीधे-सादे उपाय काम नहीं करते। socket.setdefaulttimeout का कोई असर नहीं होता, क्योंकि requests हमेशा अपनी timeout वैल्यू नीचे पास करता है, तब भी जब वह वैल्यू None हो। और SDK कोई session object नहीं लेता जिसे आप कॉन्फ़िगर कर सकें। जो काम करता है, वह है SDK के module को requests पैकेज की जगह लेने वाला एक छोटा object देना, जिसकी post और get कॉल हमेशा timeout पास करें।

# pip install capskip requests
import functools
import types

import requests
import capskip.api
from capskip import CapSkip


# The SDK calls requests.post/get with no timeout. Hand its module a
# stand-in whose calls always pass one: 5 s to connect, 30 s to answer.
capskip.api.requests = types.SimpleNamespace(
    RequestException=requests.RequestException,
    post=functools.partial(requests.post, timeout=(5, 30)),
    get=functools.partial(requests.get, timeout=(5, 30)),
)  # do this before the first solve
solver = CapSkip(host="127.0.0.1", port=8080)

यह capskip.api के अंदर requests नाम को बदल देता है, जो capskip 1.3.0 में वह module है जो SDK की रिक्वेस्ट CapSkip को भेजता है। SDK अपग्रेड करने के बाद इसे फिर से जाँचें। इसे पहले हल से पहले एक बार चलाएँ, और हर कॉल को कनेक्ट होने के लिए 5 सेकंड और जवाब के लिए 30 सेकंड मिलते हैं।

तब दोनों तरह की अटकन अलग-अलग तरीके से खत्म होती है। अगर टास्क सबमिट होते समय CapSkip जवाब देना बंद कर दे, तो जॉब 30 सेकंड बाद read timed out बताने वाले NetworkException के साथ फेल हो जाती है। अगर SDK के नतीजे के लिए poll करते समय वह जवाब देना बंद कर दे, तो SDK timeout को अभी तैयार नहीं मानकर polling जारी रखता है, इसलिए जॉब recaptchaTimeout पर TimeoutException के साथ खत्म होती है। डिफ़ॉल्ट के साथ, हमारे टेस्ट में इसमें 333 सेकंड लगे। इसलिए सबसे बुरी स्थिति करीब साढ़े पाँच मिनट की है, जो दस मिनट के interval के अंदर आ जाती है, और अगला रन सामान्य रूप से हल करता है।

चरण 3: तय करें कि देर से शुरू होने वाले रन का क्या हो

दूसरा डिफ़ॉल्ट misfire_grace_time है, जो 1 सेकंड है। जो रन अपने शेड्यूल किए गए समय से एक सेकंड से ज़्यादा देर से शुरू होता है, उसे छोड़ दिया जाता है, और APScheduler देरी के साथ एक warning log करता है कि रन का समय छूट गया। रन आपकी सोच से ज़्यादा बार देर से शुरू होते हैं। ऐसा पूल जिसके सारे threads हल करने में व्यस्त हों, या ऐसा लैपटॉप जो किसी रन के दौरान sleep में रहा, इसके लिए काफ़ी है।

# pip install "apscheduler<4" capskip
from datetime import datetime

from apscheduler.schedulers.blocking import BlockingScheduler
from capskip import CapSkip

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


def solve_login_captcha():
    result = solver.recaptcha(sitekey="YOUR_SITEKEY", url="https://example.com/login")
    print("token:", result["code"][:20])


scheduler = BlockingScheduler()
scheduler.add_job(
    solve_login_captcha, "interval", minutes=10,
    next_run_time=datetime.now(),  # first run now, not ten minutes from now
    jitter=30,                     # add 0 to 30 s to each run
    max_instances=1,               # the default: a slow solve skips the next run
    coalesce=True,                 # the default: after a stall, run once
    misfire_grace_time=None,       # default is 1 s; None runs late instead of dropping
)
scheduler.start()

misfire_grace_time को None पर सेट करें, तो देर वाला रन फिर भी होता है, देर से ही सही। coalesce ऑप्शन को उसके डिफ़ॉल्ट True पर रहने दें, ताकि लंबी रुकावट के बाद कई छूटे हुए रन एक के बाद एक चलने के बजाय एक ही रन में सिमट जाएँ। हमने कंप्यूटर के sleep मोड की नकल करने के लिए 4 सेकंड के interval वाले शेड्यूलर को 14 सेकंड के लिए रोका। डिफ़ॉल्ट के साथ, छूटे रन की भरपाई वाला रन छोड़ दिया गया। misfire_grace_time को None पर सेट करने पर वह एक बार चला। साथ में coalesce ऑप्शन को False पर सेट करने पर, वह लगातार तीन बार चला, यानी एक लॉगिन के लिए तीन कैप्चा हल।

शुरू होते ही जॉब को तुरंत चलाने के लिए next_run_time पास करें। jitter ऑप्शन हर रन में उतने सेकंड तक की एक random देरी जोड़ता है, जिससे रिक्वेस्ट फैल जाती हैं। हर देरी पिछले रन के समय में जुड़ती है, जिसमें पहले से एक देरी शामिल है, इसलिए औसत अवधि interval और jitter के आधे के जोड़ के बराबर हो जाती है।

चरण 4: नाकामियों को होते ही देखें

जॉब में उठा exception शेड्यूलर को नहीं रोकता। APScheduler उसे ERROR स्तर पर log करता है, इस संदेश के साथ कि जॉब ने exception उठाया, और पूरे traceback के साथ, और जॉब अपना शेड्यूल बनाए रखती है। listener को यही events objects के रूप में मिलते हैं, ताकि आप उन्हें गिन सकें, उन पर alert भेज सकें या उन्हें जहाँ चाहें लिख सकें।

# pip install "apscheduler<4" capskip
import logging

from apscheduler.events import EVENT_JOB_ERROR, EVENT_JOB_MAX_INSTANCES, EVENT_JOB_MISSED
from apscheduler.schedulers.blocking import BlockingScheduler
from capskip import CapSkip

logging.basicConfig(level=logging.INFO)
solver = CapSkip(host="127.0.0.1", port=8080)


def solve_login_captcha():
    solver.recaptcha(sitekey="YOUR_SITEKEY", url="https://example.com/login")


def on_event(event):
    if event.code == EVENT_JOB_ERROR:
        logging.error("job %s failed: %r", event.job_id, event.exception)
    elif event.code == EVENT_JOB_MAX_INSTANCES:
        logging.warning("job %s still solving, this run skipped", event.job_id)
    else:
        logging.warning("job %s started too late, this run dropped", event.job_id)


scheduler = BlockingScheduler()
scheduler.add_listener(on_event, EVENT_JOB_ERROR | EVENT_JOB_MAX_INSTANCES | EVENT_JOB_MISSED)
scheduler.add_job(solve_login_captcha, "interval", minutes=10, id="login")
scheduler.start()

कैप्चा जॉब के लिए तीन events काफ़ी हैं। EVENT_JOB_ERROR खुद exception लेकर आता है, जैसे एक ApiException जिसका संदेश ERROR_CAPTCHA_UNSOLVABLE है। EVENT_JOB_MAX_INSTANCES हर स्किप हुए रन के लिए एक बार चलता है, जिससे अटका हुआ हल बढ़ती गिनती के रूप में दिखने लगता है। EVENT_JOB_MISSED देर से आए और छोड़ दिए गए हर रन के लिए चलता है।

पूरी APScheduler कैप्चा जॉब

यह script एक requests session से login पेज लाती है, sitekey और CSRF फ़ील्ड पढ़ती है, CapSkip से reCAPTCHA हल करती है और token के साथ फॉर्म पोस्ट करती है। यह CapSkip की errors को जॉब के अंदर ही पकड़ती है, इसलिए फेल हुआ हल बस एक warning होता है और अगला रन फिर कोशिश करता है।

# pip install "apscheduler<4" capskip requests
import functools
import logging
import re
import time
import types
from datetime import datetime

import requests
import capskip.api
from apscheduler.schedulers.background import BackgroundScheduler
from capskip import CapSkip, CapSkipError

LOGIN_URL = "https://example.com/login"
USERNAME, PASSWORD = "YOUR_USERNAME", "YOUR_PASSWORD"

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s")
log = logging.getLogger("login-job")


# The SDK's HTTP calls have no timeout; give them one so a stuck CapSkip
# cannot hang the job.
capskip.api.requests = types.SimpleNamespace(
    RequestException=requests.RequestException,
    post=functools.partial(requests.post, timeout=(5, 30)),
    get=functools.partial(requests.get, timeout=(5, 30)),
)
solver = CapSkip(host="127.0.0.1", port=8080)


def field(html, pattern):
    match = re.search(pattern, html)
    if not match:
        raise ValueError(f"not found on the login page: {pattern}")
    return match.group(1)


def login():
    with requests.Session() as session:
        page = session.get(LOGIN_URL, timeout=30)
        sitekey = field(page.text, r'data-sitekey="([^"]+)"')
        # The form's hidden CSRF field (Django names it csrfmiddlewaretoken).
        csrf = field(page.text, r'name="csrfmiddlewaretoken" value="([^"]+)"')
        try:
            token = solver.recaptcha(sitekey=sitekey, url=LOGIN_URL)["code"]
        except CapSkipError as exc:
            log.warning("CapSkip could not solve: %s: %s", type(exc).__name__, exc)
            return
        reply = session.post(LOGIN_URL, timeout=30, headers={"Referer": LOGIN_URL}, data={
            "csrfmiddlewaretoken": csrf,
            "username": USERNAME,
            "password": PASSWORD,
            "g-recaptcha-response": token,
        })
        # Many sites answer a failed login with 200 and the form again;
        # check for something only a logged-in page shows.
        if reply.ok:
            log.info("logged in (HTTP %s)", reply.status_code)
        else:
            log.warning("login refused (HTTP %s): %s", reply.status_code, reply.text[:200])


scheduler = BackgroundScheduler()
scheduler.add_job(
    login, "interval", minutes=10,
    next_run_time=datetime.now(),  # first run now, not ten minutes from now
    max_instances=1,               # never two solves for the same account at once
    coalesce=True,                 # after a stall, run once, not once per missed slot
    misfire_grace_time=None,       # a late run still runs instead of being dropped
)
scheduler.start()
try:
    while True:
        time.sleep(1)  # Ctrl+C lands here at once, on any Python version
except KeyboardInterrupt:
    log.info("stopping; a solve in flight finishes first")
    scheduler.shutdown()

यह BlockingScheduler के बजाय BackgroundScheduler और एक sleep लूप इस्तेमाल करती है, एक ऐसी वजह से जो सिर्फ़ Windows पर सामने आती है। Python 3.13 या उससे पुराने वर्ज़न पर BlockingScheduler के साथ, मुख्य थ्रेड एक ऐसे lock पर इंतज़ार करता है जिसे Ctrl+C बाधित नहीं कर सकता, इसलिए KeyboardInterrupt सिर्फ़ अगले शेड्यूल किए गए wake-up पर पहुँचता है। दस मिनट के interval के साथ, यह दस मिनट हो सकता है। Python 3.14 ने Windows पर इसे ठीक कर दिया, लेकिन लूप हर वर्ज़न पर काम करता है: Ctrl+C, time.sleep को तुरंत बाधित कर देता है।

रोकने पर भी चल रहे हल का इंतज़ार होता है। प्रोसेस के exit होने से पहले, Python पूल के worker threads के खत्म होने का इंतज़ार करता है, इसलिए चल रहा हल पहले पूरा होता है। ऐसा होता है चाहे आप scheduler.shutdown() कॉल करें या नहीं, और चरण 2 वाले timeout के साथ यह इंतज़ार ज़्यादा से ज़्यादा करीब साढ़े पाँच मिनट चलता है। दूसरा Ctrl+C इसे छोटा नहीं करता।

session फॉर्म को Referer header के साथ पोस्ट करता है, क्योंकि requests अपनी तरफ़ से कोई Referer नहीं भेजता और Django जैसे frameworks उसके बिना आई HTTPS फॉर्म पोस्ट ठुकरा देते हैं। session वह cookie भी रखता है जो CSRF फ़ील्ड से मेल खाती है। पोस्ट करने से ठीक पहले हल करें: reCAPTCHA token जारी होने के दो मिनट बाद एक्सपायर हो जाता है। Invisible widgets, Enterprise keys और दूसरे रूप यहाँ कवर किए गए हैं: Python में reCAPTCHA v2 हल करने की गाइड.

एक ही शेड्यूल पर कई अकाउंट

हर अकाउंट के लिए एक जॉब अच्छी तरह काम करती है, एक साझा CapSkip क्लाइंट के साथ, जिसे कई threads से इस्तेमाल करना सुरक्षित है। थ्रेड पूल को अकाउंट की संख्या जितना बड़ा रखें।

# pip install "apscheduler<4" capskip
import logging
from datetime import datetime

from apscheduler.executors.pool import ThreadPoolExecutor
from apscheduler.schedulers.blocking import BlockingScheduler
from capskip import CapSkip

logging.basicConfig(level=logging.INFO)
solver = CapSkip(host="127.0.0.1", port=8080)  # one client is safe to share across threads
ACCOUNTS = [f"user{i:02d}" for i in range(20)]


def login(account):
    token = solver.recaptcha(sitekey="YOUR_SITEKEY", url="https://example.com/login")["code"]
    logging.info("%s got a token %s", account, token[:12])


scheduler = BlockingScheduler(executors={"default": ThreadPoolExecutor(max_workers=20)})
for account in ACCOUNTS:
    scheduler.add_job(login, "interval", minutes=10, args=[account], id=account,
                      next_run_time=datetime.now(), misfire_grace_time=None)
scheduler.start()

डिफ़ॉल्ट पूल में 10 threads होते हैं। एक साथ 20 जॉब का समय होने पर, 10 रन ने थ्रेड का इंतज़ार किया, 7.8 सेकंड देर से शुरू हुए और डिफ़ॉल्ट 1 सेकंड के grace time के तहत छोड़ दिए गए। आधे अकाउंट कभी लॉग इन नहीं हुए, और यह बात सिर्फ़ missed warnings से पता चली। 20 threads का पूल, या None पर सेट misfire_grace_time, इसे ठीक कर देता है।

CapSkip की अपनी भी एक सीमा है। उसकी reCAPTCHA सेटिंग्स डिफ़ॉल्ट रूप से एक साथ 10 हल की अनुमति देती हैं, Max. Threads के तहत, और बाद के टास्क खाली थ्रेड का इंतज़ार करते हैं। 20 threads वाले हमारे टेस्ट में, 10 हल में 7.8 सेकंड लगे और बाकी 10 में 12.8, क्योंकि उन्होंने CapSkip के लिए 5 सेकंड इंतज़ार किया। अगर आपकी मशीन में क्षमता है तो CapSkip में Max. Threads बढ़ाएँ, और अगर नहीं है तो jitter से अकाउंट को फैला दें।

threads के बजाय asyncio के साथ

अगर आपका बाकी कोड async है, तो AsyncIOScheduler को AsyncCapSkip के साथ इस्तेमाल करें, जो SDK का native async क्लाइंट है। coroutine जॉब event loop पर चलती हैं, और max_instances बिल्कुल पहले की तरह काम करता है।

# pip install "apscheduler<4" capskip
import asyncio

from apscheduler.schedulers.asyncio import AsyncIOScheduler
from capskip import AsyncCapSkip

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


async def solve_login_captcha():
    result = await solver.recaptcha(sitekey="YOUR_SITEKEY", url="https://example.com/login")
    print("token:", result["code"][:20], "...")


async def main():
    scheduler = AsyncIOScheduler()
    scheduler.add_job(solve_login_captcha, "interval", minutes=10)
    scheduler.start()
    await asyncio.Event().wait()  # keep the event loop running


asyncio.run(main())

दो बातें अलग हैं। AsyncCapSkip, httpx पर बना है, जिसका डिफ़ॉल्ट timeout 5 सेकंड है, इसलिए अटका हुआ CapSkip चरण 2 वाले patch के बिना भी exception पर खत्म होता है: सबमिट अटकने पर 5 सेकंड बाद, और कोई पोल अटकने पर recaptchaTimeout पर। इससे आने वाले NetworkException का संदेश खाली हो सकता है, इसलिए exception का type भी log करें। और रोकने पर चल रहा हल इंतज़ार करने के बजाय cancel हो जाता है: तब जॉब अपने exception के रूप में एक CancelledError log करती है। traceback में उसके ऊपर एक NetworkException भी दिखता है, जो सिर्फ़ SDK का यह संकेत है कि नतीजा अभी तैयार नहीं है, कोई नेटवर्क नाकामी नहीं। Ctrl+C एक सेकंड के छोटे से हिस्से में exit कर देता है।

CapSkip को किसी दूसरी मशीन पर चलाना

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

# Replace the solver line with this when CapSkip runs in Server mode
# on another Windows machine on your network.
solver = CapSkip(apiKey="YOUR_API_KEY", host="192.168.1.50", port=8080)

अगर रास्ता इंटरनेट से होकर जाता है तो स्टैटिक पब्लिक IP इस्तेमाल करें, और API key validation चालू करें, क्लाइंट में भी वही key रखें। Server मोड शुरू होते ही CapSkip अपना पोर्ट हर पते के लिए खोल देता है, इसलिए पहुँच को सिर्फ़ अपेक्षित मशीनों तक सीमित रखने के लिए बाकी पतों के लिए Windows Firewall में एक block नियम जोड़ें। सॉल्वर अब भी आपकी अपनी Windows मशीन है, और हल अब भी बिना मीटर वाले हैं।

APScheduler में कैप्चा की आम errors

आप जो देखते हैंकारणफिक्स
Skipped: maximum number of running instances reached (1), कभी-कभारकिसी हल में interval से ज़्यादा समय लगाकुछ नहीं, या लंबा interval
हर रन पर वही skipped warning, और कोई हल नहींएक हल बिना timeout वाली HTTP कॉल पर अटका हैचरण 2 की तरह SDK को timeout दें
Run time of job was missed, कुछ सेकंड की देरी के साथरन 1 सेकंड से ज़्यादा देर से शुरू हुआ और छोड़ दिया गयाmisfire_grace_time को None पर सेट करें
आधे अकाउंट कभी नहीं चलतेडिफ़ॉल्ट पूल के 10 threads से ज़्यादा जॉब का समय एक साथ हुआपूल का आकार जॉब की संख्या के बराबर रखें
Ctrl+C कई मिनट तक कुछ नहीं करताWindows पर Python 3.13 या उससे पुराने वर्ज़न के साथ BlockingSchedulersleep लूप के साथ BackgroundScheduler इस्तेमाल करें, या Python 3.14
जॉब ने exception उठाया, ApiException ERROR_CAPTCHA_UNSOLVABLECapSkip यह टास्क हल नहीं कर सकाजॉब में CapSkipError को catch करें; अगला रन फिर कोशिश करता है
NetworkException: Read timed out. (read timeout=30)CapSkip ने टास्क स्वीकार किया और जवाब देना बंद कर दियाजाँचें कि CapSkip जवाब दे रहा है; अगला रन फिर कोशिश करता है
TimeoutException: timeout 300.0 exceededSDK के polling के दौरान CapSkip ने जवाब देना बंद कर दिया, या उसे बंद कर दिया गयाजाँचें कि CapSkip चल रहा है; अगला रन फिर कोशिश करता है
NetworkException: target machine ने कनेक्शन सक्रिय रूप से ठुकरा दियाCapSkip चल नहीं रहा, या host या port गलत हैCapSkip शुरू करें और पते को उसकी कनेक्शन सेटिंग्स से मिलाकर जाँचें
ModuleNotFoundError: No module named apscheduler.schedulersAPScheduler 4 इंस्टॉल है, जैसे कोई pre-releaseAPScheduler को वर्ज़न 4 से नीचे इंस्टॉल करें
साइट टोकन ठुकरा देती हैtoken एक्सपायर हो गया, या पोस्ट में cookie, कोई hidden फ़ील्ड या Referer छूट गयापोस्ट करने से ठीक पहले हल करें, उसी session में जिसने पेज लाया था

FAQ

क्या मैं इन जॉब को डेटाबेस में रख सकता हूँ?

हाँ, SQLAlchemyJobStore के साथ, लेकिन ध्यान दें कि क्या सेव होता है। जॉब का function reference के रूप में सहेजा जाता है, इसलिए उसे किसी importable module में होना चाहिए: lambda, ValueError के साथ फेल होता है, और जिस script को आपने __main__ के रूप में चलाया, उसका function तब हटा दिया जाता है जब कोई दूसरा प्रोग्राम store लोड करता है। CapSkip क्लाइंट को जॉब के args के बजाय किसी module variable से पास करें। args में रखा क्लाइंट अपने host, port और API key के साथ pickle होकर डेटाबेस में चला जाता है, और सेव की गई जॉब वह पता तब तक रखती है जब तक आप उसी id और True पर सेट replace_existing के साथ जॉब को फिर से नहीं जोड़ते। हर हाल में जॉब को एक id दें: उसके बिना, प्रोग्राम की हर शुरुआत एक और कॉपी जोड़ देती है।

recaptcha कॉल पर ही timeout क्यों न सेट करें?

क्योंकि SDK वहाँ timeout नहीं लेता। capskip 1.3.0 में solver.recaptcha को timeout argument पास करने पर TypeError आता है, और क्लाइंट की recaptchaTimeout सेटिंग सिर्फ़ polling लूप को सीमित करती है और सिर्फ़ रिक्वेस्ट के बीच जाँची जाती है। इनमें से कोई भी एक अकेली HTTP कॉल को सीमित नहीं करता, इसीलिए चरण 2 खुद requests की कॉल को patch करता है।

APScheduler के बजाय task queue कब बेहतर विकल्प है?

जब काम कोई दूसरा कोड बनाता है, या जब काम के हर हिस्से को सहेजकर रखना और प्रोसेस crash होने पर दोबारा चलाना ज़रूरी हो। APScheduler जॉब को आपके अपने प्रोग्राम के अंदर timer पर चलाता है। RQ जैसी queue हर जॉब को Redis में सहेजती है और उसे अलग workers में चलाती है, और उसका अपना जॉब timeout होता है जिसका ध्यान रखना पड़ता है, जैसा यहाँ समझाया गया है: RQ जॉब में कैप्चा हल करने की गाइड.

क्या शेड्यूलर CapSkip से अलग किसी मशीन पर चल सकता है?

हाँ। कनेक्शन सेटिंग्स में CapSkip को Server मोड पर कर दें, ताकि वह loopback के बजाय किसी नेटवर्क पते पर सुने, और वही पता क्लाइंट के host के रूप में पास करें। तब शेड्यूलर उसे उसी HTTP API के ज़रिए किसी भी ऐसी मशीन से कॉल करता है जो पोर्ट तक पहुँच सकती है। जिन पतों को उस तक नहीं पहुँचना चाहिए, उनके लिए Windows Firewall में एक block नियम जोड़ें। सॉल्वर अब भी आपके अपने हार्डवेयर पर चलता है, और कोई भी हल किसी third-party सर्विस से होकर नहीं जाता।

संक्षेप में

APScheduler कैप्चा जॉब को सिर्फ़ हल से ज़्यादा चाहिए। SDK की HTTP कॉल को timeout दें, क्योंकि max_instances के 1 होने पर एक अकेला अटका हल बाद का हर रन स्किप करवा देता है। misfire_grace_time को None पर सेट करें और coalesce चालू रखें, ताकि देर वाले रन छोड़े जाने के बजाय एक बार चलें। थ्रेड पूल का आकार अपनी जॉब के हिसाब से रखें, जॉब के अंदर CapSkipError को catch करें, और अगर आप Windows पर Python 3.13 या उससे पुराने वर्ज़न में इसे Ctrl+C से रोकते हैं, तो sleep लूप के साथ BackgroundScheduler इस्तेमाल करें। APScheduler 3.x यूज़र गाइड में यहाँ इस्तेमाल हुआ हर ऑप्शन दर्ज है।

हर दस मिनट पर चलने वाली जॉब महीने में चार हज़ार से ज़्यादा हल करती है। अपनी खुद की Windows मशीन पर चल रहे कैप्चा सॉल्वर से इन्हें चलाने का मतलब है कि यह संख्या कभी बिल में नहीं बदलती।