Résolution de Problèmes

Erreurs et correctifs courants de reCAPTCHA Invisible

Invisible reCAPTCHA supprime la case à cocher, mais ajoute de nouveaux modes de défaillance pour l'automatisation. Les plus gros problèmes sont ne pas détecter du tout le widget invisible, manquer la fonction de rappel, soumettre des jetons expirés et utiliser les paramètres standard v2 lorsque des paramètres invisibles sont requis.

Ce guide couvre tous les modèles d'erreur courants avec le correctif exact. Si vous avez besoin d'informations sur le fonctionnement de reCAPTCHA invisible, lisezComment fonctionne reCAPTCHA Invisible et comment le résoudred'abord.


Référence d'erreur rapide

Erreur Parce que Corriger
ERROR_WRONG_GOOGLEKEY Mauvaise clé de site ou clé de site provenant d'un domaine différent Extraire la clé de site du div du widget invisible ou de l'appel grecaptcha.render()
ERROR_PAGEURL Incompatibilité d'URL : URL de la page parent envoyée au lieu de l'URL iframe Utilisez l'URL exacte où le widget invisible se charge
ERROR_CAPTCHA_UNSOLVABLE Google a signalé la tâche comme impossible Réessayez avec un nouveau proxy et des cookies ; vérifier si le site est passé à la v3
ERROR_BAD_TOKEN_OR_PAGEURL Jeton rejeté par le site cible Vérifiez exactement les correspondances de l'URL de la page ; injecter via un rappel, pas un champ caché
CAPCHA_NOT_READY Tâche en cours de traitement Continuez à interroger toutes les 5 secondes ; les résolutions invisibles prennent 10 à 30 secondes
ERROR_KEY_DOES_NOT_EXIST Clé API CaptchaAI non valide Vérifiez la clé àcaptchaai.com/account
Jeton accepté mais le formulaire échoue Rappel non exécuté après l'injection du jeton Recherchez et appelez la fonction data-callback avec le jeton

Erreur 1 : Ne pas détecter le reCAPTCHA invisible sur la page

Invisible reCAPTCHA n'a pas de case à cocher visible. Si votre scraper ne le détecte pas, les requêtes protégées par captcha échouent silencieusement avec des erreurs de soumission de formulaire ou des redirections.

Comment détecter les reCAPTCHA invisibles

Recherchez ces modèles dans la page HTML :

<!-- Pattern 1: div with data-size="invisible" -->
<div class="g-recaptcha" data-sitekey="6LdKlZEU..."
     data-size="invisible"
     data-callback="onSubmit"></div>

<!-- Pattern 2: button with data-sitekey and invisible size -->
<button class="g-recaptcha"
        data-sitekey="6LdKlZEU..."
        data-callback="onSubmit"
        data-action="submit">Submit</button>

<!-- Pattern 3: programmatic render with size: invisible -->
<script>
  grecaptcha.render('submit-btn', {
    sitekey: '6LdKlZEU...',
    callback: onSubmit,
    size: 'invisible'
  });
</script>

Script de détection (Python) :

import requests
from bs4 import BeautifulSoup
import re

def detect_invisible_recaptcha(url):
    resp = requests.get(url)
    soup = BeautifulSoup(resp.text, "html.parser")

    # Check for data-size="invisible"
    widget = soup.find("div", {"data-size": "invisible", "class": "g-recaptcha"})
    if widget:
        return {
            "type": "invisible",
            "sitekey": widget.get("data-sitekey"),
            "callback": widget.get("data-callback")
        }

    # Check for programmatic render with invisible
    scripts = soup.find_all("script")
    for script in scripts:
        if script.string and "size" in str(script.string) and "invisible" in str(script.string):
            key_match = re.search(r"sitekey['\"]?\s*[:=]\s*['\"]([^'\"]+)", script.string)
            if key_match:
                return {
                    "type": "invisible-programmatic",
                    "sitekey": key_match.group(1),
                    "callback": "check grecaptcha.render() call"
                }

    return None

Script de détection (Node.js) :

