По роду деятельности я не разработчик, но вращаюсь в этих кругах, так скажем – околоPYTHONная тусовка. Есть знакомые разработчики и даже больше – коллеги.

Как это часто бывает, понадобилось мне организовать решение капчи на Python для одного важного проекта – парсер под Амазон. Было перепробована куча вариантов, израсходована не одна тонна промтов для чата GPT, но никак не поддавалась эта долбанная Амазон капча. В итоге – коллективным разумом (ну как коллективным, просьба была моя, а решение коллеги) был составлен скрипт для обхода капчи на Python, и решение выложить его в общий доступ продиктовано исключительно благими намерениями корыстными побуждениями. Нужны советы, по оптимизации работы этого скрипта, потому что мы с чатом уже все, сдулись. Перепробовали несколько подходов, сломали кучу библиотек, но стабильной работы скрипта в итоге не получили.

Давайте по порядку:

Общее описание скрипта для обхода капчи на Python

Скрипт обхода капчи на Python работает по такому принципу:

  • Импортирует необходимые для работы библиотеки

  • Подстановка прокси

  • Открывает страницу регистрации на Амазон

  • Решение первой капчи (если ее нет – пропуск)

  • Заполнение формы регистрации

  • Решение второй капчи координатным методом

  • Проверка решения капчи

  • Закрытие браузера

Теперь разберемся с каждым пунктом отдельно.

 Необходимые для решения капчи Python библиотеки

Скрипт использует несколько библиотек:

  1. os, base64, BytesIO -  стандартные библиотеки Python, которые используются для работы с файловой системой, кодирования изображений в base64 и работы с потоками байтов (можно сказать, что этот набор библиотек используется для обхода капчи в виде изображения).

  2. seleniumbase.Driver, selenium.webdriver.common.by.By, selenium.webdriver.common.action_chains.ActionChains - Эти библиотеки позволяют управлять браузером через Selenium, выполнять поиск элементов на веб-странице и выполнять сложные действия с ними (например, клик по координатам) (таким образом, этот набор библиотек большей степенью предназначен для решения капчи координатным методом, но не стоит забывать, что в принципе все взаимодействие с капчей зависит в данном скрипте от Селениума, так что это самый важный набор библиотек).

  3. TwoCaptcha: Библиотека для работы с сервисом 2Captcha для автоматического распознавания капчи (естественно, решение капчи Python реализовано через сторонний сервис, в нашем случае сервис 2капча).

Подстановка прокси для функционирования скрипта

Изначально скрипт был настроен таким образом, чтобы прокси брались из файла, но так как я достаточно ленивый (Что? Создать файл для прокси, разобраться в каком формате записывать туда прокси, подготовить эти прокси в нужном формате, да вы издеваетесь?), пришлось добавить возможность использования прокси прямо из кода.

То есть, скрипт сперва проверяет, есть ли файл с прокси и если его нет – подгружает прокси из кода. А если и там нет прокси, то скрипт просто забивает на работу и отключается (прям как некоторые работники – «я человек простой, не поставили задачу – просто листаю видосики»)

В принципе обход капчи без прокси в современном мире уже признак дурного тона (особенно если поток огромный), и неважно на Python ты это делаешь или на другом языке.

Открытие страницы регистрации на Амазон

