Назойливая игра - разметка данных для google. Если, занимаетесь сбором доступной информации с ресурсов, не принадлежащим вам, и не сумели реализовать решение для преодоления этой преграды, советы от начинающего разработчика вам помогут. Опишу один из способов, основанный на детекторе объектов, хорошо справляется с типом 4x4, хуже с 3x3. Использую архитектуру YOLO, "золотая середина" точности/производительности, подход одинаков для всех детекторов. В коммерческом продукте стоит использовать "ансамбль" нейронных сетей, к детектору добавить классификацию каждой ячейки, это повысит общую точность с приемлемой производительностью. Также эту задачу можно решить, использую обучение с подкреплением A2C/DQN или любую современную архитектуру, трансформеры, генеративно-состязательные сети.
Примерный алгоритм
Поиск позиции кнопки
Имитация движения мыши к координатам кнопки "Я не робот"
Имитация нажатия кнопки "Я не робот"
Получение картинки ( с сервера google, посылается целой )
Получение класса искомого объекта ( из html )
Получение координат ячеек ( из html )
Решение
Активация ячеек исходя из решения
Нажатие готово/далее/подтвердить
Обработать
Если reCaptcha продолжается, повторить с 4 по 10.
Полезные инструменты в Python
pynput
selenium
numpy
tensorflow
opencv
scipy
beautiful soup, можно обойтись одним selenium
Код найден в просторах моей нейронной сети, значит что то видел в интернет сети, живу не в вакууме
from selenium import webdriver from selenium.webdriver.firefox.options import Options from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.by import By from pynput.mouse import Button, Controller import scipy.interpolate as si import numpy as np import cv2 import time import random import os import io import base64 # использование B-сплайна для имитации человеческих движений мыши def human_like_mouse_move(action, start_element): points = [[6, 2], [3, 2],[0, 0], [0, 2]]; points = np.array(points) x = points[:,0] y = points[:,1] t = range(len(points)) ipl_t = np.linspace(0.0, len(points) - 1, 100) x_tup = si.splrep(t, x, k=1) y_tup = si.splrep(t, y, k=1) x_list = list(x_tup) xl = x.tolist() x_list[1] = xl + [0.0, 0.0, 0.0, 0.0] y_list = list(y_tup) yl = y.tolist() y_list[1] = yl + [0.0, 0.0, 0.0, 0.0] x_i = si.splev(ipl_t, x_list) y_i = si.splev(ipl_t, y_list) startElement = start_element action.move_to_element(startElement); action.perform(); c = 5 i = 0 for mouse_x, mouse_y in zip(x_i, y_i): action.move_by_offset(mouse_x,mouse_y); action.perform(); print("Move mouse to, %s ,%s" % (mouse_x, mouse_y)) i += 1 if i == c: break; # "конфигурирование" selenium def my_proxy(PROXY_HOST,PROXY_PORT): fp = webdriver.FirefoxProfile() fp.set_preference("network.proxy.type", 1) fp.set_preference("network.proxy.socks",PROXY_HOST) fp.set_preference("network.proxy.socks_port",int(PROXY_PORT)) fp.update_preferences() options = Options() options.headless = True # отключаем визуализацию происходящего return webdriver.Firefox(executable_path="geckodriver/geckodriver", options=options, firefox_profile=fp) # с tor прокси, reCaptcha будет вечной, # хорошо для экспериментов proxy = my_proxy("127.0.0.1", 9050) proxy.get("https://www.google.com/search?q=apple")
После запроса, по понятным многим причинам, появляется reCaptcha.
# reCaptcha отображается в iframe's # переключение iframe №1 proxy.switch_to.frame(proxy.find_elements_by_tag_name("iframe")[0]) # находим кнопку "Я не робот" check_box = WebDriverWait(proxy, 10).until(EC.element_to_be_clickable((By.ID ,"recaptcha-anchor"))) time.sleep(2) # имитируем нажатие кнопки "Я не робот action = ActionChains(proxy); human_like_mouse_move(action, check_box) check_box.click() time.sleep(2)

Получаем информацию для дальнейшей обработки: ссылка на изображение, класс поиска, тип reCaptcha.
# переключение iframe №2 proxy.switch_to.default_content() iframes = proxy.find_elements_by_tag_name("iframe") proxy.switch_to.frame(iframes[2]) html = proxy.page_source # получаем ссылку изображения, тип reCaptcha try: img_rc = proxy.find_elements_by_xpath('//img[@class="rc-image-tile-33"]')[0] t_type = 3 except IndexError: img_rc = proxy.find_elements_by_xpath('//img[@class="rc-image-tile-44"]')[0] t_type = 4 # получаем искомый класс try: required_class = proxy.find_elements_by_xpath('//div[@class="rc-imageselect-desc-no-canonical"]/strong')[0].text except IndexError: required_class = proxy.find_elements_by_xpath('//div[@class="rc-imageselect-desc"]/strong')[0].text time.sleep(2)

