Пишем первого робота для банка

О чем это

В этой статье мы разберемся, что такое «робот», поймем, как они помогают операционистам, напишем и запустим простого робота на Python.

Исходный код робота и данные для работы можно скачать здесь.

Макросы на стероидах

Робот имитирует действия человека, сидящего за компьютером.

Он может открывать программы, нажимать в них на кнопки, читать и вводить данные.

То, что сейчас называется роботом, впервые стало популярно в 80-х годах прошлого века под названием «макрос»: при нажатии определенной комбинации клавиш макрос вместо оператора вводил и считывал необходимые данные из текстового терминала, что помогало форматировать текст и выполнять другие рутинные операции.

С появлением графических интерфейсов макросы на время остановились в развитии. Прорыв случился только с появлением пакета Microsoft Office ’97 в 1997 году с появлением возможности записывать действия пользователя и превращать их в исполняемый код на Visual Basic for Applications.

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

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

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

Сценарии могут быть и более сложными, причем нередко требуется применение интеллекта операциониста.

Задача нашего первого робота

Наш первый робот будет имитировать работу операциониста CRM в сценарии «обогати клиентские данные в локальной CRM полем ИНН из облачного сервиса».

Процесс AS IS (как работает операционист CRM до появления робота)

  • Операционист открывает локальную CRM-систему. Для нашего учебного примера для простоты мы не будем устанавливать промышленную CRM, а воспользуемся приложением Microsoft Office Excel, в котором будет открыт файл с полями «Фамилия», «Имя», «Отчество», «Дата рождения» и «Номер паспорта».

  • Операционист читает правила сервиса и соглашается с ними нажимая соответствующую галочку.

  • Сайт отображает форму поиска ИНН.

  • Операционист переключается в окно Excel, находит ячейку с фамилией и нажимает клавиши Ctrl + C, копируя данные в буфер обмена.

  • Операционист переключается в броузер и вставляет в него поле данные из буфера обмена.

  • Пункты 4-5 операционист повторяет для полей «Имя», «Отчество», «Дата рождения» и «Номер паспорта».

  • Операционист нажимает на сайте кнопку «Отправить запрос».

  • Сайт какое-то время думает и затем отображает ИНН.

  • Операционист выделяет ИНН, копирует его в буфер обмена и переносит его обратно в Excel.

  • Операционист повторяет шаги выше для остальных записей о пользователе.

Процесс TO BE (как будет работать робот)

  1. Робот находит окно Excel и перемещает курсор в левый верхний угол (в начало клиентских данных). Конечно, робот мог бы прочесть Excel-файл напрямую, но для учебного примера мы предположим, что этой возможности нет, что робот имеет доступ только к пользовательскому интерфейсу CRM и вынужден имитировать действия оператора.

  2. Робот запускает браузер и открывает в нем ссылку https://service.nalog.ru/inn.do. Если появляется окно с приглашением принять условия обслуживания, то робот имитирует нажатие на эту галочку.

  3. Робот переключается в окно Excel и последовательно копирует все клиентские данные из ячеек Excel в буфер обмена, а из буфера обмена — в массив в памяти робота.

  4. Робот переключается в браузер и последовательно получает ИНН для каждой клиентской записи, для этого:

    1. Вставляет данные из памяти в форму на сайте https://service.nalog.ru/inn.do.

    2. Нажимает кнопку «Отправить запрос».

    3. Ждет пока сайт отобразит ИНН во всплывающей подсказке.

    4. Копирует ИНН из всплывающей подсказки в память программы рядом с данными о клиенте.

  5. Робот переключается в окно 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!

Читайте так же: