Un CAPTCHA en grille BLS renvoie des positions dans une grille d'images, pas un simple token. Tout le travail consiste à traduire ces positions en clics sur les bonnes cellules — ou en masque de bits — au format exact attendu par le formulaire, puis à les injecter sans casser la session.
Les portails de rendez-vous BLS (demandes de visa) sont un cas fréquent pour les lecteurs du Maghreb francophone ; ce guide suppose que vous automatisez votre propre démarche, sur un environnement autorisé. La suite couvre la cartographie de la grille, la résolution via l'API CaptchaAI, l'analyse de la réponse et son injection avec Selenium.
Trois types de défis en grille BLS
La forme de la réponse — et donc la manière de l'injecter — dépend directement du type de grille affiché. On en rencontre trois.
Ordre des images
Les images doivent suivre une séquence précise (chiffres croissants, ordre alphabétique). La réponse est une liste ordonnée de positions : l'ordre des clics compte autant que le choix des cellules.
Sélection d'images
Il faut cliquer toutes les images correspondant à une consigne, par exemple « sélectionnez les images contenant du texte ». La réponse est un ensemble de positions, sans ordre imposé.
Correspondance de motif
Il faut repérer les images qui reproduisent un motif ou un échantillon affiché. Là encore, la réponse se résume à une liste de positions dans la grille.
Cartographier la grille : index et coordonnées
Chaque cellule porte un index à plat, numéroté de gauche à droite et de haut en bas ; les grilles BLS font en général 3×3 ou 4×4. Les deux fonctions ci-dessous font l'aller-retour entre un index et un couple (ligne, colonne), indispensable dès que le site raisonne en coordonnées plutôt qu'en index bruts.
# grid_mapping.py
# BLS grids typically use 3x3 or 4x4 layouts
# Each cell maps to an index:
# 3x3 grid:
# [0] [1] [2]
# [3] [4] [5]
# [6] [7] [8]
# 4x4 grid:
# [0] [1] [2] [3]
# [4] [5] [6] [7]
# [8] [9] [10] [11]
# [12] [13] [14] [15]
def grid_position(index, cols=3):
"""Convert flat index to row, column."""
return index // cols, index % cols
def index_from_position(row, col, cols=3):
"""Convert row, column to flat index."""
return row * cols + col
# Example: For a 3x3 grid, position (1, 2) = index 5
print(grid_position(5, cols=3)) # (1, 2)
print(index_from_position(1, 2)) # 5
Résoudre la grille via l'API CaptchaAI
On soumet le défi avec la méthode bls sur in.php, puis on interroge res.php jusqu'à récupérer les positions. Le paramètre instructions transmet la consigne affichée à l'utilisateur, ce qui aide à lever les grilles ambiguës. Prévoyez un premier délai avant l'interrogation : la résolution n'est pas instantanée.
# solve_bls_grid.py
import requests
import time
import os
import json
def solve_bls_grid(sitekey, pageurl, instructions=None):
"""Solve a BLS grid CAPTCHA and get response indices."""
api_key = os.environ["CAPTCHAAI_API_KEY"]
payload = {
"key": api_key,
"method": "bls",
"sitekey": sitekey,
"pageurl": pageurl,
"json": 1,
}
if instructions:
payload["instructions"] = instructions
resp = requests.post(
"https://ocr.captchaai.com/in.php",
data=payload,
timeout=30,
)
result = resp.json()
if result.get("status") != 1:
raise RuntimeError(f"Submit failed: {result.get('request')}")
task_id = result["request"]
time.sleep(10)
for _ in range(30):
resp = requests.get("https://ocr.captchaai.com/res.php", params={
"key": api_key,
"action": "get",
"id": task_id,
"json": 1,
}, timeout=15)
data = resp.json()
if data.get("status") == 1:
return data["request"]
if data["request"] != "CAPCHA_NOT_READY":
raise RuntimeError(data["request"])
time.sleep(5)
raise TimeoutError("BLS grid solve timeout")
Analyser la réponse : JSON, index ou masque de bits
L'API renvoie les positions sous plusieurs formes : un tableau JSON, une liste d'index séparés par des virgules, ou une valeur unique. La première fonction normalise ces trois cas en une seule liste d'entiers ; la seconde la convertit dans le format qu'attend le formulaire — certains sites veulent un masque de bits (010010000) plutôt que des index bruts.
# parse_response.py
import json
def parse_grid_response(solution):
"""Parse CaptchaAI BLS response into actionable grid data."""
# Solution may be JSON or comma-separated indices
if isinstance(solution, str):
try:
parsed = json.loads(solution)
return parsed
except json.JSONDecodeError:
pass
# Try comma-separated indices
if "," in solution:
return [int(x.strip()) for x in solution.split(",")]
# Single value
return [solution]
return solution
def format_for_submission(indices, grid_size=9):
"""Format indices for form submission."""
# Some sites expect a bitmask
bitmask = ["0"] * grid_size
for idx in indices:
if isinstance(idx, int) and 0 <= idx < grid_size:
bitmask[idx] = "1"
return {
"indices": indices,
"bitmask": "".join(bitmask),
"count": len(indices),
}
Injecter la solution avec Selenium
Deux stratégies selon le formulaire : cliquer les cellules dans le bon ordre, ou écrire directement la réponse dans un champ caché. Pour les grilles d'ordre, laissez une pause un peu plus longue entre les clics — un enchaînement trop rapide fait souvent rejeter la séquence côté serveur.
# inject_grid.py
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
def click_grid_cells(driver, indices):
"""Click specific grid cells based on solution indices."""
wait = WebDriverWait(driver, 10)
# Find all grid cells
cells = wait.until(
EC.presence_of_all_elements_located(
(By.CSS_SELECTOR, ".captcha-grid .cell, .bls-grid img, .grid-item")
)
)
for idx in indices:
if isinstance(idx, int) and idx < len(cells):
cells[idx].click()
time.sleep(0.3) # Brief delay between clicks
def set_order_sequence(driver, ordered_indices):
"""Click grid cells in the correct order for ordering challenges."""
wait = WebDriverWait(driver, 10)
cells = wait.until(
EC.presence_of_all_elements_located(
(By.CSS_SELECTOR, ".captcha-grid .cell, .bls-grid img")
)
)
for idx in ordered_indices:
if isinstance(idx, int) and idx < len(cells):
cells[idx].click()
time.sleep(0.5) # Ordering needs pauses between clicks
def inject_hidden_response(driver, solution_value):
"""Set the solution in a hidden input field."""
driver.execute_script("""
var inputs = document.querySelectorAll(
'input[name*="captcha"], input[name*="response"], #captcha-answer'
);
for (var i = 0; i < inputs.length; i++) {
inputs[i].value = arguments[0];
}
""", str(solution_value))
Assembler le flux BLS de bout en bout
Le flux complet enchaîne les étapes précédentes : attendre la grille, lire le sitekey et la consigne, résoudre via l'API, choisir la méthode d'injection selon la présence de cellules cliquables, puis soumettre le formulaire.
# full_flow.py
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
def handle_bls_grid(driver, pageurl):
"""Complete BLS grid CAPTCHA handling."""
wait = WebDriverWait(driver, 15)
# Wait for CAPTCHA to load
captcha = wait.until(
EC.presence_of_element_located(
(By.CSS_SELECTOR, "[data-sitekey], .bls-captcha")
)
)
sitekey = captcha.get_attribute("data-sitekey")
# Get instructions
instructions = None
try:
inst = driver.find_element(By.CSS_SELECTOR, ".captcha-instructions")
instructions = inst.text.strip()
except Exception:
pass
# Solve via CaptchaAI
solution = solve_bls_grid(sitekey, pageurl, instructions)
parsed = parse_grid_response(solution)
# Determine response method
grid_cells = driver.find_elements(
By.CSS_SELECTOR, ".captcha-grid .cell, .bls-grid img"
)
if grid_cells:
# Click-based response
if isinstance(parsed, list) and all(isinstance(x, int) for x in parsed):
click_grid_cells(driver, parsed)
else:
inject_hidden_response(driver, solution)
else:
# Hidden input response
inject_hidden_response(driver, solution)
# Submit
submit = driver.find_element(
By.CSS_SELECTOR, "button[type='submit'], .submit-btn, #verify"
)
submit.click()
return True
Quelle méthode d'injection choisir ?
Trois cas couvrent la quasi-totalité des formulaires BLS. Ce tableau relie chaque type de grille à la forme de réponse attendue et à la fonction à appeler.
| Type de grille | Forme de la réponse | Fonction à appeler |
|---|---|---|
| Ordre des images | Liste ordonnée de positions | set_order_sequence() |
| Sélection d'images | Ensemble de positions | click_grid_cells() |
| Réponse en champ caché | Chaîne ou masque de bits | inject_hidden_response() |
Dépannage
| Problème | Cause | Correctif |
|---|---|---|
| Clics sur les mauvaises cellules | Sélecteur CSS de cellule inadapté | Inspectez le HTML de la grille et ajustez les sélecteurs |
| Séquence d'ordre rejetée | Clics trop rapprochés | Ajoutez 300 à 500 ms entre les clics |
| Format de réponse inattendu | Le site attend un masque de bits, pas des index | Convertissez avec format_for_submission() |
| Grille incomplète à la résolution | Images chargées trop lentement | Attendez le chargement complet avant de résoudre |
FAQ
Quel format de réponse renvoie l'API pour une grille BLS ?
Un tableau JSON de positions, une liste d'index séparés par des virgules, ou une valeur unique, selon le défi. La fonction parse_grid_response() ramène ces trois cas à une seule liste exploitable.
Faut-il cliquer les cellules ou injecter la réponse dans un champ caché ?
Cela dépend du formulaire : si la grille expose des cellules cliquables, reproduisez les clics dans l'ordre ; sinon, écrivez la valeur renvoyée dans le champ caché. L'enchaînement complet détecte automatiquement le bon cas.
Combien coûte la résolution des CAPTCHA BLS avec CaptchaAI ?
La facturation repose sur les threads, pas sur le nombre de résolutions. Le forfait BASIC ($15/mois, 5 threads) couvre 5 résolutions simultanées, avec un nombre illimité de résolutions par thread ; montez en gamme quand vous avez besoin de plus de parallélisme.
Une solution BLS est-elle réutilisable ?
Non. Chaque solution est liée à une session de défi précise ; résolvez une nouvelle grille à chaque tentative plutôt que de rejouer une réponse existante.
Guides connexes
Créez votre compte CaptchaAI et résolvez votre première grille BLS.