बिना ब्राउज़र के Node.js में Friendly Captcha कैसे हल करें

Node.js में Friendly Captcha हल करने के लिए, पेज fetch करें और वह जो cookies सेट करता है उन्हें सँभालकर रखें, cheerio से frc-captcha widget और उसका script टैग पढ़ें, उसी script से तय करें कि v1 है या v2, और sitekey, पेज URL, वर्ज़न और script का पता CapSkip के friendlyCaptcha method को पास करें। बदले में मिला token फॉर्म के अपने hidden फ़ील्ड के साथ पोस्ट करें: v2 पर frc-captcha-response में, v1 पर frc-captcha-solution में। यहाँ ज़्यादातर नाकामियों की दो वजहें होती हैं। built-in fetch कोई cookie नहीं रखता, इसलिए CSRF से सुरक्षित फॉर्म एक बिल्कुल सही token को भी ठुकरा देता है। और गलत वर्ज़न हल करने पर ऐसा token मिलता है जो सही दिखता है और बिना कुछ कहे फेल हो जाता है। CapSkip ने Friendly Captcha को 1.4.0 में जोड़ा, और यह गाइड पूरा काम आपकी तरफ़ बिना किसी ब्राउज़र के करती है।
आपको क्या चाहिए
- किसी Windows मशीन पर चल रहा CapSkip 1.4.0 या उसके बाद का संस्करण। Friendly Captcha उसी रिलीज़ में जोड़ा गया था, CaptchaFox और Capy Puzzle के साथ।
- Node.js 22 या उसके बाद का वर्ज़न, और capskip पैकेज का 1.3.0 या उसके बाद का वर्ज़न, जो friendlyCaptcha वाली पहली रिलीज़ है। HTML पढ़ने के लिए सैंपल cheerio भी इस्तेमाल करते हैं।
- उस पेज का URL जो widget दिखाता है। sitekey, script का पता और फ़ील्ड का नाम, तीनों उसी पेज के HTML से निकलते हैं।
- सॉल्वर के लिए एक पता। Local मोड केवल उसी डिवाइस के लिए 127.0.0.1 पर जवाब देता है; Server मोड आपके नेटवर्क पते या पब्लिक IP पर सुनता है ताकि किसी दूसरी मशीन पर चल रही script API के ज़रिए उसे कॉल कर सके। दोनों कनेक्शन सेटिंग्स में मिलते हैं, और स्विच कब करना है, यह स्टेप 4 में बताया गया है।
# npm install capskip cheerio npm install capskip cheerio
सैंपल top-level await वाले ES modules हैं। इन्हें .mjs एक्सटेंशन के साथ सेव करें, या अपनी package.json में type फ़ील्ड को module कर दें, क्योंकि npm init अब वहाँ commonjs लिखता है, और फिर import वैसे ही काम करता है जैसा दिखाया गया है। अगर आपका प्रोजेक्ट CommonJS है, तो capskip पैकेज require से भी लोड होता है।
स्टेप 1: पेज fetch करें और उसकी cookies रखें
Node का fetch एक अच्छा HTTP क्लाइंट है, बस इसमें एक कमी है जो यहाँ मायने रखती है: इसमें कोई cookie jar नहीं है। हर कॉल बिल्कुल नए सिरे से शुरू होती है, इसलिए फॉर्म पोस्ट करने तक वह session cookie गायब हो चुकी होती है जो पेज ने सेट की थी। जो साइट अपने CSRF token को उसी session से जोड़ती है, वह फिर पोस्ट को ठुकरा देती है, अक्सर 403 के साथ, चाहे कैप्चा token कितना भी सही हो। Python का requests.Session यह समस्या छिपा लेता है, लेकिन Node में cookies आपको खुद साथ लेकर चलनी होती हैं।
इसके लिए सही टूल Headers.getSetCookie() है, जो हर Set-Cookie लाइन को अलग-अलग लौटाता है। सादी headers.get कॉल यह काम नहीं कर सकती, क्योंकि वह लाइनों को कॉमा से जोड़ देती है, और cookie की expiry तारीखों में भी कॉमा होते हैं।
// npm install capskip cheerio
import * as cheerio from "cheerio";
const PAGE_URL = "https://example.com/signup";
// fetch() keeps no cookies between calls, so carry them by hand.
const jar = new Map();
function remember(res) {
for (const line of res.headers.getSetCookie()) {
const pair = line.split(";")[0];
const eq = pair.indexOf("=");
jar.set(pair.slice(0, eq).trim(), pair.slice(eq + 1).trim());
}
}
const cookieHeader = () => [...jar].map(([k, v]) => `${k}=${v}`).join("; ");
const page = await fetch(PAGE_URL);
remember(page);
const $ = cheerio.load(await page.text());यह jar जानबूझकर छोटा रखा गया है। यह नाम और वैल्यू रखता है, और paths व expiry को नज़रअंदाज़ करता है, और एक साइट और एक फॉर्म के लिए इतना ही काफ़ी है।
स्टेप 2: widget पढ़ें, v1 या v2 चुनें, और friendlyCaptcha कॉल करें
दोनों वर्ज़न एक ही element रेंडर करते हैं: frc-captcha class वाला एक div, जिस पर data-sitekey attribute होता है, इसलिए सिर्फ़ widget से प्रोटोकॉल के बारे में कुछ पता नहीं चलता। यह script टैग बताता है। v2 साइट @friendlycaptcha/sdk पैकेज लोड करती है, जिसकी फ़ाइल site.min.js है, और v1 साइट friendly-challenge लोड करती है, जिसकी फ़ाइल widget.module.min.js है। Cheerio कुछ ही लाइनों में दोनों निकाल लेता है:
const widget = $(".frc-captcha").first();
const form = widget.closest("form");
// Every script except nomodule fallbacks, as full addresses.
const scripts = $("script[src]").not("[nomodule]")
.map((_, el) => new URL($(el).attr("src"), PAGE_URL).href)
.get();खुद होस्ट किए गए widgets के लिए पेज URL को base के रूप में पास करना ज़रूरी है। यह /vendor/v2/site.min.js जैसे relative src को पूरे पते में बदल देता है, और v2 पर solve चलाने के लिए CapSkip ठीक वही script अपने ब्राउज़र में लोड करता है। सिर्फ़ पाथ कभी लोड नहीं होता।
CapSkip वर्ज़न एक तय क्रम में चुनता है और पहले जवाब पर रुक जाता है: पहले आपका पास किया वर्ज़न, फिर moduleScript के रूप में पास किया गया script का पता, फिर डिफ़ॉल्ट v1। फँसाने वाली चीज़ यही डिफ़ॉल्ट है। /assets/app.4f2a.js जैसी bundled script से CapSkip को कुछ पता नहीं चलता, इसलिए वह v1 हल कर देता है और v2 साइट हर token ठुकरा देती है। इसलिए फ़ैसला अपने कोड में करें, जहाँ आप अनुमान लगाने से मना कर सकते हैं:
const V2_FILES = ["site.min.js", "site.compat.min.js"];
const V1_FILES = ["widget.module.min.js", "widget.min.js", "widget.polyfilled.min.js"];
function friendlyVersion(scripts, widget) {
// Package names are unambiguous, so check them first.
for (const src of scripts) {
if (src.includes("@friendlycaptcha/sdk")) return { version: "v2", script: src };
if (src.includes("friendly-challenge")) return { version: "v1", script: src };
}
// Self-hosted builds usually keep the file name. Themes ship their own
// site.min.js too, so only trust a path that says friendly.
for (const src of scripts.filter((s) => s.toLowerCase().includes("friendly"))) {
const file = new URL(src).pathname.split("/").pop();
if (V2_FILES.includes(file)) return { version: "v2", script: src };
if (V1_FILES.includes(file)) return { version: "v1", script: src };
}
// Last resort: v2 and v1 name their widget options differently.
if (widget.is("[data-api-endpoint], [data-form-field-name]")) return { version: "v2" };
if (widget.is("[data-puzzle-endpoint], [data-solution-field-name]")) return { version: "v1" };
throw new Error("v1 or v2? Read the page and set it by hand.");
}फ़ाइल नाम वाली जाँच सिर्फ़ उन paths पर भरोसा करती है जिनमें friendly लिखा हो, क्योंकि कोई theme अपनी खुद की site.min.js लोड कर सकती है जिसका widget से कोई लेना-देना नहीं।
फिर कॉल करें। friendlyCaptcha तीन चीज़ें लेता है: sitekey, पेज URL और एक options object। SDK कुछ भी भेजने से पहले undefined वैल्यू हटा देता है, इसलिए जो attribute पेज पर नहीं है, वह बस रिक्वेस्ट से बाहर रह जाता है:
import { CapSkip } from "capskip";
const solver = new CapSkip({ host: "127.0.0.1", port: 8080 });
const { version, script } = friendlyVersion(scripts, widget);
const result = await solver.friendlyCaptcha(widget.attr("data-sitekey"), PAGE_URL, {
version,
moduleScript: script,
// EU sites: data-api-endpoint="eu" (v2) or data-puzzle-endpoint (v1)
apiServer: widget.attr("data-api-endpoint") ?? widget.attr("data-puzzle-endpoint"),
});
console.log(result.token.slice(0, 40)); // v2 tokens start with AQQA.apiServer वाली लाइन Friendly Captcha के EU endpoint पर चलने वाली साइटों के लिए है। global endpoint भी EU sitekey के लिए token बना देता है, इसलिए वहाँ हल करने पर नाकामी सिर्फ़ साइट की अपनी जाँच में सामने आती है, बिल्कुल गलत वर्ज़न वाली चुपचाप नाकामी की तरह। result.token वह स्ट्रिंग है जिसे सबमिट करना है, result.code में वही स्ट्रिंग होती है, और result.captchaId इस job के लिए CapSkip की id है। v1, v2, 1 या 2 के अलावा कोई और वर्ज़न, खाली sitekey, या ऐसा ऑप्शन जिसे यह method नहीं जानता, आपकी मशीन से कोई भी रिक्वेस्ट निकलने से पहले ही ValidationException throw कर देता है।
स्टेप 3: token को फॉर्म के अपने फ़ील्ड के साथ पोस्ट करें
token वाला फ़ील्ड आपके fetch किए HTML में नहीं होता, क्योंकि widget script उसे ब्राउज़र में बनाती है। इसलिए उसे आप खुद जोड़ते हैं, उसी नाम से जो वर्ज़न और widget इस्तेमाल करते हैं। फॉर्म में बाकी जो कुछ भी जाता है, hidden CSRF token समेत, वह फॉर्म से ही आता है:
// A renamed field wins; otherwise the default for the version.
const field = widget.attr("data-form-field-name")
?? widget.attr("data-solution-field-name")
?? (version === "v2" ? "frc-captcha-response" : "frc-captcha-solution");
// The form's own fields, hidden CSRF token included.
const body = new URLSearchParams(
form.serializeArray().map((f) => [f.name, f.value]),
);
body.set("email", "YOUR_EMAIL");
body.set(field, result.token);
const res = await fetch(new URL(form.attr("action") || PAGE_URL, PAGE_URL), {
method: "POST",
// Browsers send the page as Referer; some servers refuse a post without it.
headers: { cookie: cookieHeader(), referer: PAGE_URL },
body,
});
console.log(res.status);serializeArray वे फ़ील्ड इकट्ठा करता है जो कोई ब्राउज़र भेजता, और इसी तरह CSRF token आपको उसका नाम लिए बिना ही सर्वर तक वापस पहुँचता है। यह submit बटन को छोड़ देता है, इसलिए अगर साइट बटन के नाम की जाँच करती है, तो उसे body.set से जोड़ें। पेज URL को Referer के रूप में भी भेजें: fetch न Referer header भेजता है न Origin, और कुछ frameworks, जिनमें Django भी है, ऐसी HTTPS फॉर्म पोस्ट को मना कर देते हैं जिसमें इन दोनों में से कोई न हो, चाहे cookie और token कितने भी सही हों। URLSearchParams बॉडी से fetch एक सामान्य फॉर्म पोस्ट भेजता है और content type आपके लिए खुद सेट कर देता है। v2 token करीब छह किलोबाइट का होता है, इसलिए उसकी जगह उसी बॉडी में है, query string में कभी नहीं, जबकि v1 token डॉट से अलग किए गए चार हिस्सों वाला और कुछ सौ कैरेक्टर का होता है। इसे बिना छेड़े जस का तस आगे भेजें।
दो मामलों में DevTools में देखना पड़ता है। v1 पर, अगर data-solution-field-name को एक अकेले हाइफ़न पर सेट किया गया है, तो इसका मतलब है कि widget कोई फ़ील्ड लिखता ही नहीं, और साइट की अपनी script token को किसी और तरीके से भेजती है। कुछ साइटें फॉर्म सबमिट करने के बजाय JavaScript से JSON भी पोस्ट करती हैं। दोनों ही मामलों में, एक बार हाथ से सबमिट करें और पेज असल में जो रिक्वेस्ट भेजता है, उसे कॉपी करें।
हर token एक ही सबमिट के लिए है। Friendly Captcha का वेरिफ़िकेशन ऐसे response को ठुकरा देता है जो पहले इस्तेमाल हो चुका हो या expire हो चुका हो, इसलिए हर फॉर्म के लिए दोबारा हल करें।
स्टेप 4: एक साथ कई फॉर्म, और सॉल्वर कहाँ चलता है
Node में हर CapSkip method पहले से एक Promise लौटाता है, और AsyncCapSkip उसी class का बस दूसरा नाम है, इसलिए concurrency सीधी-सादी JavaScript है। आपको बस एक सीमा चाहिए। Promise.all से सौ कॉल एक साथ भेजें, तो CapSkip अपनी Friendly Captcha Max. Threads सेटिंग से ऊपर की हर कॉल को queue में डाल देता है। यह सेटिंग डिफ़ॉल्ट रूप से 10 है, और queue में कोई task खाली thread के लिए 250 सेकंड तक इंतज़ार करता है, उसके बाद CapSkip उसे फेल कर देता है। एक छोटा worker pool एक समय में ज़्यादा से ज़्यादा दस कॉल चालू रखता है:
// Run fn over items with at most `limit` in flight. A failed item
// becomes its Error instead of rejecting the whole batch.
async function mapLimited(items, limit, fn) {
const results = new Array(items.length);
let next = 0;
async function worker() {
while (next < items.length) {
const i = next++;
results[i] = await fn(items[i]).catch((err) => err);
}
}
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
return results;
}
// jobs: { sitekey, url, version, script, apiServer } objects from Step 2
const tokens = await mapLimited(jobs, 10, (job) =>
solver.friendlyCaptcha(job.sitekey, job.url, {
version: job.version,
moduleScript: job.script,
apiServer: job.apiServer,
}).then((r) => r.token));मानकर चलें कि solve का समय हर बार अलग होगा। Friendly Captcha हर रिक्वेस्ट के लिए काम की मात्रा तय करता है और जिन पतों को वह बहुत देख चुका है उनके लिए इसे बढ़ा देता है, और CapSkip हर v2 widget को असली ब्राउज़र में हल करता है। इसीलिए यह method 120 सेकंड वाले defaultTimeout तक नहीं, बल्कि recaptchaTimeout तक पोलिंग करता है, जो डिफ़ॉल्ट रूप से 300 सेकंड है। CapSkip की अपनी घड़ी भी चलती है: उसकी Friendly Captcha सेटिंग्स में किसी solve को फेल किए जाने से पहले 120 सेकंड का Row Timeout मिलता है। किसी एक कॉल के लिए SDK का इंतज़ार बदलने के लिए आप options में timeout पास कर सकते हैं। SDK को thread के इंतज़ार और solve, दोनों के कुल समय से ज़्यादा टिकना होगा, और CapSkip की डिफ़ॉल्ट सेटिंग्स पर यह समय 370 सेकंड तक पहुँच सकता है। इसलिए pool को Max. Threads के बराबर रखें या लंबा timeout पास करें, और जब भी Row Timeout बढ़ाएँ, timeout को फिर से बढ़ाएँ, वरना कॉल TimeoutException पर खत्म होगी जबकि CapSkip अब भी काम कर रहा होगा। poll interval भी हर कॉल के लिए सेट किया जा सकता है, लेकिन सिर्फ़ snake case में polling_interval के रूप में। camelCase वाला pollingInterval constructor का ऑप्शन है, और हर कॉल वाले ऑप्शन के रूप में यह ValidationException throw करता है। अगर लंबे रन के दौरान solve धीमे होते जाएँ, तो आमतौर पर वजह एक ही पते पर बढ़ती कठिनाई होती है, इसलिए type और uri keys वाला एक प्रॉक्सी object पास करें, या CapSkip में प्रॉक्सी पूल सेट करें।
सैंपल 127.0.0.1 इस्तेमाल करते हैं, क्योंकि जब Node और सॉल्वर एक ही मशीन पर हों तो यही सही है। script को कहीं और चलाएँ, जैसे किसी VPS, कंटेनर या CI रनर पर, तो loopback उसी मशीन की ओर इशारा करता है, और पहली ही कॉल ECONNREFUSED के साथ NetworkException throw करती है। CapSkip को Server मोड पर कर दें, तो वह आपके नेटवर्क पते या पब्लिक IP पर सुनता है, ताकि इनमें से कोई भी उसी API के ज़रिए उस तक पहुँच सके। जब रास्ता इंटरनेट से होकर जाए तो स्टैटिक पब्लिक IP इस्तेमाल करें, साथ में अपेक्षित पतों के लिए फ़ायरवॉल नियम रखें। यह अब भी आपका अपना हार्डवेयर है, और अब भी बिना मीटर वाला है। क्लाइंट एनवायरनमेंट वेरिएबल खुद नहीं पढ़ता, इसलिए CAPSKIP_HOST को अपने कोड में पढ़ें और क्लाइंट को पास करें, जैसा नीचे दिया गया पूरा उदाहरण करता है।
पूरा चलने वाला उदाहरण
यह रहा पूरा रन एक ही फ़ाइल में: सिर्फ़ एक पेज URL से शुरू करके Node.js में Friendly Captcha हल करने का सबसे छोटा पूरा तरीका।
// npm install capskip cheerio
// solve-friendly.mjs: run with node solve-friendly.mjs
import * as cheerio from "cheerio";
import { CapSkip, CapSkipError, ValidationException } from "capskip";
const PAGE_URL = "https://example.com/signup";
const V2_FILES = ["site.min.js", "site.compat.min.js"];
const V1_FILES = ["widget.module.min.js", "widget.min.js", "widget.polyfilled.min.js"];
const jar = new Map();
function remember(res) {
for (const line of res.headers.getSetCookie()) {
const pair = line.split(";")[0];
const eq = pair.indexOf("=");
jar.set(pair.slice(0, eq).trim(), pair.slice(eq + 1).trim());
}
}
const cookieHeader = () => [...jar].map(([k, v]) => `${k}=${v}`).join("; ");
function friendlyVersion(scripts, widget) {
for (const src of scripts) {
if (src.includes("@friendlycaptcha/sdk")) return { version: "v2", script: src };
if (src.includes("friendly-challenge")) return { version: "v1", script: src };
}
for (const src of scripts.filter((s) => s.toLowerCase().includes("friendly"))) {
const file = new URL(src).pathname.split("/").pop();
if (V2_FILES.includes(file)) return { version: "v2", script: src };
if (V1_FILES.includes(file)) return { version: "v1", script: src };
}
if (widget.is("[data-api-endpoint], [data-form-field-name]")) return { version: "v2" };
if (widget.is("[data-puzzle-endpoint], [data-solution-field-name]")) return { version: "v1" };
throw new Error("v1 or v2? Read the page and set it by hand.");
}
const solver = new CapSkip({
apiKey: process.env.CAPSKIP_API_KEY ?? "capskip",
host: process.env.CAPSKIP_HOST ?? "127.0.0.1",
port: Number(process.env.CAPSKIP_PORT ?? 8080),
});
const page = await fetch(PAGE_URL);
remember(page);
const $ = cheerio.load(await page.text());
const widget = $(".frc-captcha").first();
if (!widget.attr("data-sitekey")) {
throw new Error("No frc-captcha widget in the HTML; it may be built by JS.");
}
const form = widget.closest("form");
const scripts = $("script[src]").not("[nomodule]")
.map((_, el) => new URL($(el).attr("src"), PAGE_URL).href).get();
const { version, script } = friendlyVersion(scripts, widget);
let result;
try {
result = await solver.friendlyCaptcha(widget.attr("data-sitekey"), PAGE_URL, {
version,
moduleScript: script,
apiServer: widget.attr("data-api-endpoint") ?? widget.attr("data-puzzle-endpoint"),
});
} catch (err) {
if (err instanceof ValidationException) throw new Error(`not sent: ${err.message}`);
if (err instanceof CapSkipError) throw new Error(`solve failed: ${err.name}: ${err.message}`);
throw err;
}
const field = widget.attr("data-form-field-name")
?? widget.attr("data-solution-field-name")
?? (version === "v2" ? "frc-captcha-response" : "frc-captcha-solution");
// Post wherever the form's action points, with its other fields.
const body = new URLSearchParams(form.serializeArray().map((f) => [f.name, f.value]));
body.set("email", "YOUR_EMAIL");
body.set(field, result.token);
const res = await fetch(new URL(form.attr("action") || PAGE_URL, PAGE_URL), {
method: "POST",
// Browsers send the page as Referer; some servers refuse a post without it.
headers: { cookie: cookieHeader(), referer: PAGE_URL },
body,
});
console.log(res.status, version, field);वर्ज़न एक बार तय होता है और दो बार इस्तेमाल होता है, solve के लिए और फ़ील्ड के नाम के लिए, इसलिए दोनों में कभी मतभेद नहीं हो सकता। जब cheerio को कोई widget नहीं मिलता, तो आमतौर पर पेज उसे JavaScript से बनाता है, और आपको sitekey रेंडर हुए पेज से या उसे बनाने वाली script से लेना होगा। raw endpoint जो भी पैरामीटर स्वीकार करता है, वे सब यहाँ दर्ज हैं: Friendly Captcha API रेफ़रेंस.
आम errors और उनका मतलब
| आप जो देखते हैं | कारण | फिक्स |
|---|---|---|
| CapSkip से मिले token के साथ भी 403 या CSRF error | पोस्ट पेज की cookies, उसके hidden फ़ील्ड या Referer के बिना गई | स्टेप 1 वाला cookie header और Referer भेजें, और बॉडी serializeArray से बनाएँ |
| साइट token ठुकरा देती है, और कोई दूसरा error नहीं आता | गलत वर्ज़न हल हुआ, अक्सर डिफ़ॉल्ट वाला v1 | स्टेप 2 की तरह वर्ज़न कोड में तय करें और उसे पास करें |
| ऐसी साइट पर token ठुकराया जाता है जिसके widget पर data-api-endpoint (v2) या data-puzzle-endpoint (v1) है | token global endpoint से आया, जबकि साइट EU endpoint इस्तेमाल करती है | attribute की वैल्यू को apiServer के रूप में पास करें |
| widget को खुद होस्ट करने वाली साइट पर v2 solve फेल हो जाता है | moduleScript एक relative पाथ था, इसलिए widget कभी लोड ही नहीं हुआ | new URL से src को पेज URL के आधार पर पूरे पते में बदलें |
| pollingInterval का नाम लेता हुआ ValidationException | हर कॉल वाले ऑप्शन की स्पेलिंग polling_interval है | हर कॉल में snake case इस्तेमाल करें, या pollingInterval को constructor पर सेट करें |
| await या import को लेकर SyntaxError | फ़ाइल CommonJS के रूप में चलती है | फ़ाइल को .mjs एक्सटेंशन दें, या पैकेज का type module पर सेट करें |
| TypeError: getSetCookie is not a function | Node.js 18.15 या 19.7 से पुराना | Node.js 22 या उसके बाद के वर्ज़न पर अपग्रेड करें |
| हर बार कुछ ही सेकंड में ERROR_CAPTCHA_UNSOLVABLE वाला ApiException | Friendly Captcha sitekey या पेज के origin को मना कर देता है, या खाते में v2 या EU endpoint चालू नहीं है | sitekey, पेज URL और apiServer जाँचें; दोबारा कोशिश करने से फ़ायदा नहीं होगा |
| दो मिनट या उससे ज़्यादा समय बाद ERROR_CAPTCHA_UNSOLVABLE वाला ApiException | solve CapSkip के 120 सेकंड वाले Row Timeout से आगे चला गया, या खाली thread के इंतज़ार में 250 सेकंड वाले Wait Timeout से ज़्यादा रुका | pool को Max. Threads के बराबर रखें, प्रॉक्सी जोड़ें, या Friendly Captcha सेटिंग्स में Row Timeout बढ़ाएँ |
| 300 सेकंड बाद TimeoutException | task खाली thread के इंतज़ार के बाद हल हुआ, और कुल मिलाकर SDK की पोलिंग से ज़्यादा समय लगा | pool को Max. Threads के बराबर रखें, या लंबा timeout पास करें |
| ECONNREFUSED के साथ NetworkException | CapSkip चल नहीं रहा, या host और port गलत हैं | CapSkip शुरू करें, फिर देखें कि Local मोड चाहिए या Server मोड |
FAQ
क्या Node.js में Friendly Captcha हल करने के लिए मुझे Puppeteer या Playwright चाहिए?
नहीं। आपकी script को सिर्फ़ HTML चाहिए, जिसे fetch ले आता है। ब्राउज़र का काम CapSkip के अंदर होता है, जो v2 widgets को अपने ब्राउज़र में और v1 puzzles को सीधे हल करता है। अगर आपकी script किसी और वजह से पहले से ब्राउज़र चला रही है, तो वही कॉल काम करती है: बस sitekey और script का पता fetch किए गए HTML के बजाय लाइव पेज से पढ़ें।
क्या यह TypeScript में काम करता है?
हाँ। capskip पैकेज अपनी type definitions के साथ आता है, और options object में यहाँ इस्तेमाल हुए camelCase नाम और snake_case वाले API नाम, दोनों typed हैं। एक पेच है: साझा result type, token को optional घोषित करता है और अब भी उसे ALTCHA फ़ील्ड बताता है, क्योंकि एक ही result shape हर method को कवर करता है। friendlyCaptcha इसे हमेशा भरता है, इसलिए result.token पर non-null assertion सुरक्षित है। cheerio का attr भी string या undefined लौटाता है, इसलिए strict mode में sitekey और script के src पर भी इसी तरह assertion लगाएँ।
क्या VPS पर चल रहा Node ऐप मेरे Windows PC पर चल रहे सॉल्वर का इस्तेमाल कर सकता है?
हाँ। कनेक्शन सेटिंग्स में CapSkip को Server मोड पर कर दें ताकि वह loopback के बजाय किसी नेटवर्क पते पर सुने, जहाँ ऐप चलता है वहाँ CAPSKIP_HOST सेट करें, और उसे क्लाइंट को पास करें, जैसा पूरा उदाहरण करता है। VPS, कंटेनर और होस्टेड रनर, सब उसी HTTP API के ज़रिए जुड़ते हैं। जब रास्ता इंटरनेट से होकर जाए तो फ़ायरवॉल नियम के साथ स्टैटिक पब्लिक IP इस्तेमाल करें। सॉल्वर आपके अपने हार्डवेयर पर ही रहता है, इसलिए ज़्यादा solve चलाने से आपका खर्च कभी नहीं बदलता।
यह Python गाइड से कैसे अलग है?
endpoint, ऑप्शंस और नतीजा हर CapSkip SDK में एक जैसे हैं; उनके आस-पास का कोड नहीं। Python का requests.Session आपके लिए cookies रखता है, जबकि Node के fetch को स्टेप 1 वाला छोटा jar चाहिए। Python का AsyncCapSkip एक अलग asynchronous क्लाइंट है, जबकि Node में हर method पहले से asynchronous है, इसलिए जोड़ने लायक इकलौती चीज़ यह सीमा है कि एक साथ कितने चलें। इस वर्कफ़्लो का Python वर्ज़न यहाँ है: Python Friendly Captcha गाइड.
संक्षेप में
Node.js में Friendly Captcha हल करने के लिए, पेज fetch करें और getSetCookie से उसकी cookies रखें, फिर cheerio से frc-captcha element और उसके script टैग पढ़ें, और हर src को पूरे पते में बदलें। v1 या v2 पैकेज या फ़ाइल के नाम से तय करें, या widget के अपने attributes से, और अगर दोनों में से कोई न बताए तो error throw करें। friendlyCaptcha को sitekey, पेज URL, उस वर्ज़न और script के पते के साथ कॉल करें, और EU साइटों के लिए apiServer भी दें। token को एक बार, बॉडी में, फॉर्म के अपने फ़ील्ड के साथ, उस फ़ील्ड में पोस्ट करें जिसका नाम वर्ज़न तय करता है।
- यह टाइप कैसे काम करता है और सॉल्वर क्या-क्या कवर करता है: Friendly Captcha सॉल्वर पेज.
- Node पैकेज जो बाकी सभी कैप्चा टाइप हल करता है: Node.js कैप्चा सॉल्वर पेज.
Friendly Captcha अपनी कठिनाई की कीमत पैसे में नहीं, CPU समय में वसूलता है, इसलिए किसी व्यस्त दिन की लागत बस आपकी अपनी मशीन पर लगा solve का समय है, इसके अलावा कुछ नहीं। एक लोकल captcha सॉल्वर चलाएँ, और तब आपकी सीमा सिर्फ़ वह मशीन और उसे दिए गए threads हैं।
