Обычная капча / Простая капча / Simple captcha



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

Подобные капчи решаются очень просто. Находим в исходном коде страницы ссылку на изображение и отправляем ее в API сервиса разгадывания капчи. Основная сложность состоит в том, что чаще всего ссылка на изображение скрыта или само изображение как таковое отсутствует, а видим мы сгенерированный текст с применением различных стилей и шумов или вообще изображение закодировано стандартом кодирования base64.

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

В Selenium есть замечательная функция .screenshot(«image.png»), которая делает снимок нужного нам веб-элемента. С ее помощью проще всего извлекать изображения с таких капч.

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

Код ниже открывает страницу, на которой есть капча, и делает скриншот области, где расположена капча. Затем он отправляет сохраненное изображение в API-сервис распознавания. Там человек, вручную или при помощи самописной нейронной сети, распознает ее и вводит то, что изображено на капче. Далее код вставляет полученный код в соответствующее поле «input» и нажимает кнопку. После всех этих манипуляций мы получаем весь HTML-код страницы.

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

Ссылка c этим кодом на github — https://github.com/nefelsay/captcha/blob/main/simple_captcha_1.py.
Пример кода:

import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from twocaptcha import TwoCaptcha

#Функция принимает путь до изображения и отправляет изображение в API rucaptcha, после получения разгаданной капчи, функция возвращает код который находился на капче

def sender_solve(path):
    solver = TwoCaptcha('***********API code***********')
    print('2) Изображение отправлено для разгадывания:')
    result = solver.normal(path)
    print('3) От API пришёл ответ: ', result)
    return result['code']

with webdriver.Chrome() as browser:
    browser.get('') # ссылка на страницу с капчей

    #используем неявное ожидание для полной загрузки страницы и появления всех элементов.
    browser.implicitly_wait(10)

    #Это условие if проверит в исходном коде ключевую фразу для определения на странице капчи.
    if 'Подтвердите, что вы не робот' in browser.page_source:
        #Находим элемент где располагается изображение капчи и делаем его скриншот, .screenshot('img.png') сохраняет скриншот в папке с проектом,
        #но можно написать и полный путь, тогда его необходимо будет передать в solver()

        browser.find_element(By.CSS_SELECTOR, 'div[class="chakra-form-control css-1sx6owr"]').find_element(By.TAG_NAME,'img').screenshot('img.png')
        print('1) Скриншот области успешно сделан')

        #Находим текстовое поле и вставляем код который возвращает функция solver(),
        browser.find_element(By.ID, 'field-:r0:').send_keys(sender_solve('img.png'))

        #Находим кнопку "Подтвердить" и кликаем по ней.
        browser.find_element().click()

    #Собираем список наименований всех товарных позиций на странице
    name_card = [x.text for x in browser.find_elements(By.CLASS_NAME, 'css-5ev4sb')]
    print('4)',name_card)

В этом коде нет проверки на правильность ответа, который приходит от API rucaptcha. В случае, если ответ неправильный, код вернет пустой список  name_card = []. Самый простой способ проверки — добавить проверку длины списка:  if len(tt) > 0:. То есть, если длина списка больше нуля, то все в порядке, печатаем найденную информацию. Иначе, повторяем процедуру и сообщаем о неверной попытке.

solver.report(id, False) #При неудачной попытке
solver.report(id, True) #При удачной попытке

В нашем коде функция sender_solve() возвращает словарь {‘captchaId’: ‘72447681441’, ‘code’: ‘gbkd’}, из которого мы можем спокойно получить ID капчи.

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

solver.report(sender_solve('img.png')['captchaId'], False)

В коде ниже я удалил повторяющиеся комментарии и добавил новые. Кроме этого, код немного изменился. Я добавил цикл while True: для того, чтобы решение заняло столько попыток, сколько требуется. Также я добавил проверку полученных данных и отчеты о неудачах. Запустите этот код и наблюдайте за его выполнением, попытайтесь понять его логику.

import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from twocaptcha import TwoCaptcha

solver = TwoCaptcha('***********API code***********')
#Создаём словарь для того чтобы положить в него результат ответа на API {'captchaId': '72447681441', 'code': 'gbkd'}
dict_resut = {}
img_name = 'img.png'

def sender_solve(path=img_name):
    print('2) Изображение отправлено для разгадывания:')
    result = solver.normal(path)
    print('3) От API пришёл ответ: ', result)
    #API вернёт словарь {'captchaId': '72447681441', 'code': 'gbkd'}
    #Обновляем словарь для дальнейшего извлечения ID капчи и отправки репорта
    dict_resut.update(result)
    return result['code']

with webdriver.Chrome() as browser:
    browser.get('') # ссылка на страницу с капчей
    browser.implicitly_wait(10)
    if 'Подтвердите, что вы не робот' in browser.page_source:
        browser.find_element(By.CSS_SELECTOR, 'div[class="chakra-control css-1sxowr"]').find_element(By.TAG_NAME,'img').screenshot(img_name)
        print('1) Скриншот области успешно сделан')
        browser.find_element(By.ID, 'field-:r0:').send_keys(sender_solve())
        browser.find_element(By.CSS_SELECTOR, 'button[class="chakra-button css-1wq39mj"]').click()
        #Запускаем бесконечный цикл на случай, если неудачных попыток будет несколько.
        while True:
            #Пытаемся спарсить список товаров
            name_card = [x.text for x in browser.find_elements(By.CLASS_NAME, 'css-5ev4sb')]
            #Если спарсить не получилось, и список name_card не содержит элементов, то переходим в блок else
            if len(name_card) > 0:
                #Репортим о успешном решении
                solver.report(dict_resut['captchaId'], True)
                print(f"Отправлен репорт об успешном разгадывании. id:{dict_resut['captchaId']}")
                print('4)',name_card)

                #После успешного получения данных, прерываем цикл
                break
            else:
                print("Капча решена неверно, повторяем попытку")
                #Репортим о неудачной попытке разагадать капчу
                print(f"Отправлен репорт о неуспешной попытке. id:{dict_resut['captchaId']}")
                solver.report(dict_resut['captchaId'], False)

                #Делаем новый скриншот, т.к. после неуспешной попытки капча обновилась
                print('Повторный скриншот области')
                browser.find_element(By.CSS_SELECTOR, 'div[class="chakra-form-control css-1sx6owr"]').find_element(
                    By.TAG_NAME, 'img').screenshot('img.png')

                #Отправляем скриншот повторно
                browser.find_element(By.ID, 'field-:r0:').send_keys(sender_solve())

                #Кликаем кнопку повторно
                browser.find_element(By.CSS_SELECTOR, 'button[class="chakra-button css-1wq39mj"]').click()

                #Пытаемся наполнить список name_card найденными элементами
                [name_card.append(x.text) for x in browser.find_elements(By.CLASS_NAME, 'css-5ev4sb')]

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



Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: