О чем это
В этой статье мы разберемся, что такое "робот", поймем, как они помогают операционистам, напишем и запустим простого робота на Python.
Исходный код робота и данные для работы можно скачать здесь.
Макросы на стероидах
Робот имитирует действия человека, сидящего за компьютером.
Он может открывать программы, нажимать в них на кнопки, читать и вводить данные.
То, что сейчас называется роботом, впервые стало популярно в 80-х годах прошлого века под названием "макрос": при нажатии определенной комбинации клавиш макрос вместо оператора вводил и считывал необходимые данные из текстового терминала, что помогало форматировать текст и выполнять другие рутинные операции.
С появлением графических интерфейсов макросы на время остановились в развитии. Прорыв случился только с появлением пакета Microsoft Office '97 в 1997 году с появлением возможности записывать действия пользователя и превращать их в исполняемый код на Visual Basic for Applications.
Сейчас макросы наиболее активно используются в онлайн-играх, для автоматизации тестирования и в офисных пакетах.
В 2010 начинается бурный переход от самописных решений к коробочным и облачным решениям, которые не всегда легко изменить под потребности клиента. Например, довольно трудно как технически, так и организационно из облачного решения обратиться к учетной системе компании и вытянуть из нее какие-либо справочные данные.
Для решения подобных задач часто привлекают операционистов. Например, в самом простом случае, операционист может открыть окно локальной программы и запись за записью синхронизировать справочник с другой облачной программой.
Сценарии могут быть и более сложными, причем нередко требуется применение интеллекта операциониста.
Задача нашего первого робота
Наш первый робот будет имитировать работу операциониста CRM в сценарии "обогати клиентские данные в локальной CRM полем ИНН из облачного сервиса".
Процесс AS IS (как работает операционист CRM до появления робота)
Операционист открывает локальную CRM-систему. Для нашего учебного примера для простоты мы не будем устанавливать промышленную CRM, а воспользуемся приложением Microsoft Office Excel, в котором будет открыт файл с полями "Фамилия", "Имя", "Отчество", "Дата рождения" и "Номер паспорта".
Операционист запускает браузер и переходит на страницу https://service.nalog.ru/inn.do
Операционист читает правила сервиса и соглашается с ними нажимая соответствующую галочку.
Сайт отображает форму поиска ИНН.
Операционист переключается в окно Excel, находит ячейку с фамилией и нажимает клавиши Ctrl + C, копируя данные в буфер обмена.
Операционист переключается в браузер и вставляет в него поле данные из буфера обмена.
Пункты 4-5 операционист повторяет для полей "Имя", "Отчество", "Дата рождения" и "Номер паспорта".
Операционист нажимает на сайте кнопку "Отправить запрос".
Сайт какое-то время думает и затем отображает ИНН.
Операционист выделяет ИНН, копирует его в буфер обмена и переносит его обратно в Excel.
Операционист повторяет шаги выше для остальных записей о пользователе.
Процесс TO BE (как будет работать робот)
Робот находит окно Excel и перемещает курсор в левый верхний угол (в начало клиентских данных). Конечно, робот мог бы прочесть Excel-файл напрямую, но для учебного примера мы предположим, что этой возможности нет, что робот имеет доступ только к пользовательскому интерфейсу CRM и вынужден имитировать действия оператора.
Робот запускает браузер и открывает в нем ссылку https://service.nalog.ru/inn.do. Если появляется окно с приглашением принять условия обслуживания, то робот имитирует нажатие на эту галочку.
Робот переключается в окно Excel и последовательно копирует все клиентские данные из ячеек Excel в буфер обмена, а из буфера обмена - в массив в памяти робота.
Робот переключается в браузер и последовательно получает ИНН для каждой клиентской записи, для этого:
Вставляет данные из памяти в форму на сайте https://service.nalog.ru/inn.do.
Нажимает кнопку "Отправить запрос".
Ждет пока сайт отобразит ИНН во всплывающей подсказке.
Копирует ИНН из всплывающей подсказки в память программы рядом с данными о клиенте.
Робот переключается в окно Excel, находит столбец "ИНН" и вставляет ИНН из памяти программы в соответствующие ячейки.
Выбираем язык программирования
Робота можно написать практически на любом современном языке программирования. Помимо этого в свободной продаже можно найти лицензии на целые пакеты роботизации и существуют сценарии, при которых их использование экономически оправдано.
Мы будем использовать язык Python, поскольку мы в конечном счете заменяем операциониста и со временем для этого могут потребоваться инструменты для программирования искусственного интеллекта, которые в Python присутствуют в полном объеме.
Помимо этого язык Python является простым в освоении и на нем проходит обучение программированию в Minecraft. Я знаю это потому, что мой семилетний сын программирует на Python в Minecraft и, кстати, одно из его любимых развлечений - роботизировать в Minecraft рутинные задачи, например, рыть тоннели к алмазам и строить место для ночлега.
Язык Python используется многими компаниями и банками, в том числе для задач роботизации он используется и в МКБ.
Подготовка
Для работы потребуется установить:
Excel для имитации CRM
Google Chrome для доступа к сайту https://service.nalog.ru/inn.do
Python 3, его можно скачать с сайта https://www.python.org/downloads/
PyWinAuto для имитации работы операциониста в Excel и любом другом не-браузерном приложении
pip3 install pywinauto
selenium web driver для имитации работы операциониста в Google Chrome. Для установки этого пакета необходимо выполнить два шага:
Скачать исполняемое приложение ChromeWebDriver отсюда https://chromedriver.chromium.org/downloads , при этом обратите внимание на версию - она должна совпадать с версией Google Chrome
Установить пакет для Python3
pip3 install selenium
Переходим к программированию
Для программирования робота нам потребуется запускать приложения, копировать из них данные в буфер обмена, вставлять в них данные из буфера обмена и нажимать на кнопки.
В обычной жизни это делает операционист.
При программировании робота эти действия имитируют библиотеки pywinauto для классических приложений и selenium — для браузерных.
При работе этих приложений со стороны будет создаваться ощущение, что за компьютером кто-то сидит: конечно, мышь по столу ездить не будет и кнопки на клавиатуре не будут продавливаться, но на мониторе будет кипеть бурная деятельность: данные будут вводиться, экраны переключаться, текст будет копироваться и вставляться — почти как при работе настоящего операциониста.
Из плохих новостей — компьютером в этот момент нельзя будет пользоваться: за ним "сидит" робот.
Код CrmAppAgent
Вся логика работы с CRM упакована в класс CrmAppAgent.
В нем реализованы как методы перемещения курсора, копирования и вставки данных, так и более высокоуровневые методы для получения списка записей и вставки ИНН напротив записей о клиенте.
from pywinauto import Application
from pywinauto import clipboard
import time
class CrmAppAgent:
def __init__(self):
window_title_regular_expression = ".*data.*"
excel_app = Application(backend="uia").connect(title_re=window_title_regular_expression)
excel_window = excel_app.window(title_re=window_title_regular_expression)
self.excel_app = excel_app
self.excel_window = excel_window
def deselect(self):
excel_window = self.excel_window
excel_window.type_keys('{ESC}')
def move_cursor_to_top_left_corner(self):
excel_window = self.excel_window
excel_window.type_keys('^{HOME}')
def move_cursor_down(self):
excel_window = self.excel_window
excel_window.type_keys('{DOWN}')
def move_cursor_to_first_person_cell(self):
excel_window = self.excel_window
self.move_cursor_to_top_left_corner()
self.move_cursor_down()
def read_cell_contents(self):
excel_window = self.excel_window
excel_window.type_keys('^c')
time.sleep(0.1)
cell_data = clipboard.GetData()
result = cell_data.rstrip()
return result
def move_cursor_right(self):
excel_window = self.excel_window
excel_window.type_keys('{RIGHT}')
def move_cursor_to_first_left_cell(self):
excel_window = self.excel_window
excel_window.type_keys('{HOME}')
def read_person(self):
excel_window = self.excel_window
last_name = self.read_cell_contents()
if not last_name:
return None
self.move_cursor_right()
first_name = self.read_cell_contents()
self.move_cursor_right()
middle_name = self.read_cell_contents()
self.move_cursor_right()
birthday = self.read_cell_contents()
self.move_cursor_right()
passport = self.read_cell_contents()
self.move_cursor_down()
self.move_cursor_to_first_left_cell()
return {
"last_name": last_name,
"first_name": first_name,
"middle_name": middle_name,
"birthday": birthday,
"passport": passport
}
def read_persons(self):
excel_window = self.excel_window
result = []
self.deselect()
self.move_cursor_to_first_person_cell()
while True:
person = self.read_person()
if person:
result.append(person)
else:
break
return result
def move_cursor_to_first_inn(self):
excel_window = self.excel_window
self.move_cursor_to_top_left_corner()
self.move_cursor_down()
for i in range(5):
self.move_cursor_right()
def fill_inns(self, inns):
excel_window = self.excel_window
self.move_cursor_to_first_inn()
for inn in inns:
excel_window.type_keys(inn)
self.move_cursor_down()
Код InnAppAgent
Логика работы с приложением https://service.nalog.ru/inn.do упакована в класс InnAppAgent.
Работа с этим приложением ведется через selenium web driver, что позволяет находить элементы на HMTL-странице веб-приложения по идентификаторам и код получается чище и короче, нежели код роботизации классического приложения.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys
import os
class InnAppAgent:
def __init__(self):
browser = self.get_browser()
self.browser = browser
browser.implicitly_wait(10)
SERVICE_URL = 'https://service.nalog.ru/inn.do'
browser.get(SERVICE_URL)
accept_terms_and_conditions_page_shown = browser.current_url != SERVICE_URL
if accept_terms_and_conditions_page_shown:
self.accept_terms_and_conditions()
def get_browser(self):
options = webdriver.ChromeOptions()
options.add_argument("--start-maximized")
current_folder = os.getcwd()
web_driver_executable_name = "chromedriver.exe"
web_driver_executable_path = "{}\\{}".format(current_folder, web_driver_executable_name)
result = webdriver.Chrome(executable_path=web_driver_executable_path, chrome_options=options)
return result
def accept_terms_and_conditions(self):
browser = self.browser
browser.find_element(By.XPATH, '//a[@class="checkbox checkbox-off"]').click()
browser.find_element(By.XPATH, '//button[@id="btnContinue"]').click()
def fill_person_data(self, person):
browser = self.browser
input_data = {
"fam": person["last_name"],
"nam": person["first_name"],
"otch": person["middle_name"],
"bdate": person["birthday"],
"docno": person["passport"]
}
for element_id, input_value in input_data.items():
element = browser.find_element(By.ID, element_id)
element.clear()
for symbol in input_value:
element.send_keys(symbol)
time.sleep(0.1)
def submit_data(self):
browser = self.browser
browser.find_element(By.ID, 'btn_send').click()
def read_inn(self):
browser = self.browser
previous_inn_element = browser.find_element(By.ID, "resultInn")
previous_inn = previous_inn_element.text
WebDriverWait(driver=browser, timeout=10, poll_frequency=1).until(lambda drv: drv.find_element(By.ID, "resultInn").text != previous_inn)
inn_element = browser.find_element(By.ID, "resultInn")
result = inn_element.text
return result
def submit_data_and_read_inn(self):
browser = self.browser
self.submit_data()
return self.read_inn()
def find_inn(self, person):
browser = self.browser
self.fill_person_data(person=person)
result = self.submit_data_and_read_inn()
return result
def find_inns(self, persons):
browser = self.browser
return [self.find_inn(person=person) for person in persons]
Код сценария робота
Бизнес-логика по взаимодействию роботов вынесена в класс EnrichPersonsWithInnsScenario.
Сценарий очень короткий, поскольку все нюансы взаимодействия с приложением CRM и приложением https://service.nalog.ru/inn.do вынесена в классы-агенты.
class EnrichPersonsWithInnsScenario:
def __init__(self, crm_app_agent, inn_app_agent):
self.crm_app_agent = crm_app_agent
self.inn_app_agent = inn_app_agent
def run(self):
crm_app_agent = self.crm_app_agent
persons = crm_app_agent.read_persons()
inn_app_agent = self.inn_app_agent
inns = inn_app_agent.find_inns(persons=persons)
crm_app_agent.fill_inns(inns=inns)
crm_app_agent = CrmAppAgent()
inn_app_agent = InnAppAgent()
enrich_persons_with_inns_scenario = EnrichPersonsWithInnsScenario(crm_app_agent=crm_app_agent, inn_app_agent=inn_app_agent)
enrich_persons_with_inns_scenario.run()
Финальный листинг
Исходный код робота и данные для работы можно скачать здесь: https://github.com/vasiliy-mikhailov/robot_tutorial
Запуск программы
Откройте файл data.xlsx при помощи Excel. Робот будет искать окно с названием "data", поэтому переименовывать файл нельзя.
Опционально: поменяйте в Excel тестовые данные клиента на свои. Если этого не сделать, то сайт https://service.nalog.ru/inn.do не сможет найти ИНН и сценарий не дойдет до конца. Реальные данные не включены в учебный пример по соображениям соблюдения закона о персональных данных.
Сохраните скрипт robot.py в любую папку.
Положите в эту же папку файл chromedriver.exe.
Перейдите в эту папку и выполните в ней команду.
python3.exe robot.py
Вы увидите как запустится браузер и робот примет в нем условия обслуживания.
Затем откроется окно Excel и робот начнет перемещаться по ячейкам и копировать их содержимое.
После того как робот скопирует все ячейки, переключитесь в окно браузера и посмотрите как робот вводит эти данные в форму и получает ИНН.
Если робот сможет вычислить все ИНН, то он перейдет обратно в Excel и заполнит ячейку с ИНН.
Обратите внимание на то, что робот может не выполнить работу до конца. Наиболее частые ошибки: невозможность скопировать данные в буфер обмена с ошибкой "Доступ запрещен" и зависание сервиса https://service.nalog.ru/inn.do на стадии поиска ИНН.
Поздравляю, что дальше?
Вы только что написали своего первого робота на языке программирования Python.
Обязательно покажите его домочадцам и посмотрите на их удивленные глаза, когда компьютер сам будет открывать окна и нажимать на клавиши — это бесценно.
Для принятия решения о том, стоит ли заниматься этим дальше, нужно найти задачи для роботизации в компании и убедить руководство в том, что это выгодно.
Поиск задач под роботизацию
Если вы выходите домой в 21:00, а операционисты все еще сидят, подойдите к ним и посмотрите, что они делают. Вполне может быть, что они выполняют рутинные операции по вводу данных, тогда весьма вероятно, что это ваши будущие клиенты. Присмотритесь и к другим шаблонным процессам в компании. Подумайте, как можно улучшить работу.
Экономика вопроса
Роботизация имеет смысл, если стоимость работы команды разработчиков окупается экономией от сокращения ручного труда операционистов и их высвобождения для решения других задач.
Простые роботы могут быть написаны Junior-разработчиком, однако, когда количество роботов растет, возникает потребность в размещении их на серверах, организации автоматического процесса поставки и мониторинга исполнения. Для организации этого процесса и формирования правильной архитектуры обычно требуется Senior-разработчик.
Роботы капризны, и за ними нужно приглядывать. Сократить затраты на сопровождение роботов помогут системы автоматизированного развертывания и мониторинга, которые может сделать Senior-разработчик.
Развиваем технические навыки
Для более глубокого изучения рекомендую прочесть и перенабрать своими руками примеры кода из книга "Автоматизация рутинных задач с помощью Python".
Это поможет набрать необходимую скорость перед тем, как появится первый реальный заказ на роботизацию.
Удачи в изучении Python!