Tutoriels

Chargement dynamique de CAPTCHA : détection des CAPTCHA chargés paresseusement

Tous les CAPTCHA ne sont pas présents lors du premier chargement de la page. De nombreux sites affichent les CAPTCHA paresseusement - après un clic sur un bouton, un focus sur un formulaire, un défilement ou une minuterie. Si votre automatisation récupère immédiatement la source de la page, le CAPTCHA n'est pas encore là. Ce guide explique comment détecter et attendre les CAPTCHA chargés dynamiquement.


Déclencheurs courants de chargement différé

Déclencheur Exemple Comment activer
Clic sur le bouton "Envoyer" ajoute reCAPTCHA au formulaire Cliquez d'abord sur le bouton
Focus sur le formulaire CAPTCHA apparaît lorsque la saisie est ciblée Focus sur le champ email/password
Position de défilement CAPTCHA se charge lorsque la section est visible Faites défiler jusqu'au formulaire
Minuterie CAPTCHA se charge après 3 secondes Attendez le retard
Condition JavaScript CAPTCHA se charge après la réponse AJAX Déclencher la demande de prérequis

Méthode 1 : MutationObserver

Regardez le DOM pour les éléments CAPTCHA ajoutés :

Marionnettiste

const puppeteer = require('puppeteer');

const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
await page.goto('https://example.com/login');

// Set up MutationObserver before triggering the CAPTCHA
const captchaInfo = await page.evaluate(() => {
  return new Promise((resolve) => {
    // Check if already present
    const existing = document.querySelector('.g-recaptcha, .cf-turnstile, .h-captcha');
    if (existing) {
      resolve({
        type: existing.className,
        sitekey: existing.getAttribute('data-sitekey'),
      });
      return;
    }

    // Watch for new elements
    const observer = new MutationObserver((mutations) => {
      for (const mutation of mutations) {
        for (const node of mutation.addedNodes) {
          if (node.nodeType !== 1) continue;

          const captcha = node.matches?.('.g-recaptcha, .cf-turnstile, .h-captcha')
            ? node
            : node.querySelector?.('.g-recaptcha, .cf-turnstile, .h-captcha');

          if (captcha) {
            observer.disconnect();
            resolve({
              type: captcha.className,
              sitekey: captcha.getAttribute('data-sitekey'),
            });
            return;
          }
        }
      }
    });

    observer.observe(document.body, { childList: true, subtree: true });

    // Timeout after 30 seconds
    setTimeout(() => {
      observer.disconnect();
      resolve(null);
    }, 30000);
  });
});

console.log('Detected CAPTCHA:', captchaInfo);

Déclenchement de la charge

// Click the submit button to trigger CAPTCHA
await page.click('#submit-btn');

// Or focus the input
await page.focus('#email');

// Or scroll to the form
await page.evaluate(() => {
  document.querySelector('#signup-form').scrollIntoView();
});

Méthode 2 : attendre l'injection du script

Les CAPTCHA nécessitent leur bibliothèque JavaScript. Surveillez cela :

// Wait for reCAPTCHA script to load
await page.waitForFunction(() => {
  return typeof window.grecaptcha !== 'undefined' 
    && typeof window.grecaptcha.render === 'function';
}, { timeout: 30000 });

// Now extract parameters
const sitekey = await page.evaluate(() => {
  const el = document.querySelector('.g-recaptcha');
  return el?.getAttribute('data-sitekey');
});

Pour tourniquet

await page.waitForFunction(() => {
  return typeof window.turnstile !== 'undefined';
}, { timeout: 30000 });

const sitekey = await page.evaluate(() => {
  const el = document.querySelector('.cf-turnstile');
  return el?.getAttribute('data-sitekey');
});

Méthode 3 : intercepter les appels de rendu

Connectez-vous à la bibliothèque CAPTCHA avant le rendu :

// Inject before page scripts run
await page.evaluateOnNewDocument(() => {
  window.__captchaDetected = null;

  // Hook grecaptcha.render
  let _grecaptcha;
  Object.defineProperty(window, 'grecaptcha', {
    set(val) {
      _grecaptcha = val;
      const origRender = val.render;
      val.render = function(container, params) {
        window.__captchaDetected = {
          type: 'recaptcha',
          sitekey: params.sitekey,
          callback: params.callback?.name,
          container: typeof container === 'string' ? container : container.id,
        };
        return origRender.apply(this, arguments);
      };
    },
    get() { return _grecaptcha; },
  });
});

await page.goto('https://example.com/signup');

// Trigger the CAPTCHA (click, scroll, etc.)
await page.click('#show-form');