const axios = require("axios");
const cheerio = require("cheerio");

async function detectInvisibleRecaptcha(url) {
  const { data } = await axios.get(url);
  const $ = cheerio.load(data);

  // Check for data-size="invisible"
  const widget = $(".g-recaptcha[data-size='invisible']");
  if (widget.length) {
    return {
      type: "invisible",
      sitekey: widget.attr("data-sitekey"),
      callback: widget.attr("data-callback"),
    };
  }

  // Check script tags for programmatic invisible render
  const scriptContent = $("script")
    .map((_, el) => $(el).html())
    .get()
    .join("\n");
  if (scriptContent.includes("invisible")) {
    const keyMatch = scriptContent.match(/sitekey['"]?\s*[:=]\s*['"]([^'"]+)/);
    if (keyMatch) {
      return {
        type: "invisible-programmatic",
        sitekey: keyMatch[1],
        callback: "check grecaptcha.render() call",
      };
    }
  }

  return null;
}

Erreur 2 : clé de site incorrecte – ERROR_WRONG_GOOGLEKEY

Cela se produit lorsque vous envoyez une clé de site qui ne correspond pas au widget reCAPTCHA invisible sur la page cible. Causes courantes :

  • Copié la clé du site à partir d'une case à cocher v2 sur une autre page
  • Utilisation d'une clé de site à partir de l'URL d'ancrage d'une autre version de reCAPTCHA
  • La page contient plusieurs widgets reCAPTCHA et vous avez choisi le mauvais.

Correctif : extrayez la clé de site invisible correcte

import requests
from bs4 import BeautifulSoup

def get_invisible_sitekey(url):
    resp = requests.get(url)
    soup = BeautifulSoup(resp.text, "html.parser")

    # Priority 1: invisible widget
    widget = soup.find(attrs={"data-size": "invisible", "class": "g-recaptcha"})
    if widget:
        return widget["data-sitekey"]

    # Priority 2: any g-recaptcha div (may be invisible without data-size)
    widget = soup.find(class_="g-recaptcha")
    if widget and widget.get("data-sitekey"):
        return widget["data-sitekey"]

    return None

sitekey = get_invisible_sitekey("https://example.com/login")
print(f"Sitekey: {sitekey}")

Erreur 3 : rappel non exécuté – le formulaire est soumis mais rien ne se passe

Il s’agit de l’échec invisible numéro un de reCAPTCHA qui manque aux développeurs. Contrairement à la case à cocher v2 où l'injection du jeton dans g-recaptcha-response suffit, invisible reCAPTCHA utilise presque toujours une fonction de rappel JavaScript. Si vous injectez le jeton mais n'appelez pas le rappel, le formulaire ne sera jamais traité.

Comment fonctionne le flux de rappel

  1. grecaptcha.execute() lance le défi invisible
  2. Après résolution, Google appelle la fonction spécifiée dans data-callback
  3. Cette fonction de rappel soumet le formulaire ou effectue l'appel API

Correctif : recherchez et exécutez le rappel

Étape 1 : identifiez le nom du rappel :

# From HTML: data-callback="onSubmit"
# From JS: callback: onSubmit
# From grecaptcha.render: second argument with callback property

Étape 2 — Injecter le jeton ET appeler le rappel (Selenium) :

from selenium import webdriver
import requests
import time

driver = webdriver.Chrome()
driver.get("https://example.com/form")

# Get sitekey
sitekey = driver.find_element("css selector", ".g-recaptcha").get_attribute("data-sitekey")
callback_name = driver.find_element("css selector", ".g-recaptcha").get_attribute("data-callback")

# Solve with CaptchaAI
task_id = requests.get("https://ocr.captchaai.com/in.php", params={
    "key": "YOUR_API_KEY",
    "method": "userrecaptcha",
    "googlekey": sitekey,
    "pageurl": driver.current_url,
    "invisible": 1
}).text.split("|")[1]

