Время прочтения: 7 мин.

Существует большое количество методов для автоматического сбора и обработки больших объемов данных из веб-ресурсов. Однако иногда недоступно извлечение данных с помощью автоматизированного кода, выполняющего GET-запросы с последующим парсингом HTML-кода, и его преобразованием в необходимый формат, также, как и все смежные методы. В таких случаях на помощь может прийти эмулятор действия пользователя («кликер», «бот», «робот»).

Была поставлена следующая задача: необходимо получить информацию на определенном веб-ресурсе (упустим его наименование) на основе файла Excel со списком определенных данных (на 2000 строк). В связи с тем, что все методы автоматизированного парсинга и скрапинга невозможны, а время ограничено, обращаем внимание, что нам подойдет и эмулятор мыши и клавиатуры.

Для работы простого эмулятора нам потребуются библиотеки:

import time, pyperclip
import pandas as pd
import pyautogui
import os
import re

Но есть одно препятствие: ввод капчи, которая имеет вид:

На просторах интернета можно найти множество алгоритмов по работе с капчами в рамках машинного обучения, и мы воспользуемся одним из них. Ознакомиться с нашим выбором можно здесь — https://habr.com/ru/post/464337/

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

import urllib
import urllib.request
HEADERS = {'accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
          'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36'}
for i in range(1, 1001):
    html = r'здесь была ссылка на ресурс'
    urllib.request.urlretrieve(html, r"здесь пусть сохранения файлов/number" + str(i) + '.jpg')

Как видно из кода, для обучения модели было скачено 1000 изображений. Далее их необходимо обработать: разрезать и распределить по директориям в зависимости от чисел на изображениях. Воспользуемся кодом ниже:

from PIL import Image
import glob
for file in glob.glob(r'\*.jpg')
    img = Image.open(file)
    area1=(2,0,29,45) #спереди,сверху,справа,снизу)
    img1 = img.crop(area1) 
    area2=(29,0,51,45) #спереди,сверху,справа,снизу)
    img2 = img.crop(area2)
    area3=(51,0,71,45) #спереди,сверху,справа,снизу)
    img3 = img.crop(area3)
    area4=(71,0,91,45) #спереди,сверху,справа,снизу)
    img4 = img.crop(area4)
    area5=(91,0,110,45) #спереди,сверху,справа,снизу)
    img5 = img.crop(area5)    
    img1.save(r"путь сохранения\\"+filename+"1"+".jpg")
    img2.save(r"путь сохранения\\"+filename+"1"+".jpg")
    img3.save(r"путь сохранения\\"+filename+"1"+".jpg")
    img4.save(r"путь сохранения\\"+filename+"1"+".jpg")
    img5.save(r"путь сохранения\\"+filename+"1"+".jpg")

Для обучения модели формируем следующую логику директории:

В dat мы разобрали числа по папкам от 0 до 9, где в каждой из папок располагается соответствующее число. Например, 0 (ноль):

В сумме получилось по 500 изображений в каждой из папок.

train_simple.py – файл для обучения модели.

И самое главное — директория output, где будет располагаться обученная модель.

Подготовительная работа выполнена, осталось обучить модель. Открываем командную строку, переходим в нашу директорию и вводим команду:

python train_simple.py --dataset dat --model output/simple_nn.model --label-bin output/simple_nn_lb.pickle --plot output/simple_nn_plot.png

Модель для распознавания капчи обучена. Видим следующий результат:

Это означает, что на обучающем наборе достигнута верность — 98,2%, на контрольном — 97,8 % и на тестовом — 97,8 %. Ориентируемся на последнее значение. Посмотрим на визуальную интерпретацию модели в директории output файл simple_nn_plot.png:

Помним, что 100% результата не будет. В целом, распознавание 5 чисел занимает около 10 сек.

Итак, подключим файл для распознавания к нашему основному коду:

import sys
sys.path.append('путь до директории, где лежит директория с обученной моделью')
import captcha

Следующая задача – выяснить все необходимые координаты для работы эмулятора мыши.

import pyautogui
import keyboard      
while True:
    if keyboard.is_pressed('space'):
        mouse_x, mouse_y = pyautogui.position()
        print(mouse_x, mouse_y)  

Суть проста: наводим мышкой на нужную позицию, нажимаем «пробел» и получаем нужные нам координаты. Перейдем к основному коду эмулятора:

Пишем функцию для упрощения общего кода.

def click(x, y, n, key):
    pyautogui.click(x, y, button=key) 
    time.sleep(n)

