О чем это
В этой статье мы разберемся, что такое "робот", поймем, как они помогают операционистам, напишем и запустим простого робота на 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!
Комментарии (9)
aborouhin
29.10.2021 22:17А можно нагуглить описание неофициального API (или за пару часов восстановить его самому, если бы не нагуглилось готовое) и обойтись без
извращенийроботов :)vmihaylov Автор
29.10.2021 23:33+3Действительно в ИТ многие задачи можно решить при помощи разных технических решений.
По факту вопрос выбора технического решения настолько важен, что в модели CMMI ему выделен целый раздел.
Обращаю внимание, что статья не о том, что лучше выбрать и не о том, как лучше выбирать, а о том как написать первого робота если выбор в пользу робота уже сделан.
aborouhin
30.10.2021 02:10В таком случае интересно было бы прочитать и про то, как делался выбор. Потому как в подавляющем случае, когда читаешь о RPA, видишь такую вот недоавтоматизацию и решения, далёкие от оптимальных. Нет, в жизни всегда есть место временным и неидеальным костылям, но когда маркетологи RPA-платформ начинают преподносить их как нечто disruptive и вообще silver bullet от всех бед, - тут невольно начинаешь писать язвительные комментарии под любым постом на тему :)
HeadsetAdapterCo
30.10.2021 17:33+2Есть же отличный инструмент - Sikuli. Уже много лет. Язык программирования (основной) - все тот же Python. Но он помимо "клавиатуры" и "мышки" позволяет еще распознавать изображения, и производить действия основываясь именно на наличии или отсутствии конкретного изображения. Все, описанное выше вполне можно сделать значительно меньшим количеством строк, плюс не привязываться к Chrome.
Я понимаю, что выше описан один из вариантов "автоматизации экранной деятельности", причем как "учебный процесс" он может быть значительно интереснее других вариантов. Но если просто нужно "автоматизировать определенные действия", есть более легкие и простые варианты.
vmihaylov Автор
01.11.2021 17:30+1Да, Вы верно поняли: здесь один из вариантов автоматизации, учебный пример.
Фреймворков действительно много, не было цели их сравнивать.
conopus
Если вы используете Python, то что бы вы могли сказать об использовании Robot Framework для задач RPA? Как по вашему, оправдан ли в этом случае подход Key Driven Development, который используется в Robot Framework?
vmihaylov Автор
Я не использовал этот фреймворк и не могу дать компетентный ответ. Вам лучше обратиться к тем, кто его активно использует. Немного погуглил - фреймворк существует с 2008 года, есть весьма активные гугл группы и большой сайт с тьюториалами.