# Poll for result
token = None
for _ in range(60):
    time.sleep(5)
    resp = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": "YOUR_API_KEY",
        "action": "get",
        "id": task_id
    }).text
    if resp.startswith("OK|"):
        token = resp.split("|")[1]
        break

# Inject token into the response field
driver.execute_script(
    f'document.getElementById("g-recaptcha-response").value = "{token}";'
)

# CRITICAL: Call the callback function
driver.execute_script(f'{callback_name}("{token}");')

Étape 2 — Injecter le jeton ET appeler le rappel (Marionnette) :

const puppeteer = require("puppeteer");
const axios = require("axios");

(async () => {
  const browser = await puppeteer.launch({ headless: "new" });
  const page = await browser.newPage();
  await page.goto("https://example.com/form");

  // Get sitekey and callback
  const { sitekey, callback } = await page.evaluate(() => {
    const el = document.querySelector(".g-recaptcha[data-size='invisible']");
    return {
      sitekey: el?.getAttribute("data-sitekey"),
      callback: el?.getAttribute("data-callback"),
    };
  });

  // Submit to CaptchaAI
  const submitResp = await axios.get("https://ocr.captchaai.com/in.php", {
    params: {
      key: "YOUR_API_KEY",
      method: "userrecaptcha",
      googlekey: sitekey,
      pageurl: page.url(),
      invisible: 1,
    },
  });
  const taskId = submitResp.data.split("|")[1];

  // Poll for result
  let token;
  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const result = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: "YOUR_API_KEY", action: "get", id: taskId },
    });
    if (result.data.startsWith("OK|")) {
      token = result.data.split("|")[1];
      break;
    }
  }

  // Inject token and fire callback
  await page.evaluate(
    (tok, cb) => {
      document.getElementById("g-recaptcha-response").value = tok;
      if (cb && typeof window[cb] === "function") {
        window[cb](tok);
      }
    },
    token,
    callback,
  );

  await browser.close();
})();

Erreur 4 : paramètre invisible=1 manquant

Lorsque vous résolvez un reCAPTCHA invisible via CaptchaAI, vous devez inclure invisible=1 dans votre demande. Sans cela, le solveur traite la tâche comme un défi de case à cocher v2 standard, ce qui peut conduire à ERROR_CAPTCHA_UNSOLVABLE ou à des jetons rejetés par le site cible.

Demande erronée ou correcte

# WRONG — missing invisible=1
params = {
    "key": "YOUR_API_KEY",
    "method": "userrecaptcha",
    "googlekey": sitekey,
    "pageurl": page_url
}

# CORRECT — includes invisible=1
params = {
    "key": "YOUR_API_KEY",
    "method": "userrecaptcha",
    "googlekey": sitekey,
    "pageurl": page_url,
    "invisible": 1  # Required for invisible reCAPTCHA
}

response = requests.get("https://ocr.captchaai.com/in.php", params=params)

Erreur 5 : le jeton a expiré avant la soumission

Les jetons reCAPTCHA invisibles expirent dans 120 secondes, comme la norme v2. Mais les flux de travail invisibles comportent souvent des étapes de traitement supplémentaires entre la résolution et la soumission, ce qui rend l'expiration plus probable.

Symptômes

  • Le formulaire renvoie une erreur générique après l'injection du jeton
  • siteverify côté serveur renvoie timeout-or-duplicate
  • Le jeton était valide mais a mis trop de temps à atteindre l'étape de soumission

Correctif : résolution juste à temps

Ne demandez la solution que lorsque vous êtes prêt à la soumettre immédiatement :

import requests
import time

def solve_invisible_recaptcha(api_key, sitekey, page_url):
    # Submit task
    resp = requests.get("https://ocr.captchaai.com/in.php", params={
        "key": api_key,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": page_url,
        "invisible": 1
    })
    if not resp.text.startswith("OK|"):
        raise Exception(f"Submit failed: {resp.text}")
    task_id = resp.text.split("|")[1]

    # Poll for result
    for _ in range(60):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": api_key,
            "action": "get",
            "id": task_id
        })
        if result.text.startswith("OK|"):
            return result.text.split("|")[1]
        if result.text != "CAPCHA_NOT_READY":
            raise Exception(f"Solve failed: {result.text}")

    raise Exception("Solve timed out after 5 minutes")

