ब्राउज़र चलाए बिना Python में ALTCHA कैसे हल करें

आप Python में ALTCHA को सिर्फ़ एक HTTP क्लाइंट से हल कर सकते हैं, और कुछ नहीं चाहिए। ALTCHA पहचान नहीं, बल्कि proof of work है: साइट एक challenge जारी करती है, और जिसे अंदर जाना है उसे तब तक हैशिंग करनी पड़ती है जब तक वह संख्या न मिल जाए जो उसे संतुष्ट करे। कुछ भी देखना नहीं पड़ता, इसलिए इसमें न ब्राउज़र, न WebDriver और न user agent शामिल होता है, और उत्तर अनुमान से नहीं, गणना से निकलता है। इसका मतलब यह भी है कि हल निश्चित होता है। या तो वह संख्या मिलीसेकंड में मिल जाती है, या फिर खुद challenge गलत बना था या पहले ही समाप्त हो चुका था। CapSkip ने यह टाइप version 1.2.6 में जोड़ा और Python SDK इसे एक ही method के रूप में देता है।
आपको क्या चाहिए
- किसी Windows मशीन पर CapSkip 1.2.6 या उसके बाद का संस्करण। ALTCHA उसी रिलीज़ में आया था।
- Python 3.10 या उससे नया, और CapSkip पैकेज।
- widget वाले पेज का URL, साथ ही वह endpoint जहाँ से widget अपना challenge लाता है।
- solver के लिए एक पता। Local मोड 127.0.0.1 पर सुनता है और सिर्फ़ उसी डिवाइस को सेवा देता है; Server मोड आपके नेटवर्क पते या पब्लिक IP पर सुनता है ताकि कोई दूसरी मशीन उस तक पहुँच सके। दोनों यहाँ सेट होते हैं: कनेक्शन सेटिंग्स.
# pip install capskip pip install capskip requests
स्टेप 1: solve कॉल, और उसे किस पते पर लगाना है
पेज URL और challenge endpoint पास करें। CapSkip खुद challenge ले आता है, काउंटर मिलने तक हैशिंग करता है, और वह payload लौटा देता है जो फॉर्म चाहता है।
# pip install capskip
from capskip import CapSkip
solver = CapSkip(host="127.0.0.1", port=8080)
# CapSkip fetches the challenge, then brute-forces the counter.
result = solver.altcha(
url="https://example.com/signup",
challenge_url="https://example.com/altcha/challenge",
)
print(result["token"]) # base64 payload for the form field
print(result["number"]) # the counter that satisfied itउस डिक्शनरी की दो keys सिर्फ़ ALTCHA के लिए हैं। token key में base64 payload होता है, और number key में वह काउंटर होता है जिसने challenge हल किया। code key में वही स्ट्रिंग होती है जो token में है, इसलिए इनमें से कोई भी चल जाती है, लेकिन token key का नाम उसी फ़ील्ड पर है जिसमें वह जाता है।
काउंटर ALTCHA की दोनों पीढ़ियों के लिए रिपोर्ट होता है, जो सुनने में जितना लगता है उससे ज़्यादा काम का है, क्योंकि उनके payload इस बात पर सहमत नहीं कि वह रहता कहाँ है। लेगेसी payload उसे टॉप लेवल पर रखता है और proof-of-work v2 payload नहीं रखता, बल्कि उसे एक solution ऑब्जेक्ट के अंदर रखता है। CapSkip इसे अपने API रिस्पॉन्स के solution ऑब्जेक्ट से पढ़ता है, इसलिए साइट जिस भी पीढ़ी पर चले, आपको एक ही फ़ील्ड मिलता है।
loopback तभी तक सही है जब तक आप एक ही मशीन साझा कर रहे हैं
स्क्रिप्ट के जगह बदलते ही वही host आर्गुमेंट एकमात्र लाइन है जिसे बदलना पड़ता है। जब तक Python प्रोसेस और solver एक ही मशीन पर बैठे हैं, 127.0.0.1 सही है। जैसे ही स्क्रिप्ट किसी कंटेनर में, VPS पर, CI रनर पर या किसी मैनेज्ड प्लेटफ़ॉर्म पर शेड्यूल्ड जॉब के रूप में चलती है, loopback उसी माहौल की ओर इशारा करने लगता है और पहली ही कॉल NetworkException उठा देती है।
इसका जवाब Server मोड है। यह CapSkip को loopback के बजाय आपके नेटवर्क पते या पब्लिक IP पर सुनने पर लगा देता है, ताकि इनमें से कोई भी API के ज़रिए उसी solver तक पहुँच सके। अगर ट्रैफ़िक इंटरनेट से गुज़रता है तो स्टैटिक पब्लिक IP इस्तेमाल करें, साथ में ऐसा फ़ायरवॉल नियम जो सिर्फ़ अपेक्षित पतों तक सीमित हो। बाकी कुछ नहीं बदलता: solver अब भी आपके अपने हार्डवेयर पर चलता है और अब भी बिना मीटर वाला है, इसलिए सिर्फ़ उसका पता अलग होता है। वैल्यू को हार्डकोड करने के बजाय एनवायरनमेंट से पढ़ें, क्योंकि क्लाइंट पहले से ही CAPSKIP_HOST, CAPSKIP_PORT और CAPSKIP_API_KEY ढूँढता है।
स्टेप 2: challenge कहाँ से आता है, और उसे पास करने के दो तरीके
DevTools खोलें, Network टैब पर जाएँ, और पेज रीलोड करें। widget अपने challenge के लिए एक रिक्वेस्ट करता है, आमतौर पर ऐसे पाथ पर जिसमें altcha होता है। वही URL आप पास करते हैं। वह जो JSON लौटाता है वही challenge डॉक्यूमेंट है, और आप उसे इसके बजाय पास कर सकते हैं।
attribute का अनुमान लगाने के बजाय पेज सोर्स पढ़ें, क्योंकि widget की पीढ़ियों के बीच यह बदल चुका है।
| Widget पीढ़ी | वह attribute जो challenge का नाम बताता है |
|---|---|
| v1 और v2 | endpoint के लिए challengeurl, और जब challenge इनलाइन हो तब अलग challengejson attribute |
| v3 और उसके बाद | challenge, और वही एक attribute या तो URL स्वीकार करता है या challenge डेटा |
यहाँ widget की स्टाइलिंग बेमानी है। native, checkbox और switch, तीनों वैरिएंट वही payload सबमिट करते हैं और यह फ़र्क कभी solver तक नहीं पहुँचता, इसलिए पता लगाने जैसा कुछ है ही नहीं। challenge के फ़ील्ड-दर-फ़ील्ड विवरण के लिए ALTCHA अपना ही दस्तावेज़ रखता है: widget और सर्वर डॉक्युमेंटेशन.
जब आपके स्क्रैपर के पास डॉक्यूमेंट पहले से हो, तो उसे सीधे सौंप दें और फ़ेच पूरी तरह छोड़ दें। यह पैरामीटर एक डिक्शनरी लेता है और उसे आपके लिए सीरियलाइज़ कर देता है, या अगर आपके पास JSON स्ट्रिंग है तो वही ले लेता है।
# The document is already here, so no request goes out.
result = solver.altcha(
url="https://example.com/signup",
challenge_json={
"algorithm": "SHA-256",
"challenge": "YOUR_CHALLENGE_HASH",
"salt": "YOUR_SALT",
"signature": "YOUR_SIGNATURE",
"maxnumber": 1000000,
},
)दोनों पास करना मान्य है, और इनलाइन डॉक्यूमेंट जीतता है, क्योंकि फ़ेच करने पर वही चीज़ दोबारा मिलेगी जो आपने अभी दी थी। जब काम कतार में जमा होने लगे तब दोनों रास्तों में एक फ़र्क मायने रखता है: जो इनलाइन challenge पहले ही समाप्त हो चुका है उसे बेमतलब हैश करने के बजाय तुरंत मना कर दिया जाता है, जबकि endpoint देने पर solver नया challenge ला सकता है, अगर जॉब के इंतज़ार करते समय पहला वाला मर गया।
solver किन algorithm को कवर करता है
एक ही method दोनों पीढ़ियों को संभालता है। लेगेसी स्कीम SHA-1, SHA-256, SHA-384 और SHA-512 के साथ कवर होती है, और proof-of-work v2 PBKDF2 तथा iterative SHA के साथ। PBKDF2 वही डिफ़ॉल्ट है जिसकी सिफ़ारिश ALTCHA खुद करता है, इसलिए यही लाइव साइटों का बहुत बड़ा हिस्सा है।
Argon2id और scrypt अपवाद हैं। उन्हें आज़माने के बजाय मना कर दिया जाता है: इनमें से किसी की माँग करने वाला challenge करीब एक तिहाई सेकंड में ERROR_CAPTCHA_UNSOLVABLE बनकर लौट आता है और उसे कभी दोबारा नहीं आज़माया जाता, क्योंकि memory-hard फ़ंक्शन ऐसी चीज़ नहीं जिसे दोबारा कोशिश करने से ठीक किया जा सके।
स्टेप 3: token को बिना छेड़े वापस पोस्ट करें, उसके समाप्त होने से पहले
widget अपना payload altcha नाम के फॉर्म फ़ील्ड में सबमिट करता है। स्ट्रिंग को ठीक वैसे ही भेजें जैसे वह आई थी।
import requests
# No strip(), no re-encoding, no rebuilding the JSON.
r = requests.post("https://example.com/signup", data={
"email": "someone@example.com",
"altcha": result["token"],
})वह payload एक JSON डॉक्यूमेंट का base64 है, जिसके फ़ील्ड सर्वर के HMAC signature के दायरे में आते हैं, इसलिए कोई भी बदलाव इसे अमान्य कर देता है। उस पर strip कॉल करना, उसे डिकोड करके फिर से एनकोड करना, या keys को अलग क्रम में रखकर डिक्शनरी दोबारा बनाना, ये सब ऐसा token बनाते हैं जिसे साइट ठुकरा देती है। कुछ इंटीग्रेशन इसे फॉर्म फ़ील्ड के बजाय JSON बॉडी फ़ील्ड से पढ़ते हैं, इसलिए देखें कि पेज का अपना सबमिट क्या भेजता है और उससे मिलाएँ।
अगर कोई साइट ऐसे token को ठुकरा देती है जिसे आपका लॉग हल हुआ दिखाता है, तो token के बिगड़ने से ज़्यादा संभावना यही है कि challenge समाप्त हो चुका था। challenge की विंडो छोटी होती है और कुछ तो दो मिनट के अंदर बंद हो जाती हैं, और समाप्त हो चुका challenge एक सूखी वेरिफ़िकेशन फेल्योर बनकर लौटता है जो बिलकुल गलत जवाब जैसी दिखती है। लाना, हल करना और सबमिट करना, तीनों एक ही काम की इकाई में करें, और किसी व्यक्ति के फॉर्म भरने के दौरान token को कभी रोककर न रखें।
यहाँ आपको सीमित करने वाली चीज़ क्लाइंट के अपने पोलिंग timeout नहीं हैं, क्योंकि challenge की विंडो इन दोनों में से किसी के भी पूरा होने से बहुत पहले बंद हो जाती है। फिर भी यह जानना फ़ायदेमंद है कि लागू कौन सा होता है, क्योंकि ALTCHA इस बँटवारे के छोटे हिस्से पर बैठता है। यह ब्राउज़र सेशन नहीं, बल्कि CPU का काम है, इसलिए यह डिफ़ॉल्ट पोलिंग timeout इस्तेमाल करता है, लंबा reCAPTCHA वाला नहीं।
| कंस्ट्रक्टर ऑप्शन | डिफ़ॉल्ट | यह क्या कवर करता है |
|---|---|---|
| defaultTimeout | 120 सेकंड | ALTCHA और इमेज कैप्चा की पोलिंग |
| recaptchaTimeout | 300 सेकंड | reCAPTCHA, Turnstile और GeeTest की पोलिंग |
| pollingInterval | अधिकतम 5 सेकंड | पोलिंग 0.25 सेकंड से शुरू होती है और बढ़कर इस तक पहुँचती है |
स्टेप 4: पूरा बैच हल करना, बिना हर challenge को बासी हुए
Python अकेला ऐसा SDK है जिसमें async क्लाइंट एक अलग इम्प्लीमेंटेशन है: यहाँ AsyncCapSkip असली asyncio है। Node.js और .NET पैकेज में वही नाम साधारण क्लाइंट का उपनाम भर है, जिसके method पहले से ही asynchronous हैं, और PHP शुरू से आख़िर तक synchronous है।
अड़चन कॉनकरेंसी नहीं, ताज़गी है। सौ challenge पहले ले आना और फिर उन्हें हल करना गलत शक्ल है, क्योंकि बैच के आगे बढ़ते बढ़ते शुरुआती challenge समाप्त हो जाते हैं। इसके बजाय एक ही task के अंदर लाएँ और हल करें, हर पेज के लिए एक task, और फैन आउट को gather संभालने दें।
# pip install capskip
import asyncio
from capskip import AsyncCapSkip
async def solve_one(solver, page_url, challenge_url):
# One fresh challenge per page, fetched and solved together.
result = await solver.altcha(url=page_url, challenge_url=challenge_url)
return page_url, result["token"]
async def main():
solver = AsyncCapSkip()
pages = [
("https://example.com/signup", "https://example.com/altcha/challenge"),
("https://example.com/contact", "https://example.com/altcha/challenge"),
]
for page_url, token in await asyncio.gather(
*(solve_one(solver, p, c) for p, c in pages)
):
print(page_url, token[:24])
asyncio.run(main())proof of work CPU पर टिका है, इसलिए सीमा खुले कनेक्शन नहीं बल्कि कोर तय करते हैं, और अनुमान लगाने के बजाय उसी मशीन पर नाप लेना फ़ायदेमंद है जिस पर आप असल में चलाते हैं। थोक में हल करने का सामान्य तरीका, उन टाइप समेत जहाँ इंतज़ार हैशिंग का नहीं बल्कि ब्राउज़र सेशन का होता है, यहाँ मिलेगा: समानांतर में कैप्चा हल करने की गाइड.
पूरा चलने वाला उदाहरण
# pip install capskip
import os
import requests
from capskip import CapSkip, ApiException, NetworkException, TimeoutException
solver = CapSkip(host=os.environ.get("CAPSKIP_HOST", "127.0.0.1"), port=8080)
try:
result = solver.altcha(
url="https://example.com/signup",
challenge_url="https://example.com/altcha/challenge",
)
# Submit here, while the challenge is still fresh.
r = requests.post("https://example.com/signup", data={
"email": "someone@example.com",
"altcha": result["token"],
})
print(r.status_code, "solved with counter", result["number"])
except ApiException as exc:
# ERROR_CAPTCHA_UNSOLVABLE here means Argon2id or scrypt.
print("refused:", exc)
except NetworkException:
print("CapSkip is not answering on that host and port")
except TimeoutException:
print("gave up waiting, which on this type means something is wrong")बाकी टाइप भी वैसी ही शक्ल के हैं, बस method अलग है। reCAPTCHA v2, v3 और Enterprise के लिए एक कॉल है, Turnstile के लिए एक, GeeTest v3 के लिए एक और इमेज कैप्चा के लिए एक, और पूरी सूची यहाँ है: Python कैप्चा सॉल्वर पेज.
Node.js, PHP और .NET के पैकेज वही method नाम देते हैं। हर CapSkip SDK का ब्यौरा यहाँ है: SDK पेज.
आम errors और उनका मतलब
| आप जो देखते हैं | कारण | फिक्स |
|---|---|---|
| साइट से एक सूखी वेरिफ़िकेशन फेल्योर, ऐसे token पर जो साफ़ तरीके से हल हो चुका था | फॉर्म जाने से पहले ही challenge समाप्त हो गया | लाना, हल करना और सबमिट करना, तीनों एक ही फ़ंक्शन के अंदर करें |
| एक तिहाई सेकंड बाद ApiException में ERROR_CAPTCHA_UNSOLVABLE | challenge Argon2id या scrypt की माँग करता है | दोबारा कोशिश करने जैसा कुछ नहीं। वे दोनों जानबूझकर मना किए जाते हैं |
| कॉल पर एक ValidationException | कोई भी challenge पैरामीटर पास नहीं किया गया, या ऐसा पैरामीटर दिया गया जो ALTCHA स्वीकार नहीं करता | challenge endpoint या challenge डॉक्यूमेंट पास करें, और बाकी हटा दें |
| कोई हैशिंग होने से पहले ही एक NetworkException | CapSkip चल नहीं रहा, या host और port गलत हैं | CapSkip शुरू करें, फिर देखें कि उसे Local मोड में होना चाहिए या Server मोड में |
| token key पर एक KeyError | यह key सिर्फ़ ALTCHA के रिज़ल्ट में होती है | altcha method कॉल करें, या code key पढ़ें, जिसमें वही स्ट्रिंग होती है |
| बैच के शुरुआती solve फेल होते हैं और आख़िरी वाले पास हो जाते हैं | challenge पहले ही ला लिए गए और कतार में रहते हुए समाप्त हो गए | हर task के अंदर लाएँ, gather से पहले नहीं |
| ऐसा token जिसे साइट हर बार ठुकरा देती है | किसी चीज़ ने payload को ट्रिम कर दिया या दोबारा एनकोड कर दिया | स्ट्रिंग को बिना छेड़े आगे भेजें |
FAQ
क्या ALTCHA वाले पेज के लिए मुझे Selenium या Playwright चाहिए?
ALTCHA वाले हिस्से के लिए नहीं। challenge एक हैशिंग समस्या है और उत्तर एक स्ट्रिंग है जिसे आप फॉर्म फ़ील्ड में पोस्ट करते हैं, इसलिए requests या httpx काफ़ी है और solve मिलीसेकंड में पूरा हो जाता है। अगर पेज के बाकी हिस्से को ब्राउज़र चाहिए तो ब्राउज़र फिर भी रखना होगा, जैसे जब सेशन कुकी JavaScript से सेट होती है या फॉर्म क्लाइंट साइड रेंडर होता है। ऐसी सूरत में widget को चलाने की कोशिश करने के बजाय नेविगेशन के लिए ब्राउज़र रखें और token के लिए सीधे solver को कॉल करें।
क्या किसी होस्टेड प्लेटफ़ॉर्म पर चल रही स्क्रिप्ट solver तक पहुँच सकती है?
हाँ। कनेक्शन सेटिंग्स में Server मोड चालू करें ताकि CapSkip loopback के बजाय किसी नेटवर्क पते पर सुने, फिर CAPSKIP_HOST को उसी पर लगा दें। Docker कंटेनर, VPS, किसी मैनेज्ड प्लेटफ़ॉर्म पर शेड्यूल्ड जॉब और CI रनर, सब उसी तरह जुड़ते हैं, उसी HTTP API के ज़रिए। अगर रास्ता इंटरनेट से गुज़रता है, तो स्टैटिक पब्लिक IP और ऐसा फ़ायरवॉल नियम इस्तेमाल करें जो सिर्फ़ अपेक्षित पतों को ही अनुमति दे। दोनों ही सूरतों में solver आपके अपने हार्डवेयर पर ही चलता रहता है, इसलिए लाइसेंस और असीमित हल करने पर कोई असर नहीं पड़ता।
ALTCHA को हल करना reCAPTCHA के मुकाबले इतना तेज़ क्यों है?
क्योंकि दोनों अलग अलग चीज़ें माँगते हैं। reCAPTCHA और Turnstile इस बात का सबूत चाहते हैं कि भरोसेमंद इतिहास वाला ब्राउज़र मौजूद है, और इसमें असली सेशन तथा असली समय लगता है। ALTCHA सिर्फ़ इतना सबूत चाहता है कि कुछ CPU खर्च हुआ, इसलिए काम एक हैश लूप है जिसके रुकने की शर्त पहले से पता है। इसी वजह से उत्तर कोई फ़ैसला नहीं, बल्कि निश्चित होता है: एक संख्या है जो challenge को संतुष्ट करती है, और या तो वह मिल जाती है या challenge ही टूटा हुआ था। इसका सौदा यह है कि एक दो मिनट बाद वही संख्या बेकार हो जाती है।
क्या मैं एक ही token को कई रिक्वेस्ट में दोबारा इस्तेमाल कर सकता हूँ?
नहीं, और इसकी वजह साफ़ कहना ज़रूरी है। payload एक ख़ास challenge के लिए साइन किया जाता है, वह challenge एक ही बार जारी होता है, और सर्वर उसे ट्रैक करता है, इसलिए उसी token को दूसरी बार सबमिट करना ठीक वही रीप्ले है जिसे रोकने के लिए यह डिज़ाइन बना है। हर सबमिशन के लिए एक बार हल करें। यहाँ यह उस तरह किफ़ायती है जैसा किसी मीटर वाली सेवा पर नहीं होता, क्योंकि काम आपके अपने CPU के कुछ मिलीसेकंड है, कोई बिल वाली कॉल नहीं, इसलिए जिसे आप आसानी से दोबारा गिन सकते हैं उसे कैश करने की कोई वजह नहीं।
संक्षेप में
widget से challenge endpoint पढ़ें, उसे पेज URL के साथ उस एक ALTCHA method को दें, और token को बिना संपादित किए altcha नाम के फ़ील्ड में पोस्ट करें। लाना, हल करना और सबमिट करना, तीनों साथ रखें, क्योंकि challenge दो मिनट के अंदर मर सकता है और समाप्त हो चुके challenge की रिपोर्ट एक सादी वेरिफ़िकेशन फेल्योर के रूप में आती है। बैच के लिए async क्लाइंट से फैन आउट करें और हर challenge को पहले इकट्ठा करने के बजाय उसके अपने task के अंदर लाएँ। ERROR_CAPTCHA_UNSOLVABLE की उम्मीद सिर्फ़ Argon2id और scrypt से रखें।
- खुद challenge कैसे काम करता है: ALTCHA सॉल्वर पेज.
- Python पैकेज जो बाकी सभी method देता है: Python सॉल्वर पेज.
एक नतीजा ऐसा है जिसे अपनी retry logic में शामिल कर लेना चाहिए। लोकल रूप से चलते हुए, एक कैप्चा सॉल्वर हैशिंग उसी मशीन पर करता है जो पहले से आपकी अपनी है, इसलिए बेकार गया एक solve क्रेडिट के बजाय कुछ मिलीसेकंड खर्च करता है। लिहाज़ा बासी challenge पर सही प्रतिक्रिया यही है कि नया ले आएँ और दोबारा चलें।