// Wait for detection
await page.waitForFunction(() => window.__captchaDetected !== null, {
  timeout: 30000,
});

const detected = await page.evaluate(() => window.__captchaDetected);
console.log('Detected:', detected);
// { type: 'recaptcha', sitekey: '6Le-wvkS...', callback: 'onCaptcha', container: 'recaptcha-box' }

Python (Selenium) : En attente de CAPTCHA paresseux

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

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

# Trigger the CAPTCHA loading
submit = driver.find_element(By.ID, "submit-btn")
submit.click()

# Wait for CAPTCHA to appear
try:
    captcha_el = WebDriverWait(driver, 30).until(
        EC.presence_of_element_located((
            By.CSS_SELECTOR,
            ".g-recaptcha, .cf-turnstile, .h-captcha"
        ))
    )
    sitekey = captcha_el.get_attribute("data-sitekey")
    captcha_class = captcha_el.get_attribute("class")

    if "g-recaptcha" in captcha_class:
        captcha_type = "recaptcha"
    elif "cf-turnstile" in captcha_class:
        captcha_type = "turnstile"
    else:
        captcha_type = "hcaptcha"

    print(f"Type: {captcha_type}, Sitekey: {sitekey}")
except Exception:
    print("No CAPTCHA appeared within 30 seconds")

En attente d'une iframe (reCAPTCHA)

# reCAPTCHA loads an iframe even when the div exists but the script is still loading
WebDriverWait(driver, 30).until(
    EC.presence_of_element_located((
        By.CSS_SELECTOR,
        "iframe[src*='recaptcha'], iframe[src*='challenges.cloudflare.com']"
    ))
)
print("CAPTCHA iframe loaded")

Détection complète + flux de résolution

import requests
import time

def detect_and_solve(driver, api_key, trigger_action=None):
    """Detect a lazy-loaded CAPTCHA, solve it, and inject the token."""

    # 1. Trigger the CAPTCHA
    if trigger_action:
        trigger_action(driver)

    # 2. Wait for it to appear
    captcha_el = WebDriverWait(driver, 30).until(
        EC.presence_of_element_located((
            By.CSS_SELECTOR,
            ".g-recaptcha, .cf-turnstile, .h-captcha"
        ))
    )

    sitekey = captcha_el.get_attribute("data-sitekey")
    page_url = driver.current_url
    captcha_class = captcha_el.get_attribute("class")

    # 3. Determine type and method
    if "g-recaptcha" in captcha_class:
        method, key_param, token_field = "userrecaptcha", "googlekey", "g-recaptcha-response"
    elif "cf-turnstile" in captcha_class:
        method, key_param, token_field = "turnstile", "sitekey", "cf-turnstile-response"
    else:
        method, key_param, token_field = "hcaptcha", "sitekey", "h-captcha-response"

    # 4. Solve with CaptchaAI
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": api_key, "method": method,
        key_param: sitekey, "pageurl": page_url, "json": "1",
    }).json()

    task_id = resp["request"]
    for _ in range(24):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": api_key, "action": "get", "id": task_id, "json": "1"
        }).json()
        if result["status"] == 1:
            token = result["request"]
            break

    # 5. Inject
    driver.execute_script(f"""
        const el = document.querySelector('textarea[name="{token_field}"], input[name="{token_field}"]');
        if (el) el.value = arguments[0];
    """, token)

    return token

Dépannage

Problème Parce que Corriger
CAPTCHA n'apparaît jamais Mauvaise action de déclenchement Inspectez la page pour trouver ce qui déclenche le CAPTCHA
La clé du site est nulle L'élément existe mais le script ne s'est pas exécuté Attendez l'iframe CAPTCHA, pas seulement le div
L'observateur le manque CAPTCHA était déjà là Vérifiez les éléments existants avant de configurer l'observateur
Délai d'attente CAPTCHA se charge uniquement pour les vrais utilisateurs Utilisez un navigateur complet avec une empreinte digitale réaliste

FAQ

Comment savoir si un CAPTCHA est chargé paresseux ?

Afficher la source de la page (Ctrl+U). Si le div ou le script CAPTCHA n'est pas là mais apparaît lorsque vous interagissez avec la page, il est chargé paresseux.

Est-ce que cela fonctionne avec les navigateurs sans tête ?

Oui, avec des réserves. Certains sites chargent uniquement des CAPTCHA pour les navigateurs non-headless. Utilisez headless: 'new' dans Puppeteer ou des plugins furtifs.


Résolvez n'importe quel CAPTCHA avec CaptchaAI

Obtenez votre clé API surcaptchaai.com.


Guides associés

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