# Usage: solve JUST before you need to submit
# 1. Navigate to page and prepare form data first
# 2. THEN solve the captcha
# 3. Inject token and submit immediately
token = solve_invisible_recaptcha("YOUR_API_KEY", sitekey, page_url)
# Submit within 120 seconds of receiving the token

Erreur 6 : jeton rejeté – ERROR_BAD_TOKEN_OR_PAGEURL

Le site cible a vérifié le jeton auprès de Google et a échoué. Causes courantes :

Parce que Comment identifier Corriger
Mauvais pageurl L'URL ne correspond pas au domaine lors de l'enregistrement de la clé de site Utilisez l'URL exacte où le widget se charge
Jeton utilisé sur un domaine différent Réutilisation des jetons entre domaines Résolvez avec le pageurl du domaine correct
Jeton déjà utilisé Soumettre le même jeton deux fois Demander une nouvelle solution pour chaque soumission
Incompatibilité IP Votre IP diffère de celle du solveur Ajoutez votre paramètre proxy pour correspondre à l'adresse IP de la session
Drapeau invisible manquant Résolu selon la norme v2, utilisé sur une page invisible Ajoutez invisible=1 à la demande de résolution

Liste de contrôle de débogage

def debug_invisible_solve(api_key, sitekey, page_url, proxy=None):
    """Run a diagnostic solve with detailed logging."""
    print(f"Sitekey: {sitekey}")
    print(f"Page URL: {page_url}")
    print(f"Proxy: {proxy or 'none'}")

    params = {
        "key": api_key,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": page_url,
        "invisible": 1
    }
    if proxy:
        params["proxy"] = proxy
        params["proxytype"] = "HTTP"

    # Submit
    resp = requests.get("https://ocr.captchaai.com/in.php", params=params)
    print(f"Submit response: {resp.text}")
    if not resp.text.startswith("OK|"):
        return None

    task_id = resp.text.split("|")[1]
    print(f"Task ID: {task_id}")

    # Poll with timing
    start = time.time()
    for _ in range(60):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": api_key, "action": "get", "id": task_id
        })
        elapsed = time.time() - start
        print(f"  [{elapsed:.0f}s] {result.text[:50]}")
        if result.text.startswith("OK|"):
            token = result.text.split("|")[1]
            print(f"Token received after {elapsed:.0f}s")
            print(f"Token length: {len(token)} characters")
            print(f"Token starts with: {token[:30]}...")
            return token
        if result.text != "CAPCHA_NOT_READY":
            print(f"FAILED: {result.text}")
            return None

    print("TIMEOUT after 5 minutes")
    return None

Erreur 7 : plusieurs widgets reCAPTCHA sur une seule page

Certaines pages ont à la fois une case à cocher v2 visible ET un reCAPTCHA invisible. Si vous résolvez le mauvais problème, le jeton est valide mais ne correspond pas au widget gardant l'action dont vous avez besoin.

Correctif : ciblez le bon widget

from bs4 import BeautifulSoup

def find_all_recaptcha_widgets(html):
    soup = BeautifulSoup(html, "html.parser")
    widgets = []

    for el in soup.find_all(class_="g-recaptcha"):
        widgets.append({
            "sitekey": el.get("data-sitekey"),
            "size": el.get("data-size", "normal"),
            "callback": el.get("data-callback"),
            "tag": el.name,
            "id": el.get("id")
        })

    return widgets

# Example output:
# [
#   {"sitekey": "6LdA...", "size": "normal", "callback": None, "tag": "div", "id": "recaptcha-login"},
#   {"sitekey": "6LdB...", "size": "invisible", "callback": "onRegister", "tag": "div", "id": "recaptcha-register"}
# ]
# Use the widget with size="invisible" for the invisible solve

Solveur reCAPTCHA invisible complet avec gestion des erreurs