Скачиваем изображение с сервера, не самым "элегантным способом". Встраиваем код javascript, что бы выполнить XMLHttpRequest, полученный ответ отобразить в canvas, для дальнейшей передачи в python строкой base64. После получения base64, преобразовываем в numpy array. Альтернативное получение изображения, описано в статье https://habr.com/ru/post/449236/
answ = proxy.execute_script(''' var img = new Image(); var cnv = document.createElement('canvas'); cnv.id = 'tutorial'; img.onload = function(){ cnv.height = img.height; cnv.width = img.width; console.log(cnv.width, cnv.height, img.width, img.height); cnv.getContext('2d').drawImage(img, 0, 0); } var request = new XMLHttpRequest(); request.open('GET', arguments[0].src); request.responseType = 'blob'; request.onload = function() { var reader = new FileReader(); reader.readAsDataURL(request.response); reader.onload = function(e){ img.src = e.target.result; }; }; request.send(); var child = document.body.appendChild(cnv); ''', img_rc) time.sleep(4) answ = proxy.execute_script(''' cnv = document.getElementById('tutorial'); return cnv.toDataURL('image/jpeg').substring(22); ''') nparr = np.asarray(bytearray(io.BytesIO(base64.b64decode(answ)).read()), dtype=np.uint8) img_np = cv2.imdecode(nparr, cv2.IMREAD_COLOR)

Получены все нужные компоненты, для ответа на вопрос: "в каком квадрате находиться искомый объект". На этом ресурсе не целесообразно в очередной раз описывать архитектуру YOLO детектора, поэтому демонстрирую фрагменты кода нужные этой статье.
def draw_boxes(image, boxes, scores, labels, classes, detection_size, search_class): """ :param boxes, shape of [num, 4] :param scores, shape of [num, ] :param labels, shape of [num, ] :param image, :param classes, the return list from the function `read_coco_names` """ new = np.ones(shape=image.shape, dtype=np.float32) ans = [] if boxes is None: return ans, image, new for i in range(len(labels)): # for each bounding box, do: bbox, score, label = boxes[i], scores[i], classes[labels[i]] bbox_text = "%s %.2f" %(label, score) # convert_to_original_size detection_size, original_size = np.array(detection_size), np.array(image.shape[1]) ratio = float(original_size) / float(detection_size) bbox = list((bbox.reshape(2,2) * ratio).reshape(-1)) coord = [abs(int(x)) for x in bbox] # встраиваем наш плохой код o0 = coord[0] o1 = coord[1] o2 = coord[2] o3 = coord[3] # создаем маску искомых объектов if search_class == label.split('\n')[0]: new[o1:o3, o0:o2, :] = 2 ans.append(classes[labels[i]]) return ans, image, new # основная функция для получения ответа def imcr(i, col, activation_threshold = 1): answ = [] im_w, im_h, im_c = i.shape w, h = im_w//col, im_h//col sZero = i[0:w, 0:h,:].size num = 0 for wi in range(0, col): for hi in range(0, col): num += 1 P_R = (np.sum(i[wi*w:(wi+1)*w, hi*h:(hi+1)*h, :]) / sZero) * 100 P_R = P_R - 100 if activation_threshold < int(P_R): answ.append(num) else: pass return answ def ocr(img_np, required_class, t_type): # img_np -> YOLO -> B,C # выполняем функцию не максимального подавления boxes, scores, labels = cpu_nms(B, C, len(classes), max_boxes=1000, score_thresh=0.4, iou_thresh=0.5) # встроил свой плохой код в чужой код визуализации данных result, img, z_image = draw_boxes(img_np, boxes, scores, labels, classes, yolo_image_shape, required_class) # наша основная функция answ = imcr(np.array(z_image), t_type)
Наша основная функция, imcr(image_array, type_captcha, activation_threshold) - получая маску искомого объект, функция проверяет процент заполнения в каждой ячейке по заданному условию
параметры:
image_array - маска объекта
type_captcha - тип картинки, пример 4 (4x4)
activation_threshold - заданное условие активации, по умолчанию 1%возвращает:
номера искомых ячеек в списке
Получаем ответ, имитируем нажатие нужной ячейки, подтверждаем.
answ_ocr = ocr(img_np, required_class, t_type) ids = proxy.find_elements_by_xpath('//td[@class="rc-imageselect-tile"]') for i in answ_ocr: ids[i].click() # поиск кнопки подтверждения confirm_btn = WebDriverWait(proxy, 4).until(EC.element_to_be_clickable((By.XPATH ,'//button[@id="recaptcha-verify-button"]'))) #Нахожу кнопку action = ActionChains(proxy); human_like_mouse_move(action, confirm_btn) # двигать курсор к кнопке confirm_btn.click() # нажать
Это не детальное руководство, а советы с фрагментами кода. Если применять предложенный подход, с другими алгоритмами, точность 93% и выше. Используя детектор, ваша главная задача повысить точность детектора (YOLO, RCNN, SSD). Также, одно из условий успешного обхода google captcha, использование "чистых proxy". У меня не особо получается преобразовывать свои мысли в текст, надеюсь попытка успешна, и моя статья появиться на этом ресурсе.
