So lösen Sie GeeTest v3 in Node.js und senden es zurück

solve geetest in node.js - How to Solve GeeTest v3 in Node.js and Post It Back

Fast jede Captcha-Integration geht davon aus, dass ein Token hineingeht und eines herauskommt. GeeTest funktioniert nicht so. Eine Lösung liefert drei Werte, die gemeinsam übermittelt werden müssen, und die Challenge, mit der Sie begonnen haben, läuft etwa eine Minute nach dem Abruf ab. Keiner der beiden Fehler erzeugt eine nützliche Fehlermeldung, weshalb GeeTest den Ruf hat, kniffelig zu sein.

Zwei Eingaben, eine davon vergänglich

WertLifetime
gtPro Website statisch. Cachen Sie ihn nach Belieben
challengeZum einmaligen Gebrauch, nach etwa 60 Sekunden ungültig

Beide stammen vom eigenen GeeTest-init-Endpunkt der Website. Die Challenge als wiederverwendbar zu behandeln, ist der Grund, warum Integrationen im Test bestehen und in der Produktion zusammenbrechen, wo die Warteschlangenverzögerung das Lösen über den Ablaufzeitpunkt hinausschiebt.

Einrichtung

npm install capskip
const { CapSkip } = require('capskip');

const solver = new CapSkip({
  host: '127.0.0.1',
  port: 8080,
  recaptchaTimeout: 300,   // GeeTest uses this, not defaultTimeout
});

Diese Unterscheidung ist wichtig. defaultTimeout regelt nur Bild-Captchas; alles Interaktive verwendet recaptchaTimeout.

Wird gelöst

const result = await solver.geetest(
  '81388ea1fc187e0c335c0a8907ff2625',   // gt, static per site
  '7cf6a8b1a2c34d5e6f7089abcdef0123',   // challenge, fetched seconds ago
  'https://example.com/login',
);

console.log(result.challenge, result.validate, result.seccode);

Diese drei Eigenschaften sind die Antwort. result.code wird ebenfalls befüllt, aber bei GeeTest enthält es die rohe JSON-Zeichenkette statt etwas Absendbarem, sodass ein gewohnheitsmäßiger Griff danach zu einem rätselhaften Fehler führt.

Verwenden Sie den challenge der zurückkommt, nicht den, den Sie übergeben haben. Sie sind nicht immer identisch.

Der gesamte Ablauf

const { CapSkip } = require('capskip');

const solver = new CapSkip();
const LOGIN = 'https://example.com/login';

// 1. Fresh pair, cache-busted. Cached init responses return spent challenges.
const init = await (await fetch(
  `https://example.com/geetest/init?t=${Date.now()}`,
)).json();

// 2. Solve immediately. Nothing slow between here and the previous step.
const result = await solver.geetest(init.gt, init.challenge, LOGIN);

// 3. Post all three together with the real form fields.
const response = await fetch(LOGIN, {
  method: 'POST',
  body: new URLSearchParams({
    geetest_challenge: result.challenge,
    geetest_validate: result.validate,
    geetest_seccode: result.seccode,
    username: '...',
    password: '...',
  }),
});

Der Cache-Busting-Zeitstempel leistet echte Arbeit. GeeTest-Init-Endpunkte werden häufig von einem CDN oder Proxy gecacht, und eine gecachte Antwort liefert Ihnen eine Challenge, die jemand bereits verwendet hat.

Diese Feldnamen sind die übliche GeeTest-v3-Konvention, wobei eine Website sie umbenennen kann. Prüfen Sie das echte Formular, bevor Sie etwas annehmen.

Die Batching-Falle

Hier arbeitet die Ergonomie von Node’s gegen Sie. Die naheliegende parallele Version ist falsch:

// WRONG: every challenge is fetched up front, so the later solves
// start against pairs that have already expired.
const pairs = await Promise.all(urls.map(fetchPair));
const results = await Promise.all(
  pairs.map((p, i) => solver.geetest(p.gt, p.challenge, urls[i])));

Halten Sie das Abrufen und das Lösen innerhalb derselben Aufgabe, sodass jedes Paar innerhalb von Sekunden nach seiner Ausstellung verwendet wird:

// RIGHT: each task fetches its own pair immediately before solving.
const results = await Promise.all(urls.map(async (url) => {
  const pair = await fetchPair(url);
  return solver.geetest(pair.gt, pair.challenge, url);
}));

AsyncCapSkip wird exportiert als Alias von CapSkip hier, sodass es keinen separaten async-Client gibt, zu dem man greifen müsste. Promise.all ist die ganze Geschichte.

Fehler

const {
  ValidationException, NetworkException, ApiException, TimeoutException,
} = require('capskip');

try {
  const result = await solver.geetest(gt, challenge, pageUrl);
} catch (err) {
  if (err instanceof ValidationException)   { /* missing gt or challenge */ }
  else if (err instanceof NetworkException) { /* CapSkip not running */ }
  else if (err instanceof ApiException)     { /* usually an expired challenge */ }
  else if (err instanceof TimeoutException) { /* exceeded recaptchaTimeout */ }
  else throw err;
}

Die meisten GeeTest-Fehlschläge kommen als ApiException und bedeuten, dass die Herausforderung abgelaufen ist. Ein erneuter Versuch mit demselben Paar funktioniert nie. Holen Sie ein neues.

Häufig gestellte Fragen

Warum kann ich nicht einfach result.code verwenden?

Weil die Antwort von GeeTest aus drei Werten besteht. code hält das rohe JSON zur Vollständigkeit bereit, während das SDK die nützlichen Teile expandiert in challenge, validate und seccode. Übermitteln Sie diese.

Kann ich den gt-Wert zwischenspeichern?

Ja. Der gt ist eine Website-Kennung und ändert sich selten. Die challenge ist die verderbliche Hälfte und muss jedes Mal frisch abgerufen werden.

Unterstützt dies GeeTest v4?

The geetest Methode zielt auf v3, das Slide-Puzzle, aufgebaut auf einem gt und challenge Paar. v4 hat das Parametermodell geändert, prüfen Sie also das aktuelle API-Dokumentation bevor Sie annehmen, dass derselbe Aufruf funktioniert.

Zusammenfassung

Rufen Sie ein cache-busted Paar ab, lösen Sie sofort und senden Sie dann challenge, validate und seccode zusammen. Verwenden Sie die zurückgegebene challenge, cachen Sie nur den gt, und halten Sie Abrufen und Lösen innerhalb derselben Aufgabe, wenn Sie parallel laufen.

Andere Sprachen finden Sie auf der GeeTest-Löser -Seite, die breitere Node-Oberfläche auf der Node.js-Captcha-Löser Seite, und es gibt ein Live-Puzzle auf unserer GeeTest-v3-Demo. CapSkip tut Captcha-Umgehung lokal, sodass das Lösevolumen nichts kostet.