Ce wrapper prêt pour la production gère toutes les erreurs ci-dessus :

import requests
import time
import logging

logger = logging.getLogger(__name__)

class InvisibleRecaptchaSolver:
    def __init__(self, api_key, max_retries=3):
        self.api_key = api_key
        self.max_retries = max_retries
        self.base_url = "https://ocr.captchaai.com"

    def solve(self, sitekey, page_url, proxy=None):
        """Solve invisible reCAPTCHA with automatic retry on transient errors."""
        for attempt in range(1, self.max_retries + 1):
            try:
                token = self._attempt_solve(sitekey, page_url, proxy)
                if token:
                    return token
            except Exception as e:
                logger.warning(f"Attempt {attempt} failed: {e}")
                if attempt < self.max_retries:
                    time.sleep(2 ** attempt)
        raise Exception(f"Failed to solve after {self.max_retries} attempts")

    def _attempt_solve(self, sitekey, page_url, proxy):
        params = {
            "key": self.api_key,
            "method": "userrecaptcha",
            "googlekey": sitekey,
            "pageurl": page_url,
            "invisible": 1
        }
        if proxy:
            params["proxy"] = proxy
            params["proxytype"] = "HTTP"

        # Submit task
        resp = requests.get(f"{self.base_url}/in.php", params=params)

        if "ERROR" in resp.text:
            error = resp.text.strip()
            if error in ("ERROR_WRONG_GOOGLEKEY", "ERROR_KEY_DOES_NOT_EXIST"):
                raise Exception(f"Configuration error (do not retry): {error}")
            if error == "ERROR_ZERO_BALANCE":
                raise Exception("Account balance is zero — add funds")
            raise Exception(f"Submit error: {error}")

        if not resp.text.startswith("OK|"):
            raise Exception(f"Unexpected submit response: {resp.text}")

        task_id = resp.text.split("|")[1]

        # Poll for result
        for _ in range(60):
            time.sleep(5)
            result = requests.get(f"{self.base_url}/res.php", params={
                "key": self.api_key,
                "action": "get",
                "id": task_id
            })

            if result.text.startswith("OK|"):
                return result.text.split("|")[1]

            if result.text == "CAPCHA_NOT_READY":
                continue

            if result.text == "ERROR_CAPTCHA_UNSOLVABLE":
                logger.warning("Captcha unsolvable — will retry with new task")
                return None

            raise Exception(f"Poll error: {result.text}")

        raise Exception("Solve timed out after 5 minutes")


# Usage
solver = InvisibleRecaptchaSolver("YOUR_API_KEY")
token = solver.solve(
    sitekey="6LdKlZEU...",
    page_url="https://example.com/login"
)
print(f"Token: {token[:50]}...")
const axios = require("axios");

class InvisibleRecaptchaSolver {
  constructor(apiKey, maxRetries = 3) {
    this.apiKey = apiKey;
    this.maxRetries = maxRetries;
    this.baseUrl = "https://ocr.captchaai.com";
  }

  async solve(sitekey, pageUrl, proxy) {
    for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
      try {
        const token = await this._attemptSolve(sitekey, pageUrl, proxy);
        if (token) return token;
      } catch (err) {
        console.warn(`Attempt ${attempt} failed: ${err.message}`);
        if (attempt < this.maxRetries) {
          await new Promise((r) => setTimeout(r, 2 ** attempt * 1000));
        }
      }
    }
    throw new Error(`Failed to solve after ${this.maxRetries} attempts`);
  }

  async _attemptSolve(sitekey, pageUrl, proxy) {
    const params = {
      key: this.apiKey,
      method: "userrecaptcha",
      googlekey: sitekey,
      pageurl: pageUrl,
      invisible: 1,
    };
    if (proxy) {
      params.proxy = proxy;
      params.proxytype = "HTTP";
    }

    // Submit task
    const submitResp = await axios.get(`${this.baseUrl}/in.php`, { params });
    if (submitResp.data.includes("ERROR")) {
      const error = submitResp.data.trim();
      if (["ERROR_WRONG_GOOGLEKEY", "ERROR_KEY_DOES_NOT_EXIST"].includes(error)) {
        throw new Error(`Configuration error (do not retry): ${error}`);
      }
      throw new Error(`Submit error: ${error}`);
    }

    const taskId = submitResp.data.split("|")[1];

    // Poll for result
    for (let i = 0; i < 60; i++) {
      await new Promise((r) => setTimeout(r, 5000));
      const result = await axios.get(`${this.baseUrl}/res.php`, {
        params: { key: this.apiKey, action: "get", id: taskId },
      });

      if (result.data.startsWith("OK|")) {
        return result.data.split("|")[1];
      }
      if (result.data === "CAPCHA_NOT_READY") continue;
      if (result.data === "ERROR_CAPTCHA_UNSOLVABLE") return null;
      throw new Error(`Poll error: ${result.data}`);
    }
    throw new Error("Solve timed out after 5 minutes");
  }
}

