Un BLS CAPTCHA vous demande de cliquer, dans une grille 3×3, sur les cases correspondant à un code numérique affiché à l'écran — par exemple « 664 ». L'automatiser ne consiste pas à « comprendre » chaque image une par une : il suffit de transmettre les neuf cellules et ce code à un solveur, qui renvoie la liste des cases à cocher. C'est le rôle de l'API CaptchaAI, avec un taux de réussite élevé sur ce format.
Ce format est surtout associé aux portails de BLS International, utilisés pour la prise de rendez-vous de visa dans de nombreux consulats — un contexte familier aux lecteurs francophones d'Afrique du Nord. Ce guide détaille, dans l'ordre, comment extraire la grille, isoler le code d'instruction et récupérer les indices à cliquer, avec des exemples en Python et en Node.js. Périmètre sûr : n'automatisez que votre propre démarche, sur un environnement que vous êtes autorisé à utiliser, dans le respect des conditions du service et de vos obligations RGPD.
Prérequis
Avant de vous lancer, réunissez les éléments suivants :
- Une clé API CaptchaAI valide, avec du solde disponible sur votre compte.
- Un navigateur piloté par Selenium (Python) ou Puppeteer (Node.js).
- L'accès à la page qui affiche la grille, dans un contexte que vous êtes autorisé à automatiser.
- De quoi convertir les images en base64 lorsqu'elles ne sont pas déjà servies sous forme d'URI de données.
Anatomie d'un BLS CAPTCHA : grille 3×3 et code d'instruction
Un BLS CAPTCHA repose sur trois éléments :
- Une grille 3×3 de 9 cellules d'image.
- Un code d'instruction numérique (par exemple 664, 123 ou 546) qui désigne les cellules à sélectionner.
- Un numérotage des cellules de gauche à droite, puis de haut en bas :
1 2 3
4 5 6
7 8 9
Le code indique au solveur quel motif chercher ; la réponse est un tableau d'indices (de 1 à 9) correspondant. Votre script se contente de relayer le code, puis d'appliquer la sélection reçue, sans jamais avoir à l'interpréter lui-même.
Étape 1 : extraire la grille d'images et le code d'instruction
Première brique : récupérer les 9 images (converties en base64 au besoin) et lire le code d'instruction dans la page. En Python, avec Selenium :
import base64
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com/bls-protected-page")
# Find the grid container
grid_cells = driver.find_elements(By.CSS_SELECTOR, ".captcha-grid img")
images = []
for cell in grid_cells:
src = cell.get_attribute("src")
if src.startswith("data:image"):
images.append(src)
else:
# Download and convert to base64
import requests
img_data = requests.get(src).content
b64 = base64.b64encode(img_data).decode()
images.append(f"data:image/png;base64,{b64}")
# Extract the instruction code
instruction_el = driver.find_element(By.CSS_SELECTOR, ".captcha-instruction")
instruction_code = instruction_el.text.strip()
# e.g., "664" or parsed from "Select all boxes with number 664"
import re
code_match = re.search(r'(\d{3,})', instruction_code)
instruction = code_match.group(1) if code_match else instruction_code
print(f"Instruction: {instruction}")
print(f"Images extracted: {len(images)}")
Le motif (\d{3,}) capte aussi bien un code brut (« 664 ») qu'une phrase du type « Select all boxes with number 664 ». La version Node.js, avec Puppeteer, dessine chaque image sur un canvas pour l'exporter en base64 :
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com/bls-protected-page');
// Extract grid images as base64
const images = await page.evaluate(() => {
const cells = document.querySelectorAll('.captcha-grid img');
return Array.from(cells).map(img => {
const canvas = document.createElement('canvas');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
canvas.getContext('2d').drawImage(img, 0, 0);
return canvas.toDataURL('image/png');
});
});
// Extract instruction code
const instruction = await page.evaluate(() => {
const el = document.querySelector('.captcha-instruction');
const match = el.textContent.match(/(\d{3,})/);
return match ? match[1] : el.textContent.trim();
});
console.log(`Instruction: ${instruction}, Images: ${images.length}`);
Vérifiez toujours que la boucle a bien produit neuf entrées : une grille incomplète est la première cause d'échec côté API.
Étape 2 : envoyer la grille à l'API CaptchaAI
Le solveur BLS attend method=bls, le code instructions et les 9 images de image_base64_1 à image_base64_9. Vous envoyez la tâche, récupérez un identifiant, puis interrogez le résultat jusqu'à ce qu'il soit prêt. En Python :
import requests
import time
import json
API_KEY = "YOUR_API_KEY"
# Prepare submission data
data = {
"key": API_KEY,
"method": "bls",
"instructions": instruction,
"json": "1",
}
# Add all 9 images
files = {}
for i, img in enumerate(images):
files[f"image_base64_{i+1}"] = (None, img)
# Submit
resp = requests.post(
"https://ocr.captchaai.com/in.php",
data=data,
files=files
).json()
if resp["status"] != 1:
raise Exception(f"Submit error: {resp['request']}")
task_id = resp["request"]
print(f"Task ID: {task_id}")
# Poll for result
for _ in range(20):
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:
solution = json.loads(result["request"])
print(f"Selected cells: {solution}") # e.g., [1, 4, 7, 8]
break
if result["request"] != "CAPCHA_NOT_READY":
raise Exception(f"Error: {result['request']}")
Tant que le résultat renvoie CAPCHA_NOT_READY, la boucle patiente 5 secondes avant de réinterroger res.php ; la réponse arrive en général en quelques secondes. La même logique en Node.js, avec Axios :
const axios = require('axios');
const FormData = require('form-data');
const form = new FormData();
form.append('key', 'YOUR_API_KEY');
form.append('method', 'bls');
form.append('instructions', instruction);
form.append('json', '1');
images.forEach((img, i) => {
form.append(`image_base64_${i + 1}`, img);
});
const submit = await axios.post('https://ocr.captchaai.com/in.php', form, {
headers: form.getHeaders(),
});
const taskId = submit.data.request;
// Poll
let solution = null;
for (let i = 0; i < 20; i++) {
await new Promise(r => setTimeout(r, 5000));
const poll = await axios.get('https://ocr.captchaai.com/res.php', {
params: { key: 'YOUR_API_KEY', action: 'get', id: taskId, json: 1 }
});
if (poll.data.status === 1) {
solution = JSON.parse(poll.data.request);
break;
}
}
console.log('Selected cells:', solution); // e.g., [2, 4, 7]
Conservez l'intervalle de 5 secondes : interroger l'endpoint plus souvent n'accélère pas la résolution et consomme des requêtes inutiles.
Étape 3 : cliquer sur les cellules renvoyées
L'API renvoie un tableau d'indices commençant à 1, alors que le DOM est indexé à partir de 0 : d'où le - 1 avant chaque clic. En Python :
# Selenium — click the cells returned by CaptchaAI
grid_cells = driver.find_elements(By.CSS_SELECTOR, ".captcha-grid .cell")
for cell_index in solution:
# cell_index is 1-based
grid_cells[cell_index - 1].click()
# Submit the form
submit_btn = driver.find_element(By.CSS_SELECTOR, ".captcha-submit")
submit_btn.click()
L'équivalent Puppeteer parcourt le même tableau, clique sur chaque cellule, puis valide le formulaire :
// Puppeteer
const cells = await page.$$('.captcha-grid .cell');
for (const idx of solution) {
await cells[idx - 1].click();
}
await page.click('.captcha-submit');
Si les mauvaises cellules sont cochées, c'est presque toujours ce décalage d'indice qu'il faut revérifier en premier.
Le workflow complet en une fonction
Voici les trois étapes réunies dans une seule fonction réutilisable — extraction, résolution via l'API, puis clic — que vous pouvez appeler dès qu'une grille apparaît :
def solve_bls_captcha(driver, api_key):
"""Extract, solve, and submit a BLS CAPTCHA."""
import base64, requests, time, json, re
# 1. Extract images
grid_cells = driver.find_elements(By.CSS_SELECTOR, ".captcha-grid img")
images = []
for cell in grid_cells:
src = cell.get_attribute("src")
if src.startswith("data:image"):
images.append(src)
else:
img_data = requests.get(src).content
b64 = base64.b64encode(img_data).decode()
images.append(f"data:image/png;base64,{b64}")
# 2. Extract instruction
el = driver.find_element(By.CSS_SELECTOR, ".captcha-instruction")
match = re.search(r'(\d{3,})', el.text)
instruction = match.group(1)
# 3. Submit to CaptchaAI
data = {"key": api_key, "method": "bls", "instructions": instruction, "json": "1"}
files = {f"image_base64_{i+1}": (None, img) for i, img in enumerate(images)}
resp = requests.post("https://ocr.captchaai.com/in.php", data=data, files=files).json()
task_id = resp["request"]
# 4. Poll
for _ in range(20):
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:
solution = json.loads(result["request"])
break
# 5. Click cells
clickable = driver.find_elements(By.CSS_SELECTOR, ".captcha-grid .cell")
for idx in solution:
clickable[idx - 1].click()
return solution
Cette fonction encapsule tout le flux : il ne vous reste qu'à lui passer votre driver Selenium et votre clé API, puis à traiter la valeur de retour.
Dépannage
| Problème | Cause | Correctif |
|---|---|---|
ERROR_BAD_PARAMETERS |
Images manquantes ou code absent | Vérifiez les 9 URI base64 et le champ instructions |
| Mauvaises cellules cliquées | Décalage indice / cellule | Confirmez la numérotation 1 à 9 et le - 1 à l'indexation du DOM |
| Images non chargées | Restriction cross-origin sur les src |
Téléchargez côté serveur, puis convertissez en base64 |
| Code d'instruction vide | Instruction rendue dans une image | Passez le visuel à l'OCR au lieu de lire .captcha-instruction |
En cas d'échec répété, isolez d'abord l'étape fautive : journalisez le nombre d'images extraites, le code d'instruction lu et la réponse brute de res.php avant de conclure quoi que ce soit.
Bonnes pratiques
- Envoyez toujours la grille complète : le solveur compare les neuf cellules au code d'instruction.
- Journalisez le code envoyé et la réponse reçue pour diagnostiquer rapidement les écarts.
- Prévoyez une nouvelle tentative si la première soumission expire, sans réduire l'intervalle de polling.
- Ne collectez que les données strictement nécessaires à votre workflow, conformément au RGPD.
FAQ
Faut-il envoyer les neuf images à chaque tentative ?
Oui. Le solveur attend la grille complète, de image_base64_1 à image_base64_9, pour comparer les 9 cellules au code. Une grille incomplète renvoie souvent ERROR_BAD_PARAMETERS.
Combien de temps prend la résolution d'un BLS CAPTCHA ?
En général quelques secondes. Le script interroge res.php toutes les 5 secondes et s'arrête dès que status vaut 1. Les délais varient selon la charge.
Que faire si l'API renvoie ERROR_BAD_PARAMETERS ?
Ce code signale presque toujours une charge incomplète : une image manquante, un base64 mal formé ou un champ instructions vide. Revérifiez que la boucle d'extraction a produit 9 entrées et que le code a été isolé avant l'envoi.
Puis-je automatiser un BLS CAPTCHA sur un portail de rendez-vous visa ?
Uniquement dans le cadre de votre propre démarche, sur un environnement autorisé, et dans le respect des conditions du service et de vos obligations RGPD. Ce guide couvre la mécanique technique, pas la levée des règles d'un portail.
Résolvez votre première grille BLS
Créez un compte et récupérez votre clé API sur captchaai.com pour reprendre les exemples ci-dessus sur votre propre grille.