Skip to content

reCAPTCHA image clicking with Selenium

This guide opens Google’s reCAPTCHA demo page with Selenium 4 and completes the image challenge in a real browser: it reads the question and image, asks reCAPTCHA v2 image recognition (ReCaptchaV2Classification) which tiles to click, and clicks them. Unlike the token approach, the check is completed in the browser itself, which helps on sites where tokens rarely pass.

Without code, the browser extension does the same thing using the same API.

终端窗口
pip install requests selenium pillow
export YESCAPTCHA_CLIENT_KEY=your_client_key

Chrome must be installed. Selenium 4 downloads a matching chromedriver automatically.

  1. Click the checkbox. It is inside the iframe titled “reCAPTCHA”; switch into it and click #recaptcha-anchor.
  2. Switch to the challenge iframe. The challenge is in another iframe whose title contains “challenge”.
  3. Read the question. The bold text in .rc-imageselect-desc-wrapper strong is the target, for example “traffic lights”. The API needs the question ID, so map the name to an ID (/m/015qff) using the table in Question IDs. An English browser UI makes the mapping easiest.
  4. Get the image. The tiles are slices of one large image; download the src of td.rc-imageselect-tile img. Scale 9-tile (3x3) images to 300×300 and 16-tile (4x4) images to 450×450, then Base64-encode.
  5. Recognize. Call createTask with type ReCaptchaV2Classification, image and question. Adding "confidence": 0.5 makes 3x3 images return every matching tile at once, so you don’t need to handle the 1x1 tiles that fade in after clicking.
  6. Click. objects lists tile indexes from 0, left to right and top to bottom; click the matching td elements.
  7. Verify. Click #recaptcha-verify-button. If a new round appears, go back to step 3; when the challenge window closes, you have passed.
  8. Submit the form on the main page.
recaptcha_click_selenium.py
"""Click through a reCAPTCHA v2 image challenge with Selenium 4 and ReCaptchaV2Classification.
Flow: open the page -> click the checkbox -> read the target name and the grid image ->
ask YesCaptcha which tiles to click -> click them -> press Verify. Repeat if a new challenge appears.
Run: pip install requests selenium pillow
export YESCAPTCHA_CLIENT_KEY=your_client_key
python recaptcha_click_selenium.py
Each recognized image is charged (see the price list). reCAPTCHA's page structure can change;
adjust the selectors below if a step stops working.
"""
import base64
import io
import os
import time
import requests
from PIL import Image
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
CLIENT_KEY = os.environ["YESCAPTCHA_CLIENT_KEY"]
PAGE_URL = "https://www.google.com/recaptcha/api2/demo"
# Target names shown in the challenge (English UI) -> question IDs.
# Full list: https://docs.yescaptcha.com/en/tasks/recaptcha-classification/
QUESTION_IDS = {
"taxis": "/m/0pg52", "bus": "/m/01bjv", "buses": "/m/01bjv", "motorcycles": "/m/04_sv",
"tractors": "/m/013xlm", "chimneys": "/m/01jk_4", "crosswalks": "/m/014xcs",
"traffic lights": "/m/015qff", "bicycles": "/m/0199g", "parking meters": "/m/015qbp",
"cars": "/m/0k4j", "bridges": "/m/015kr", "boats": "/m/019jd", "palm trees": "/m/0cdl1",
"mountains or hills": "/m/09d_r", "fire hydrants": "/m/01pns0", "a fire hydrant": "/m/01pns0",
"stairs": "/m/01lynh",
}
def classify(image_b64, question):
task = {"type": "ReCaptchaV2Classification", "image": image_b64, "question": question, "confidence": 0.5}
data = requests.post(
"https://api.yescaptcha.com/createTask",
json={"clientKey": CLIENT_KEY, "task": task},
timeout=30,
).json()
if data.get("errorId"):
raise RuntimeError(f"{data.get('errorCode')}: {data.get('errorDescription')}")
return data["solution"] # synchronous: the solution comes back right away
def grid_image_b64(url, size):
"""Download the challenge image and scale it to the standard size (300 for 3x3, 450 for 4x4)."""
img = Image.open(io.BytesIO(requests.get(url, timeout=30).content)).convert("RGB")
img = img.resize((size, size))
buf = io.BytesIO()
img.save(buf, format="JPEG")
return base64.b64encode(buf.getvalue()).decode()
driver = webdriver.Chrome()
wait = WebDriverWait(driver, 15)
try:
driver.get(PAGE_URL)
# 1. Click the checkbox (inside the "reCAPTCHA" iframe)
wait.until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR, 'iframe[title="reCAPTCHA"]')))
wait.until(EC.element_to_be_clickable((By.ID, "recaptcha-anchor"))).click()
driver.switch_to.default_content()
# 2. Switch to the challenge iframe and solve rounds until it closes
wait.until(EC.frame_to_be_available_and_switch_to_it(
(By.XPATH, '//iframe[contains(@title, "challenge")]')))
for _ in range(5):
target = wait.until(EC.presence_of_element_located(
(By.CSS_SELECTOR, ".rc-imageselect-desc-wrapper strong"))).text.strip().lower()
question = QUESTION_IDS.get(target)
if not question:
print("unsupported target, reloading:", target)
driver.find_element(By.ID, "recaptcha-reload-button").click()
time.sleep(2)
continue
tiles = driver.find_elements(By.CSS_SELECTOR, "td.rc-imageselect-tile")
size = 450 if len(tiles) == 16 else 300
url = driver.find_element(By.CSS_SELECTOR, "td.rc-imageselect-tile img").get_attribute("src")
solution = classify(grid_image_b64(url, size), question)
for index in solution.get("objects", []):
tiles[index].click()
time.sleep(0.3)
time.sleep(1)
driver.find_element(By.ID, "recaptcha-verify-button").click()
time.sleep(3)
driver.switch_to.default_content()
frame = driver.find_element(By.XPATH, '//iframe[contains(@title, "challenge")]')
if not frame.is_displayed(): # challenge closed: passed
break
driver.switch_to.frame(frame)
driver.switch_to.default_content()
driver.find_element(By.ID, "recaptcha-demo-submit").click()
time.sleep(2)
print("passed" if "Verification Success" in driver.page_source else "not passed")
finally:
driver.quit()
  • reCAPTCHA’s page structure changes over time; adjust the selectors if a step stops working.
  • Every recognized image is charged (prices on reCAPTCHA v2 image recognition). Tasks whose clicks were rejected can be reported within 1 hour for a refund.
  • If a target isn’t in the mapping table, click the reload button for another challenge, or ask support to add it.