Un moniteur de billetterie ne sert à rien s'il se fait bloquer au premier défi CAPTCHA. Dès que vous interrogez régulièrement une page d'événement — concert, match, festival —, la plateforme sert un reCAPTCHA v2, un Cloudflare Turnstile ou une page Cloudflare Challenge. CaptchaAI résout ce défi à la volée : votre script récupère un token valide, le renvoie avec sa requête et lit la disponibilité.
Ce guide construit un moniteur en Python qui détecte le CAPTCHA, le résout via l'API et vous alerte quand l'état d'un événement change. Le tout dans un périmètre sûr : ciblez vos propres environnements de test ou de préproduction (staging), pas la mise en vente réelle d'un tiers.
Les défis que vous allez rencontrer
Sur les plateformes de billetterie, trois protections reviennent le plus souvent :
- reCAPTCHA v2 — la case « Je ne suis pas un robot », parfois suivie d'une grille d'images.
- Cloudflare Turnstile — un défi léger et sans interaction, très répandu.
- Cloudflare Challenge — la page interstitielle « Vérification de votre navigateur ».
CaptchaAI prend en charge ces trois types ; hCaptcha et FunCaptcha, en revanche, ne sont pas encore couverts.
Comment fonctionne la surveillance
La boucle enchaîne quatre étapes :
- interroger la page de chaque événement ;
- résoudre le CAPTCHA s'il apparaît ;
- comparer la disponibilité à la dernière valeur connue ;
- envoyer une alerte en cas de changement.
Configure events → Check availability → CAPTCHA?
↓ Yes
Solve via CaptchaAI → Retry
↓ No
Parse availability → Changed?
↓ Yes
Send alert
Ce qu'il vous faut avant de commencer
| Exigence | Détails |
|---|---|
| Clé API CaptchaAI | captchaai.com |
| Python 3.8+ | Avec requests |
| Proxy | Proxy résidentiel recommandé |
pip install requests
Le proxy résidentiel n'est pas un détail : les sites de billetterie bloquent en priorité les IP de datacenter. Prévoyez également :
- une rotation de proxys pour répartir les requêtes sur plusieurs IP ;
- une persistance de session (cookies) pour ne pas repartir de zéro à chaque passage ;
- un canal d'alerte (e-mail, webhook Slack) pour être prévenu d'un changement d'état.
La fonction de résolution CAPTCHA
Soumettre le défi puis interroger le résultat
Cette fonction générique envoie le défi à l'API, patiente, puis interroge le résultat jusqu'à obtenir le token. L'attente initiale est plus courte pour Turnstile (10 s) que pour reCAPTCHA (20 s).
import requests
import time
API_KEY = "YOUR_API_KEY"
def solve_captcha(method, params):
"""Generic CaptchaAI solver for any supported method."""
params["key"] = API_KEY
params["json"] = 1
submit = requests.post("https://ocr.captchaai.com/in.php", data=params).json()
if submit.get("status") != 1:
raise RuntimeError(f"Submit error: {submit.get('request')}")
task_id = submit["request"]
initial_wait = 10 if method == "turnstile" else 20
time.sleep(initial_wait)
for _ in range(30):
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": task_id, "json": 1
}).json()
if result.get("status") == 1:
return result["request"]
if result.get("request") != "CAPCHA_NOT_READY":
raise RuntimeError(f"Solve error: {result['request']}")
time.sleep(5)
raise TimeoutError("Solve timed out")
Le moniteur de billetterie
Détecter le défi et lire la disponibilité
La classe TicketMonitor conserve une session, détecte le type de défi et renvoie le token dans le champ attendu — g-recaptcha-response pour reCAPTCHA, cf-turnstile-response pour Turnstile. Elle mémorise le dernier état de chaque événement pour n'alerter que sur un vrai changement.
from datetime import datetime
import json
class TicketMonitor:
def __init__(self, proxy=None):
self.session = requests.Session()
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
})
if proxy:
self.session.proxies = {
"http": f"http://{proxy}",
"https": f"http://{proxy}"
}
self.last_status = {}
def check_event(self, event):
"""Check ticket availability for an event, solving CAPTCHAs if needed."""
url = event["url"]
response = self.session.get(url)
# Handle CAPTCHA if detected
if "g-recaptcha" in response.text or "recaptcha" in response.text:
sitekey = self._extract_sitekey(response.text)
if sitekey:
token = solve_captcha("userrecaptcha", {
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": url
})
response = self.session.post(url, data={
"g-recaptcha-response": token
})
elif "cf-turnstile" in response.text:
sitekey = self._extract_turnstile_key(response.text)
if sitekey:
token = solve_captcha("turnstile", {
"method": "turnstile",
"sitekey": sitekey,
"pageurl": url
})
response = self.session.post(url, data={
"cf-turnstile-response": token
})
# Parse availability
availability = self._parse_availability(response.text, event)
# Check for changes
event_key = event["name"]
if event_key in self.last_status:
if availability != self.last_status[event_key]:
self._send_alert(event, availability)
self.last_status[event_key] = availability
return availability
def _extract_sitekey(self, html):
if 'data-sitekey="' in html:
start = html.index('data-sitekey="') + 14
end = html.index('"', start)
return html[start:end]
return None
def _extract_turnstile_key(self, html):
if 'data-sitekey="' in html:
start = html.index('data-sitekey="') + 14
end = html.index('"', start)
return html[start:end]
return None
def _parse_availability(self, html, event):
"""Parse ticket availability. Customize per ticketing site."""
available = "sold out" not in html.lower()
return {
"event": event["name"],
"available": available,
"checked_at": datetime.now().isoformat()
}
def _send_alert(self, event, availability):
"""Send availability change notification."""
status = "AVAILABLE" if availability["available"] else "SOLD OUT"
print(f"[ALERT] {event['name']}: {status}")
def monitor_all(self, events):
"""Check all events and return results."""
results = []
for event in events:
try:
result = self.check_event(event)
results.append(result)
print(f"[OK] {event['name']}: {'available' if result['available'] else 'sold out'}")
except Exception as e:
print(f"[ERROR] {event['name']}: {e}")
return results
# Usage
events = [
{
"name": "Concert - Madison Square Garden - Aug 15",
"url": "https://example-tickets.com/event/12345"
},
{
"name": "Basketball Finals - Game 7",
"url": "https://example-tickets.com/event/67890"
}
]
monitor = TicketMonitor(proxy="user:pass@proxy.example.com:8080")
results = monitor.monitor_all(events)
for r in results:
print(json.dumps(r, indent=2))
Résultat attendu :
[OK] Concert - Madison Square Garden - Aug 15: available
[OK] Basketball Finals - Game 7: sold out
La méthode _parse_availability est volontairement simple : adaptez-la au HTML réel de chaque plateforme.
Planifier les vérifications
Lancez le moniteur à intervalle régulier avec cron plutôt qu'en boucle infinie : un échec ponctuel n'arrête alors pas la surveillance.
# Check every 15 minutes
*/15 * * * * cd /path/to/project && python ticket_monitor.py >> /var/log/tickets.log 2>&1
Un worker OVHcloud ou Scaleway proche de vos cibles garde la latence basse. Espacez les vérifications pour rester sous les seuils de débit.
Bonnes pratiques de surveillance
Quelques réflexes évitent la plupart des blocages :
- Espacez les vérifications : 10 à 30 minutes suffisent pour la plupart des événements ; réservez les intervalles courts aux mises en vente réellement tendues.
- Isolez les erreurs : une exception sur un événement ne doit pas interrompre les autres, comme le fait déjà
monitor_all. - Journalisez chaque passage : un historique daté aide à repérer une IP grillée ou un changement de structure de page.
Dépannage
| Problème | Cause | Correctif |
|---|---|---|
| Un CAPTCHA à chaque vérification | Même IP, session non persistée | Conservez les cookies et alternez des proxys résidentiels |
| Blocage après quelques passages | Limitation de débit atteinte | Allongez l'intervalle et activez la rotation de proxys |
| État de disponibilité erroné | La structure de la page a changé | Adaptez la méthode _parse_availability |
| Résolution CAPTCHA lente | Forte charge côté solveur | Ajoutez une nouvelle tentative avec backoff exponentiel |
FAQ
Combien de threads pour surveiller plusieurs événements en parallèle ?
Un thread traite un CAPTCHA à la fois. Un moniteur séquentiel se contente d'un thread ; pour du simultané, comptez un thread par résolution concurrente. Le plan BASIC ($15/mois, 5 threads) couvre déjà une poignée d'événements en parallèle.
CaptchaAI prend-il en charge hCaptcha sur les sites de billetterie ?
Non — hCaptcha n'est pas encore pris en charge, tout comme FunCaptcha. Concentrez votre moniteur sur reCAPTCHA v2, Cloudflare Turnstile et Cloudflare Challenge, qui couvrent la majorité des défis rencontrés.
Comment réduire la fréquence des CAPTCHA ?
Persistez la session, réutilisez les cookies et faites tourner des proxys résidentiels. Un rythme raisonnable (toutes les 10 à 30 minutes) déclenche nettement moins de défis qu'une salve de requêtes depuis un datacenter.
Que faire face aux files d'attente virtuelles ?
Les salles d'attente sont distinctes du CAPTCHA. Votre moniteur doit repérer la page de file d'attente, patienter puis réessayer. CaptchaAI résout le défi qui apparaît une fois la file franchie, pas la file elle-même.
Est-ce conforme de surveiller la disponibilité des billets ?
Restez sur vos propres environnements de test, respectez les conditions d'utilisation du site ciblé et minimisez les données personnelles collectées, conformément au RGPD. Ce guide vise la validation de parcours en staging.
Créez votre compte CaptchaAI
Démarrez la surveillance de la disponibilité sur captchaai.com et laissez l'API gérer les défis reCAPTCHA v2 et Turnstile au fil de vos vérifications.