Далее скрипт открывает страницу регистрации на Амазон (за это отвечает эта функция - driver.uc_open_with_reconnect:). Конечная цель ведь у скрипта – регистрация на сайте, а не распознавание капчи (хотя больше он пока что ничего сделать и не может, если быть до конца честным.

Решение первой капчи на Python

Вот мы наконец-то и добрались до ключевой функции (вернее ее части) - обход капчи либо пропуск, если капчи нет. Речь про простую текстовую Амазон капчу, она то появляется то нет (все зависит от того, какие дни у Амазон).

Работает это так – скрипт при помощи selenium находит изображение капчи на странице и делает его скриншот, после чего преобразует скрин в формат base64 и отправляет получившийся набор букв и символов на сервис 2капча для обхода, а после получения ответа от сервера подставляет текст в специальное поле и жмет кнопку «Продолжить».

Соответсвенно, если обходить нечего (капча не появилась) этот пункт просто пропускается и скрипт переходит к следующему.

Заполнение формы регистрации

Как вы помните, перед тем, как попытаться решить капчу скрипт открыл страницу регистрации, собственно к ней он и возвращается и заполняет регистрационную форму.

Данные для регистрационной формы загружаются прямиком из кода, и это сделано как раз по той причине, что необходимо до конца допилить модуль распознавания капчи.

В идеале, сюда можно дописать свое решение, которое будет брать данные для заполнения формы из файла методом перебора и таким образом в автоматическом режиме можно заполнять много форм.

Обход капчи Python координатным методом

С первой капчей, за все время тестирования этого модуля у меня ни разу не возникло никаких заморочек (ну тупо Амазон был в хорошем расположении духа и ни разу не выдал мне текстовую капчу), а вот со второй появились вопросики, как к скрипту, так и к комьюнити.

Давайте разберемся:

Итак, вторая капча более сложная и требует указания куда нужно кликнуть, чтобы ее обойти. Принцип работы скрипта такой – делается скриншот и отправляется на сервис для получения координат.

Сервис присылает координаты и скрипт, используя ActionChains перемещает курсор на указанные координаты, после чего переключается во фрейм и нажимает кнопку подтверждения.

И вот дальше происходит то самое волшебство тот самый затык. Либо в момент распознавания, либо на шаге проверки иногда происходит провис, то есть капча либо не решается, либо заканчивается время на решение капчи и она обновляется, не успев получить правильный результат. В большинстве случаев все решается, но вот это исключение из большинства случаев мне и не дает покоя…

Ниже приведу сам скрипт, может кому то будет полезным, а может кто-то в комментах даст рекомендацию, что по его мнению можно доработать, чтобы пофиксить этот момент?

Либо, это не исправить, так как ответ такой – «Это же Амазон»…

import os
import base64
from io import BytesIO
from seleniumbase import Driver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from twocaptcha import TwoCaptcha  # pip3 install 2captcha-python

# Прокси для ручного ввода
manual_proxy = "http://login:password@ip:port"  # Замените на ваш прокси

# Функция для чтения прокси из файла
def get_proxy_from_file(file_path):
    if os.path.exists(file_path):
        with open(file_path, 'r') as file:
            proxy = file.read().strip()
            return proxy
    return None

# Попробуем сначала подключить прокси из внешнего файла, если файл отсутствует, используем ручное значение
proxy_file_path = "proxy.txt"  # Имя файла с прокси
proxy = get_proxy_from_file(proxy_file_path) or manual_proxy

my_key = "API Key 2captcha"
solver = TwoCaptcha(my_key, defaultTimeout=70)
agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"

# Инициализация драйвера с прокси
driver = Driver(uc=True, headless=False, proxy=proxy, agent=agent)  # headless=True for invisible mode

try:
    url = "https://www.amazon.com/ap/register?openid.pape.max_auth_age=0&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&pageId=usflex&ignoreAuthState=1&openid.assoc_handle=usflex&openid.mode=checkid_setup&openid.ns.pape=http%3A%2F%2Fspecs.openid.net%2Fextensions%2Fpape%2F1.0&prepopulatedLoginId=&failedSignInCount=0&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&disableLoginPrepopulate=1&switch_account=signin&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0"
    driver.uc_open_with_reconnect(url, 5)
    
    # Решение первой капчи
    try:
        my_img = driver.find_element("body > div > div.a-row.a-spacing-double-large > div.a-section > div > div > form > div.a-row.a-spacing-large > div > div > div.a-row.a-text-center > img")
        print("SOLVE FIRST CAPTCHA...")
        screenshot = my_img.screenshot_as_png
        screenshot_bytes = BytesIO(screenshot)
        base64_screenshot = base64.b64encode(screenshot_bytes.getvalue()).decode('utf-8')
        result = solver.normal(base64_screenshot)
        print('result: ' + str(result))  # Отправка запроса на сервис 2Captcha
        res = result['code']
        driver.find_element(By.ID, "captchacharacters").send_keys(f"{res}")
        driver.find_element(By.CLASS_NAME, "a-button-inner").click()
    except:
        pass

    # Ожидание и заполнение формы
    driver.sleep(1)
    print("Fill out the form")
    driver.find_element(By.ID, "ap_customer_name").send_keys("Alex0053")  # Заполнение формы
    driver.find_element(By.ID, "ap_email").send_keys("some_post43120@gmail.com")
    driver.find_element(By.ID, "ap_password").send_keys("password40000A#")
    driver.find_element(By.ID, "ap_password_check").send_keys("password40000A#")
    driver.find_element(By.ID, "continue").click()
    driver.sleep(10)
    
    # Вторая капча - координатная
    while True:
        try:
            cap_img = driver.find_element(By.ID, "cvf-aamation-challenge-iframe")  # Доработка элемента фрейма
            print("SOLVE SECOND-COORD CAPTCHA...")
            screenshot = cap_img.screenshot_as_png
            screenshot_bytes = BytesIO(screenshot)
            base64_screenshot = base64.b64encode(screenshot_bytes.getvalue()).decode('utf-8')
            element_position = cap_img.location
            result = solver.coordinates(base64_screenshot, lang='en', min_clicks=1, max_clicks=1)
            x = str(result['code']).split(":")[1].split(",")[0].replace("x=", "")
            y = str(result['code']).split(":")[1].split(",")[1].replace("y=", "")
            print('result: ' + str(result))
            x_coord = element_position["x"] + int(x)
            y_coord = element_position["y"] + int(y)
            actions = ActionChains(driver)
            actions.move_by_offset(x_coord, y_coord).click().perform()
            driver.sleep(2)
            actions.reset_actions()
            driver.switch_to_frame(cap_img)
            driver.find_element(By.ID, "amzn-btn-verify-internal").click()
            driver.switch_to.default_content()
            driver.sleep(7)
        except Exception as e:
            print(e)
        try:
            driver.find_element('form[id="verification-code-form"]')
            print("CAPTCHA PASSED!!!")
            break
        except:
            pass

    driver.sleep(3)
    # Последний блок кода, если нужно

except Exception as e:
    print(e)
finally:
    driver.close()
    driver.quit()

Обход капчи Python – раздел которого в коде нет

У Амазон еще есть третий вид капчи, Фанкапча, и я не смог победить ее в данном контексте, поэтому просто выпилил из этого кода на всякий случай. Да и Фанкапча за все время тестирования мне так ни разу не встретилась (но из разговоров мудрейших я знаю что она там есть, где то в чертогах Амазона). Ходят легенды, что есть специально обученный мужик, назовем его просто СОМ, который руками меняет условия для обхода капчи, либо дизайн страницы.

Никто этого СОМа никогда не видел, но у вечерами, когда ITшники собираются у костра этой байкой пугают джунов.

В общем – скрипт Фанкапчу не решает, но готов к конструктиву и предложениям, как сделать так, чтобы начал решать.

Итого вывод: Скрипт работает, обход капчи осуществляется, но встречаются провисы – было бы неплохо, если бы на них указали (желательно без минусения)

Скрипт не решает Фанкапчу, но на первый взгляд и не нужОна нам эта ваш фанкапча, но если нужна – также, буду рад почитать мнение коммьюнити.

Комментарии (2)


  1. uralhi
    25.10.2024 09:05

    А качпча на Амазон вылазит при каждом запросе или ситуативно при превышении частоты запросов? И сам парсинг выполняется как часто?

    В кейсе когда нужно несколько раз в год скачать большой массив данных защищённых капчей я пришел к решению что тратить ресурсы на обход капчи смысла нет - проще сделать подуавтоматизированное решение при котором RPA качает данные, а в случае возникновения капчи тормозит процесс, сигнализирует пользователю и ждёт от него прохождения капчи....

    Ну и поскольку срочности в загрузке данных не было, а необходимость периодически посматривать на ход RPA меня не сильно напрягал - это оказалось оптимальным решением.


    1. Markus_automation Автор
      25.10.2024 09:05

      У меня капча выползала каждый раз. Я думаю это еще от прокси сильно зависит (от их качества)