Як я обійшов Slide CAPTCHA за допомогою Computer Vision and Selenium

💡 Усі статті, обговорення, новини про Python — в одному місці. Приєднуйтесь до Python спільноти!

Мене звати Дмитро, я займаюсь збором даних з інтернету та автоматизацією процесів, пов’язаних з цим.

Також мене захоплює комп’ютерний зір і його здатність розв’язувати складні проблеми. Думав над тим, як його можна використати у веб-скрапінгу — не тільки для парсингу таблиць чи витягування тексту з зображень, але й для обходу захисту від ботів на вебсайтах. Ідея «боти проти ботів» здається водночас кумедною та трохи страшною...

Я почав шукати модель, яка могла б допомогти обійти CAPTCHA, і після деяких досліджень знайшов одну, яка здалася перспективною. Більше про цю модель можна прочитати тут. Тепер перейдемо до кроків, які я зробив, і того, що я отримав.

1. Знайти сайт з CAPTCHA та завантажити модель

Я знайшов сайт для тестування рішення CAPTCHA: цей демонстраційний проєкт на CodePen. Щодо моделі, яку я використовую, можна прочитати більше тут. Для управління рухами миші під час слайдінгу я використовую клас Controller з бібліотеки pynput.mouse, яка надає точне керування курсором.

Пропущу кроки налаштування залежностей, оскільки нічого надзвичайного не використовував. Але якщо вам потрібні деталі, не соромтесь питати в коментарях!

Код для цього кроку:

import math
import random
from time import sleep

from selenium import webdriver 
from selenium.webdriver.common.by import By

from pynput.mouse import Button, Controller

from ultralytics import YOLO

CAPTCHA_URL = "https://2captcha.com/demo/geetest"

mouse = Controller()

2. Відкрити браузер, перейти за URL CAPTCHA і натиснути на кнопку CAPTCHA

Наступним кроком є відкриття браузера та перехід за URL CAPTCHA_URL. Крок простий, тому не будемо витрачати на нього багато часу.

driver = webdriver.Firefox()
driver.maximize_window()
driver.get(CAPTCHA_URL)

sleep(10) 

try:
    btn = driver.find_element(By.CLASS_NAME, "geetest_radar_btn")
    btn.click()
    sleep(3)  # Wait for CAPTCHA to load
except Exception as e:
    raise Exception("CAPTCHA button was not found!") from e

3. Знайти та зберегти зображення CAPTCHA

Далі ми знаходимо елемент зображення CAPTCHA на сторінці та зберігаємо його як файл. Це зображення ми використаємо з нашою моделлю для обчислення відстані між шматочками пазла.

element = driver.find_element(By.CLASS_NAME, "geetest_fullpage_click_box")
captcha_file_name = "geetest_canvas_slice.png"
element.screenshot(captcha_file_name)

sleep(3)

4. Обчислити відстань між шматочками пазла

На цьому етапі ми використовуємо нашу навчену модель для передбачення положень шматочків пазла, що дає змогу обчислити відстань між ними.

results = model.predict(
            source=captcha_file_name,
            device='cpu',
            conf=0.8,
            imgsz=[416, 416], 
        )

box_with_max_conf = max(results, key=lambda x: x.boxes.conf.max())
box_with_conf = box_with_max_conf.boxes.data.tolist()

Якщо перевірити box_with_conf, ви побачите список списків. Кожен список містить дані про шматочок пазла, де 0.0 в кінці позначає тіньовий шматочок, а 2.0 — оригінальний шматочок. Наприклад:

[[141.16949462890625, 117.8351058959961, 199.66102600097656, 166.53150939941406, 0.9436098337173462, 0.0], [13.732378959655762, 117.82501220703125, 73.385498046875, 165.75665283203125, 0.9163503646850586, 2.0]]

Далі ми позначаємо ці шматочки та обчислюємо відстань між ними.

# Ensure we have both pieces identified by the model
assert len(box_with_conf) == 2, "Found only one piece of puzzle!"
shadow = [el for el in box_with_conf if el[-1] == 0.0][0]
origin = [el for el in box_with_conf if el[-1] != 0.0][0]

# Calculate the horizontal distance, adjusting for resolution difference
distance = (shadow[0] - origin[0])*0.791

Застосовуємо коефіцієнт масштабування (0.791), щоб врахувати різницю в розмірах між збереженим знімком екрана та вхідним розміром моделі (швидше за все у вас він буде інший).

5. Перемістити мишу на кнопку слайдера і натискати

На цьому етапі ми знаходимо кнопку слайдера, обчислюємо її позицію і потім переміщаємо мишу на неї, щоб симулювати натискання.

# Locate the slider button and get its location and size
slider = driver.find_element(By.CLASS_NAME, "geetest_slider_button")
location = slider.location
size = slider.size

# Calculate the start position (center of the button, slightly adjusted for accuracy)
start_x = location['x'] + size['width'] / 2  
start_y = location['y'] + size['height'] * 2  # Adjust to a better starting point for clicking

sleep(1)
mouse.press(Button.left)

6. Порахувати значення для кожного кроку

Хоча й без цієї додаткової логіки все має працювати, я подумав, що було б більш схоже на людську поведінку, якщо рухи починатимуться швидше і згодом сповільнюватимуться. Це досягається шляхом обчислення кроків за допомогою геометричної прогресії, що симулює більш плавний, природний рух.

def geometric_progression_steps(initial_value, threshold=1e-12):
    if initial_value <= 0:
        raise ValueError("Initial value must be positive")
    if threshold <= 0:
        raise ValueError("Threshold must be positive")
    
    steps = math.ceil((math.log(threshold) - math.log(initial_value)) / math.log(0.5))
    current_value = initial_value
    values_per_step = []

    for _ in range(steps):
        current_value /= 2
        values_per_step.append(current_value)

    return values_per_step

# Calculate the values for each step based on the distance
values_per_step = geometric_progression_steps(distance)

7. Останній крок: перемістити пазл

В останньому кроці виконуємо рух миші для вирішення CAPTCHA. Додаючи значення з values_per_step до початкової позиції, ми симулюємо рух слайдера. Використовуємо випадкову затримку, щоб рухи були більш схожі на людські.

# Move the puzzle piece by adjusting the position with values from the progression
for value in values_per_step[:]:    
    start_x += value  # Move horizontally by the current step value
    
    # Set the mouse position to the new coordinates
    mouse.position = (start_x, start_y)
    
    # Add a slight delay between movements to mimic human interaction
    sleep(random.uniform(0.05, 0.1))  

# Release the left mouse button to drop the piece
mouse.release(Button.left)

# Wait a moment before closing the browser to ensure the action completes
sleep(5) 
driver.quit()  # Close the browser session

img

Підсумок

Не знаю, як ви, але я отримав велике задоволення від цього проєкту. Завжди захопливо бачити, як комп’ютерний зір та автоматизація можуть об’єднуватися для розв’язання реальних проблем, як-то обхід CAPTCHA! І цікаво, який наступний вигляд матиме капча 🤔

👍ПодобаєтьсяСподобалось6
До обраногоВ обраному3
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Підписатись на коментарі