Un solveur reCAPTCHA ne devine rien : il rejoue exactement les paramètres présents dans la page cible. Avant le moindre appel à l'API CaptchaAI, vous devez donc lire la source de la page et en extraire au minimum deux valeurs, le sitekey et l'URL de la page. Selon la version rencontrée, il faut parfois aussi capter l'action (reCAPTCHA v3), le champ data-s (sites Google) ou l'indicateur Enterprise. Ce guide passe en revue chaque emplacement où ces valeurs se cachent, avec du code Python directement réutilisable.
L'enjeu est concret : un sitekey erroné ou une action manquante, et la résolution échoue silencieusement côté API.
Quelle méthode d'extraction choisir ?
Le bon point d'entrée dépend de la manière dont la page charge son widget :
- Widget visible dans le HTML : lisez l'attribut
data-sitekey(méthode 1). - reCAPTCHA v3 ou Enterprise : la clé est dans l'URL du script
render=(méthode 2). - Widget dans une iframe : la clé voyage dans le paramètre
kde l'iframe (méthode 3). - Widget injecté par
grecaptcha.render(): lisez l'objet de configuration JavaScript (méthode 4).
Les paramètres à extraire selon la version
Ce tableau récapitule ce qui est obligatoire, optionnel ou inutile selon la variante :
| Paramètre | v2 standard | v2 invisible | v3 | Enterprise |
|---|---|---|---|---|
googlekey (sitekey) |
Obligatoire | Obligatoire | Obligatoire | Obligatoire |
pageurl |
Obligatoire | Obligatoire | Obligatoire | Obligatoire |
invisible |
- | 1 |
- | - |
action |
- | - | Obligatoire | Parfois |
data-s |
Parfois | Parfois | - | - |
enterprise |
- | - | - | 1 |
sitekey et pageurl sont toujours requis ; le reste dépend de ce que vous détectez.
Méthode 1 : lire les attributs HTML du widget
Dans la majorité des intégrations reCAPTCHA v2, le sitekey est posé en clair sur l'élément .g-recaptcha via l'attribut data-sitekey. Une requête HTTP et une regex suffisent, sans navigateur.
Depuis l'attribut data-sitekey
Ce script récupère le HTML brut, puis extrait le sitekey, le mode invisible, le callback éventuel et le champ data-s propre aux sites Google.
import re
import requests
url = "https://example.com/login"
html = requests.get(url).text
# Find data-sitekey
match = re.search(r'data-sitekey=["\']([A-Za-z0-9_-]+)["\']', html)
if match:
sitekey = match.group(1)
print(f"Sitekey: {sitekey}")
# Check if invisible
invisible_match = re.search(r'data-size=["\']invisible["\']', html)
is_invisible = bool(invisible_match)
print(f"Invisible: {is_invisible}")
# Find callback
callback_match = re.search(r'data-callback=["\'](\w+)["\']', html)
callback = callback_match.group(1) if callback_match else None
print(f"Callback: {callback}")
# Check for data-s (Google-owned sites)
data_s_match = re.search(r'data-s=["\']([^"\']+)["\']', html)
data_s = data_s_match.group(1) if data_s_match else None
print(f"data-s: {data_s}")
Avec Puppeteer quand le widget est injecté par JavaScript
Si le .g-recaptcha n'apparaît pas dans le HTML statique, c'est qu'il est ajouté au DOM après le chargement. Il faut alors un navigateur headless comme Puppeteer, laisser le réseau se stabiliser (networkidle2), puis lire les attributs directement sur l'élément rendu.
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com/login', { waitUntil: 'networkidle2' });
const params = await page.evaluate(() => {
const widget = document.querySelector('.g-recaptcha');
if (!widget) return null;
return {
sitekey: widget.getAttribute('data-sitekey'),
size: widget.getAttribute('data-size'),
callback: widget.getAttribute('data-callback'),
dataS: widget.getAttribute('data-s'),
invisible: widget.getAttribute('data-size') === 'invisible',
};
});
console.log(params);
Méthode 2 : extraire depuis les balises de script
reCAPTCHA v3 et Enterprise ne posent pas de data-sitekey visible : la clé voyage dans l'URL du script api.js ou enterprise.js, via le paramètre render.
sitekeys reCAPTCHA v3 et Enterprise
La présence de enterprise.js est le signal le plus fiable pour distinguer une intégration Enterprise d'une v3 classique.
# Find sitekey from script src
v3_match = re.search(
r'recaptcha/(?:api|enterprise)\.js\?.*?render=([A-Za-z0-9_-]+)',
html
)
if v3_match:
sitekey = v3_match.group(1)
print(f"v3 Sitekey: {sitekey}")
# Check enterprise
is_enterprise = 'enterprise.js' in html
print(f"Enterprise: {is_enterprise}")
Retrouver le paramètre action
En v3, l'action est déclarée dans le JavaScript, au moment de l'appel grecaptcha.execute, et non dans un attribut HTML. Sans elle, le score obtenu sera rejeté par le site. Ciblez donc l'appel execute.
# Search for grecaptcha.execute calls
action_match = re.search(
r'grecaptcha\.execute\s*\([^,]+,\s*\{[^}]*action\s*:\s*["\']([^"\']+)',
html
)
if action_match:
action = action_match.group(1)
print(f"Action: {action}")
Méthode 3 : extraire depuis l'attribut src de l'iframe
Quand reCAPTCHA est rendu à l'intérieur d'une iframe, la clé se retrouve dans l'URL de l'iframe, transportée par le paramètre k. Repérez l'iframe recaptcha/api2/anchor, puis isolez la valeur de k.
# Find reCAPTCHA iframe
iframe_match = re.search(
r'<iframe[^>]+src=["\']([^"\']*recaptcha/api2/anchor[^"\']*)["\']',
html
)
if iframe_match:
iframe_src = iframe_match.group(1)
sitekey_match = re.search(r'k=([A-Za-z0-9_-]+)', iframe_src)
if sitekey_match:
sitekey = sitekey_match.group(1)
print(f"Iframe sitekey: {sitekey}")
Méthode 4 : extraire du rendu JavaScript dynamique
Certaines pages appellent directement grecaptcha.render() avec un objet de configuration. Le sitekey, le callback et la size s'y trouvent sous forme de propriétés JavaScript.
# Find grecaptcha.render calls
render_match = re.search(
r'grecaptcha\.render\s*\([^,]*,\s*\{([^}]+)\}',
html
)
if render_match:
config = render_match.group(1)
sk = re.search(r'sitekey\s*:\s*["\']([A-Za-z0-9_-]+)', config)
cb = re.search(r'callback\s*:\s*["\']?(\w+)', config)
sz = re.search(r'size\s*:\s*["\'](\w+)', config)
print(f"Sitekey: {sk.group(1) if sk else 'not found'}")
print(f"Callback: {cb.group(1) if cb else 'not found'}")
print(f"Size: {sz.group(1) if sz else 'not found'}")
Une fonction d'extraction complète
Plutôt que de choisir la méthode à la main, cette fonction les enchaîne dans l'ordre : data-sitekey, puis render (v3), l'iframe, et enfin grecaptcha.render. Elle s'arrête dès qu'une clé est trouvée et complète les indicateurs invisible, enterprise, action, data-s et le callback.
import re
import requests
def extract_recaptcha_params(url):
html = requests.get(url, timeout=15).text
params = {"pageurl": url}
# Sitekey from data-sitekey
sk = re.search(r'data-sitekey=["\']([A-Za-z0-9_-]+)', html)
if sk:
params["sitekey"] = sk.group(1)
# Sitekey from script render parameter (v3)
if "sitekey" not in params:
v3 = re.search(r'render=([A-Za-z0-9_-]{20,})', html)
if v3:
params["sitekey"] = v3.group(1)
# Sitekey from iframe
if "sitekey" not in params:
iframe = re.search(r'recaptcha.*?k=([A-Za-z0-9_-]+)', html)
if iframe:
params["sitekey"] = iframe.group(1)
# Sitekey from grecaptcha.render
if "sitekey" not in params:
render = re.search(r'sitekey\s*:\s*["\']([A-Za-z0-9_-]+)', html)
if render:
params["sitekey"] = render.group(1)
# Version detection
if re.search(r'data-size=["\']invisible', html):
params["invisible"] = True
if 'enterprise.js' in html:
params["enterprise"] = True
# Action (v3)
action = re.search(
r'action\s*:\s*["\']([^"\']+)',
html[html.find('grecaptcha.execute'):] if 'grecaptcha.execute' in html else ''
)
if action:
params["action"] = action.group(1)
# data-s
ds = re.search(r'data-s=["\']([^"\']+)', html)
if ds:
params["data_s"] = ds.group(1)
# Callback
cb = re.search(r'data-callback=["\'](\w+)', html)
if cb:
params["callback"] = cb.group(1)
return params
# Usage
params = extract_recaptcha_params("https://example.com/login")
for k, v in params.items():
print(f" {k}: {v}")
Sur une page v2 invisible avec callback, la sortie ressemble à ceci :
pageurl: https://example.com/login
sitekey: 6Le-SITEKEY-abc123
invisible: True
callback: onCaptchaComplete
Envoyer les paramètres extraits à CaptchaAI
Une fois params rempli, l'envoi se résume à un POST sur in.php avec method=userrecaptcha. Les champs optionnels ne sont ajoutés que s'ils ont été détectés, ce qui évite un invisible ou un enterprise parasite.
data = {
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": params["sitekey"],
"pageurl": params["pageurl"],
"json": "1",
}
if params.get("invisible"):
data["invisible"] = "1"
if params.get("enterprise"):
data["enterprise"] = "1"
if params.get("action"):
data["action"] = params["action"]
if params.get("data_s"):
data["data-s"] = params["data_s"]
resp = requests.post("https://ocr.captchaai.com/in.php", data=data).json()
Le compte BASIC ($15/mois, 5 threads) suffit pour valider une extraction : la facturation se fait au thread concurrent, avec des résolutions illimitées, jamais à la pièce.
Exemple : auditer un formulaire de connexion en pré-production
Cas courant pour une équipe QA francophone : vous préparez des tests sur un formulaire de connexion protégé par reCAPTCHA, sur votre pré-production hébergée chez OVHcloud ou Scaleway. Lancez extract_recaptcha_params sur l'URL de staging et lisez le résultat :
- un
sitekeysansinvisible: c'est une v2 standard ; - un
enterprise: Trueavec uneaction: votre suite devra transmettre ces deux champs à chaque appel.
Profitez-en pour vérifier qu'aucune donnée personnelle inutile ne transite dans le formulaire : le sitekey est public, mais c'est le bon moment pour appliquer le principe de minimisation des données du RGPD sur vos propres pages.
Dépannage
Quand l'extraction ne rend pas la valeur attendue, la cause est presque toujours ici.
| Problème | Cause probable | Correctif |
|---|---|---|
| Aucun sitekey trouvé | La page génère le widget par JavaScript | Passez sur Puppeteer ou Selenium plutôt que sur le HTML statique |
| Mauvais sitekey capté | Plusieurs instances de reCAPTCHA sur la page | Vérifiez quel widget correspond au formulaire que vous soumettez |
| Action introuvable | Elle est définie dans un fichier JS externe | Récupérez et analysez les fichiers JavaScript liés à la page |
data-s change à chaque requête |
Google le régénère côté serveur | Extrayez un data-s frais pour chaque résolution |
FAQ
Comment identifier la version de reCAPTCHA présente sur une page ?
Un data-sitekey sur .g-recaptcha indique une v2, data-size="invisible" une v2 invisible, un paramètre render= dans l'URL du script une v3, et enterprise.js une intégration Enterprise. La fonction complète détecte ces cas automatiquement.
Pourquoi mon extraction ne trouve-t-elle aucun sitekey ?
Le plus souvent, le widget est injecté après le chargement par du JavaScript et n'existe pas dans le HTML renvoyé par requests.get. Rejouez l'extraction dans un navigateur headless (Puppeteer ou Selenium) qui exécute le script avant de lire le DOM.
Le sitekey doit-il rester secret comme la clé API ?
Non. Le sitekey est une clé publique attribuée au site, visible dans la source de la page : sa présence dans votre code ne pose aucun risque. Votre clé API CaptchaAI, elle, reste confidentielle et ne doit jamais figurer côté client.
À quelle fréquence le paramètre data-s change-t-il ?
Sur les sites Google, data-s est régénéré côté serveur et peut changer à chaque chargement. Ne le mettez jamais en cache : extrayez-le juste avant chaque envoi à l'API.
Passez des paramètres extraits à une résolution fiable
Récupérez votre clé API sur le site CaptchaAI et testez votre extraction de bout en bout.
Guides associés
- Détecter un CAPTCHA depuis la console du navigateur
- Déclencher le callback reCAPTCHA v2 après injection du token
- Détecter le déclenchement de reCAPTCHA v2 invisible