// Usage
const solver = new InvisibleRecaptchaSolver("YOUR_API_KEY");
solver.solve("6LdKlZEU...", "https://example.com/login").then((token) => {
  console.log(`Token: ${token.substring(0, 50)}...`);
});

Liste de contrôle de dépannage

Parcourez cette liste de contrôle lorsque la résolution invisible de reCAPTCHA échoue :

Étape Vérifier Commande/Action
1 Confirmez qu'il est invisible, pas la norme v2 Recherchez data-size="invisible" ou size: 'invisible' dans l'appel de rendu
2 Vérifiez que la clé du site est correcte Comparez avec data-sitekey sur le widget invisible spécifiquement
3 Confirmez invisible=1 dans la requête API Vérifiez vos paramètres in.php
4 Vérifiez que pageurl correspond exactement Utilisez l'URL DevTools du navigateur, pas une URL de redirection
5 Rechercher le nom de la fonction de rappel Recherchez l'attribut data-callback ou callback dans grecaptcha.render()
6 Vérifier l'injection de jeton + appel de rappel Les deux étapes sont nécessaires : le jeton seul ne suffit pas
7 Vérifier la fraîcheur du jeton Le jeton doit être utilisé dans les 120 secondes
8 Testez avec proxy si l'IP est importante Ajouter les paramètres proxy et proxytype

FAQ

En quoi le reCAPTCHA invisible est-il différent de la norme v2 pour la résolution ?

La méthode API est la même (method=userrecaptcha), mais vous devez ajouter invisible=1 à votre requête. La différence critique se situe du côté de l'injection : le reCAPTCHA invisible nécessite presque toujours l'appel d'une fonction de rappel JavaScript après l'injection du jeton, alors que la norme v2 fonctionne généralement avec uniquement le champ caché.

Pourquoi mon jeton fonctionne-t-il en test mais échoue-t-il en production ?

Très probablement une incompatibilité IP. Lors des tests, le solveur et votre navigateur peuvent partager des adresses IP similaires. En production, l'IP du solveur et l'IP de votre serveur diffèrent. Ajoutez un paramètre proxy qui correspond à l'adresse IP de votre session pour résoudre ce problème.

Combien de temps faut-il pour que reCAPTCHA invisible soit résolu ?

Les temps de résolution typiques sont de 10 à 30 secondes via CaptchaAI. Les défis invisibles sont généralement plus rapides que les défis de cases à cocher v2 car ils ne nécessitent pas de reconnaissance d'image : ils reposent sur une analyse des risques.

Puis-je résoudre le reCAPTCHA invisible sans navigateur ?

Oui. Étant donné que la résolution s'effectue côté serveur via l'API, vous n'avez besoin que de la clé du site et de l'URL de la page. Le navigateur n'est nécessaire que si vous devez exécuter la fonction de rappel sur la page réelle. Pour les flux de travail API purs, extrayez la clé du site, résolvez via CaptchaAI et soumettez le jeton avec votre requête HTTP.


Prochaines étapes

Articles connexes

Les commentaires sont désactivés pour cet article.