time.sleep(2) 
# Переходим в папку с проектом, где лежит обученная модель
os.chdir(путь до директории проекта\project')

# Формируем два датафрейма: в первом содержится наш файл, а второй будет содержать итоговую информацию после отработки кода.
df1 = pd.read_excel(r'путь до excel-файла\значения_для_проверки_.xlsx')
df1=df1[:]
df2 = pd.DataFrame()

Логика проста: данные из столбца нашего Excel-файла будут вноситься в поле ввода на веб-ресурсе, после – распознается и вводится капча, нажимается кнопка проверки и текстовый результат проверки вносится во второй датафрейм, формируя таким образом таблицу Excel с итоговыми данными, взятыми из веб-ресурса.

for i in range(0, len(df1[' id '])):  
 	 # 1) Поле ввода значения из таблицы excel:
    click(174, 461, 1, 'left')     
    pyautogui.typewrite(str(df1['id'][i])) # вводит в поле ввода из столбца «id» значение n-ой строки
    time.sleep(2)

	 # 2) Блок работы с капчей
    while True:       
        click(150, 679, 1, 'left') # поле ввода капчи   
        
        click(361, 673, 2, 'right') # ПКМ по изображению
        click(501, 688, 3, 'left') # скачать
        click(582, 565, 5, 'left') # сохранить
        
        # Изображение скачено, теперь необходимо эту распознать капчу. Для этого мы используем ранее подключенный созданный нами модуль
      numbers = captcha.content() 

        click(150, 679, 3, 'left') # поле ввода капчи
        pyautogui.typewrite(numbers) # вводит распознанную капчу
        
        time.sleep(3) 
        click(323, 773, 5, 'left')  # кнопка «проверить»         
        pyperclip.copy('') # очищаем буфер обмена

        # 3) Следующий блок проверки. Если капча введена не верно, то возвращаемся в начало цикла
        pyautogui.click(140, 713, button='left', clicks=3) # проверка
        time.sleep(3)         
        pyautogui.hotkey('ctrl', 'c') # копируем  
        valid = pyperclip.paste()   
        time.sleep(1) 
    
        if re.findall(r'ведите\s', valid):   
            os.remove("number.jpg")
        else:   
            os.remove("number.jpg")
            break                
  
# 5) Если капча введена верно, то сохраняем значения в переменные.
    pyautogui.click(190, 156, button='left', clicks=3)
    time.sleep(2) 
    pyautogui.hotkey('ctrl', 'c')
    time.sleep(2) 
    result = pyperclip.paste()
    time.sleep(1) 
    
    pyautogui.click(177, 187, button='left', clicks=3)
    time.sleep(2) 
    pyautogui.hotkey('ctrl', 'c')
    time.sleep(2) 
    description = pyperclip.paste()
    time.sleep(1) 
    
# 6) Блок формирования результирующей таблицы. Формируем строку в новый датафрейм.
    new_row = {'id': df1['id'][i], 'number': df1['text'][i], 'text': result, 'description': description}
    print(i, new_row)
    df2 = df2.append(new_row, ignore_index=True)    
    pyautogui.scroll(1000) # Перемещаемся снова наверх.

Код содержит несколько логических блока:
• Работа с полем ввода с данными из исходной таблицы Excel. DataFrame(df1) исходной таблицы Excel, где лежат данные, содержит столбец «id». Значения в столбце содержат до 10 символов. Работа кода предполагает, что берется значение из строки «i» столбца «id» в соответствии с номером круга цикла. Пока значения в столбце не закончатся, цикл не прервется.
• Работа с капчей. Создаем бесконечный цикл, условием выхода из которого будет правильный ввод капчи. Мы качаем изображение капчи, вызываем функцию из сформированного ранее модуля, далее происходит попытка ввода. Если капча введена верно, то цикл прерывается и мы переходим к следующему логическому блоку, если нет, то возвращаем в начало цикла, удаляя скаченное изображение.
• Немного о логике проверки ввода. В случае неудачного ввода, на главной странице появляется предупреждение и не пропадает, более того, текст предупреждения можно копировать. Последним и воспользуемся. Чтобы проверить введена ли капча правильно, мы ищем текст по конкретным координатам и проверяем, что там сказано, если советуют повторить попытку ввода, то, соответственно, возвращаемся в начало цикла.
• Блок извлечения данных при верном вводе капчи. Все просто: мы берем и копируем нужный нам текст по определенным координатам, сохраняя этот текст в переменные.
• Блок формирования результирующей таблице. Создается словарь с нужными нам данными. Этот словарь представляет собой одну строку в таблице.
Теперь мы автоматизировали сбор данных и подключили модель машинного обучения для распознавания капчи, что позволило увеличить скорость работы в сравнении с человеческими ресурсами.
Удачи в кодировании 😊