Scarnox · Blog

Solveur de captcha Selenium en Python : exemple fonctionnel (2026)

Selenium pilote un vrai navigateur, la page s'affiche donc et ses scripts s'exécutent comme pour une personne. La seule étape qu'il ne peut pas faire est de résoudre le widget PopularCaptcha. Ce guide ajoute cette étape : lire la sitekey dans la page affichée, obtenir un jeton avec un seul appel API, l'écrire dans le formulaire, puis l'envoyer.

Il couvre le widget PopularCaptcha, que Scarnox résout aujourd'hui sans proxy. Commencez avec un crédit d'essai gratuit, sans carte bancaire et testez le script sur un formulaire de test d'abord.

Ce que Selenium doit faire

Le flux comporte quatre parties :

  1. Charger la page et attendre que le widget s'affiche.
  2. Lire l'attribut data-sitekey du widget.
  3. Obtenir un jeton auprès de l'API et le placer dans le formulaire.
  4. Envoyer le formulaire avant que le jeton n'expire.

La troisième étape est la seule qui touche au captcha. Tout le reste relève du travail Selenium ordinaire.

Installer les paquets

pip install selenium requests

Selenium 4.6 et versions ultérieures trouvent seuls un ChromeDriver compatible, vous n'avez donc pas besoin de télécharger le pilote manuellement.

Le script complet

import requests
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

API_KEY = "scarnox_YOURAPIKEY"
API_URL = "https://api.scarnox.com/api/tasks/create"
PAGE_URL = "https://example.com/signup"


def solve(sitekey: str, page_url: str, invisible: bool = False) -> str:
    body = {
        "captcha_type": "PopularCaptchaTaskProxyless",
        "site_key": sitekey,
        "site_url": page_url,
    }
    if invisible:
        body["is_invisible"] = True

    r = requests.post(
        API_URL,
        headers={"Authorization": f"Bearer {API_KEY}"},
        json=body,
        timeout=185,  # bloque jusqu'à ce que le jeton soit prêt
    )
    data = r.json()
    if data.get("errorId") != 0:
        raise RuntimeError(f"{data.get('errorCode')}: {data.get('errorDescription')}")
    return data["solution"]["token"]


INJECT_TOKEN_JS = """
const token = arguments[0];
let field = document.querySelector('[name="h-captcha-response"]');
if (!field) {
    field = document.createElement("textarea");
    field.name = "h-captcha-response";
    field.style.display = "none";
    document.querySelector("form").appendChild(field);
}
field.value = token;
let extra = document.querySelector('[name="g-recaptcha-response"]');
if (!extra) {
    extra = document.createElement("textarea");
    extra.name = "g-recaptcha-response";
    extra.style.display = "none";
    document.querySelector("form").appendChild(extra);
}
extra.value = token;
"""


def main() -> None:
    driver = webdriver.Chrome()
    try:
        driver.get(PAGE_URL)

        widget = WebDriverWait(driver, 15).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, "[data-sitekey]"))
        )
        sitekey = widget.get_attribute("data-sitekey")
        invisible = widget.get_attribute("data-size") == "invisible"

        token = solve(sitekey, driver.current_url, invisible=invisible)

        # Remplir d'abord les autres champs, puis injecter le jeton juste avant l'envoi,
        # car le jeton expire au bout d'environ deux minutes.
        driver.find_element(By.NAME, "email").send_keys("you@example.com")
        driver.execute_script(INJECT_TOKEN_JS, token)

        driver.find_element(By.CSS_SELECTOR, "form button[type=submit]").click()
        WebDriverWait(driver, 15).until(EC.url_changes(PAGE_URL))
        print("Submitted. Now at:", driver.current_url)
    finally:
        driver.quit()


if __name__ == "__main__":
    main()

Le script n'a pas besoin de proxy pour une résolution PopularCaptcha sans proxy. Omettez le champ proxy et laissez le site voir votre connexion normale.

L'ordre des opérations est la partie la plus souvent source d'erreurs. Remplissez le reste du formulaire d'abord, puis résolvez, injectez, et cliquez. Si vous résolvez avant de saisir les autres champs, une page ou un formulaire lent peut repousser l'envoi au-delà de la durée de vie du jeton. Réduisez le plus possible l'écart entre la résolution et le clic, et vérifiez la page de résultat pour une erreur avant de supposer que le captcha a été réussi.

Pourquoi le jeton est injecté avec JavaScript

La plupart des widgets PopularCaptcha écrivent le jeton dans un textarea caché nommé h-captcha-response lorsqu'une personne le résout. Comme Selenium ne voit jamais de personne, le script écrit lui-même ce même champ. Le snippet INJECT_TOKEN_JS fait deux choses :

Il remplit aussi g-recaptcha-response, car de nombreux sites lisent ce nom par compatibilité.

Widgets invisibles

Un widget invisible n'a pas de case à cocher. Il s'exécute en arrière-plan et se déclenche généralement à l'envoi du formulaire. Le script vérifie data-size="invisible" et envoie is_invisible: true lorsqu'il le trouve. Certains sites associent aussi un callback JavaScript au widget. Si le formulaire reste en place après l'injection du jeton, cherchez un attribut data-callback et appelez cette fonction avec le jeton. Notre guide sur le captcha invisible couvre ce cas.

Utiliser le script dans les tests

Selenium est aussi l'outil habituel pour les tests de bout en bout. Si votre site de préproduction affiche un captcha, le même code permet à un test de le passer. Gardez l'environnement de test séparé de la production, et ne l'exécutez pas sur des sites que vous ne possédez pas. La mise en place des suites de tests est décrite dans captcha dans les tests automatisés.

Problèmes courants

Tarifs

Vous payez à chaque résolution réussie. Les résolutions échouées ne sont pas facturées. Consultez les tarifs actuels et commencez avec un crédit d'essai gratuit, sans carte bancaire.

Prochaines étapes