Selenium — стандартные решения
Selenium — расширение для браузера Mozilla Firefox, позволяющее записывать действия пользователя в браузере (переходы по ссылкам, нажатие кнопок), а затем воспроизводить их. Удобно использовать для тестирования и для автоматизации рутинных действий.
Мне Selenium понадобился для автоматизации огромного количества рутинных действий: создание некоторого числа объектов (создание карточки с информацией, приложение к карточке дополнительных данных, картинок, расстановка точек на карте).
Начинала работу с Selenium с минимальными навыками программирования, поэтому реализация некоторых задач требовала много гугла:).
Selenium или автоматизация работы браузера
Предположим, что вам нужно выгрузить большое количество документов, генерируемых автоматически или подгружаемых в систему человеком. Но ручное выполнение этой задачи займет огромное количество времени. На помощь приходит Selenium WebDriver! Подробнее в сегодняшней статье
Selenium WebDriver – библиотека языка Python для управления работой браузера. По сути, такой веб-драйвер создает бота для автоматизации работы в браузере.
Одно из неоспоримых преимуществ Selenium WebDriver – это возможность выполнения действий на странице с «ожиданием». Веб-драйвер позволяет «ожидать», как бы ожидал человек, пока не появится необходимый элемент.
Но есть и минусы:
- Работа возможно только с первой вкладкой браузера;
- Иногда поведение в разных браузерах разнится;
- Драйвер может «упасть» прямо посреди работы;
- Бывают сложности с поиском элементов (XPath не всегда работает, но помогает замена поиска по ID);
- Необходимо четко продумать алгоритм действий.
Связка Python-Selenium имеет удобный API для доступа ко многим браузерам.
Допустим, что библиотека уже установлена (иначе нужно запустить в CMD команду «pip install selenium»). Для работы будем использовать программный интерфейс Anaconda 3, а именно Jupiter Notebook.
Практика применения
В рамках работы по проекту нам нужно было получить для обработки приблизительно 10 тыс. документов формата. docx. Выгрузка одного объекта занимала от 30 до 40 секунд или 5-6 действий.
Автоматизация выгрузки была выполнена с помощью следующих действий:
1. Получение объектов с веб-страниц
Для поиска объектов используются локаторы (строки, которые идентифицируют элемент страницы).
- by_id – осуществляется поиск по атрибуту id;
- by_xpath – поиск элемента по XPath выражению;
- by_cssSelector – поиск, основанный на описаниях таблиц стилей (CSS)
После поиска объектов, с ними можно выполнять различные действия. Например с помощью метода click() можно нажать на объект веб-страницы.
2. Работа с выпадающими списками
Метод используется для работы с выпадающими списками
Локатор by_id ищет элемент, необходимый для выбора. Далее выбирает строку со значением `EQUAL`.
3. Имитация работы клавиатуры
Ниже представлен пример имитации нажатия клавиши на клавиатуре. Создаем экземпляр класса ActionChains, в который передаем browser. Далее вызываем метод SendKeys, в котором указываем действие, в данном случае – нажатие на Enter.
JavaScript, Node, Puppeteer: автоматизация Chrome и веб-скрапинг
Библиотека puppeteer для Node.js позволяет автоматизировать работу с браузером Google Chrome. В частности, с помощью puppeteer можно создавать программы для автоматического сбора данных с веб-сайтов, так называемые веб-скраперы, имитирующие действия обычного пользователя. В подобных сценариях может применяться браузер без пользовательского интерфейса, так называемый «Headless Chrome». Используя puppeteer , можно управлять и браузером, который запущен в обычном режиме, что особенно полезно при отладке программ.

Предварительная подготовка
Перед началом работы вам понадобится Node 8+. Найти и загрузить его можно здесь, выбрав текущую (Current) версию. Если вы никогда раньше не работали с Node, взгляните на эти учебные курсы или поищите другие материалы, благо, их в Сети предостаточно.
После установки Node создайте папку для проекта и установите puppeteer . Вместе с ним будет установлена и актуальная версия Chromium, который гарантированно будет работать с интересующим нас API. Сделать это можно с помощью следующей команды:
Пример №1: создание копий экрана
После установки puppeteer разберём простой пример. Он, с небольшими изменениями, повторяет документацию к библиотеке. Код, который мы сейчас рассмотрим, делает скриншот заданной веб-страницы.
Для начала создадим файл test.js и поместим в него следующее:
Построчно разберём этот код. Сначала покажем общую картину.
В этой строке мы подключаем ранее установленную библиотеку puppeteer в качестве зависимости.
Тут представлена главная функция, getPic() . Эта функция содержит код, автоматизирующий работу с браузером.
В этой строке мы вызываем функцию getPic() , то есть, выполняем её.
Важно обратить внимание на то, что функция getPic() является асинхронной, она определена с ключевым словом async . В ней используется конструкция async / await из ES 2017. Так как getPic() — функция асинхронная, она, при вызове, возвращает объект Promise . Такие объекты обычно называют «промисами». Когда функция, определённая с ключевым словом async , завершает работу и возвращает некое значение, промис либо будет разрешён (в случае успешного завершения операции), либо отклонён (если произойдёт ошибка).
Благодаря использованию при определении функции ключевого слова async , мы можем выполнять в ней вызовы других функций с ключевым словом await . Оно приостанавливает выполнение функции и позволяет дождаться разрешения соответствующего промиса, после чего работа функции продолжится. Если это всё вам пока не понятно — просто читайте дальше и постепенно всё начнёт становиться на свои места.
Теперь разберём код функции getPic() .
Тут мы запускаем puppeteer . Фактически это означает, что мы запускаем экземпляр браузера Chrome и записываем ссылку на него в только что созданную константу browser . Так как в этой строке использовано ключевое слово await , выполнение основной функции будет приостановлено до разрешения соответствующего промиса. В данном случае это означает ожидание либо успешного запуска экземпляра Chrome, либо возникновения ошибки.
Здесь мы создаём в браузере, управляемом посредством программного кода, новую страницу. А именно, запрашиваем эту операцию, ожидаем её завершения и записываем ссылку на страницу в константу page .
Используя переменную page , созданную в предыдущей строке, мы можем дать странице команду по переходу на указанный URL. В данном примере мы переходим на https://google.com . Выполнение кода, как и в предыдущих строках, приостановится до завершения операции.
Здесь мы запрашиваем у puppeteer создание скриншота текущей страницы, представленной константой page . Метод screenshot() принимает, в виде параметра, объект. Тут можно указать путь, по которому нужно сохранить скриншот в формате .png . Опять же, здесь используется ключевое слово await , что приводит к приостановке выполнения функции до завершения операции.
Функция getPic() завершает работу и мы закрываем браузер.
Запуск примера
Вышеописанный код, сохранённый в файле test.js , можно запустить с помощью Node следующим образом:
Вот что получится после того, как он успешно отработает:

Замечательно! А теперь, чтобы было веселей (и чтобы облегчить отладку), мы можем выполнить те же действия, запустив Chrome в обычном режиме.
Что бы это значило? Попробуйте и увидите сами. Для этого нужно заменить эту строку кода:
Сохраним файл и снова его запустим с помощью Node:
Здорово, правда? Передавая объект
Прежде чем идти дальше, сделаем ещё кое-что. Вы заметили, что скриншот, который делает программа, включает в себя лишь часть страницы? Так происходит из-за того, что окно браузера немного меньше размера веб-страницы. Исправить это можно с помощью следующей строчки, меняющей размер окна:
Её надо добавить в код сразу после команды перехода по URL. Это приведёт к тому, что программа сделает скриншот, который выглядит гораздо лучше:

Вот как будет выглядеть итоговый вариант кода:
Пример №2: веб-скрапинг
Теперь, когда вы освоили основы автоматизации Chrome с помощью puppeteer , разберём более сложный пример, в котором займёмся сбором данных с веб-страниц.
Сначала стоит взглянуть на документацию к puppeteer . Можно обратить внимание на то, что тут имеется огромное количество различных методов, которые позволяют нам не только имитировать щелчки мышью по элементам страниц, но и заполнять формы, и читать со страниц данные.
Мы будем собирать данные с сайта Books To Scrape. Это — имитация электронного книжного магазина, созданная для экспериментов по веб-скрапингу.
В той же директории, где лежит файл test.js , создайте файл scrape.js и вставьте туда следующую заготовку:
В идеале, после разбора первого примера, вы уже должны понять то, как устроен этот код. Но если это не так — ничего страшного.
В этом фрагменте мы подключаем ранее установленный puppeteer . Далее, у нас имеется функция scrape() , в которую, ниже, мы добавим код для скрапинга. Эта функция возвратит некое значение. И, наконец, мы вызываем функцию scrape() и работаем с тем, что она возвратила. В данном случае — просто выводим это в консоль.
Проверим этот код, добавив в функцию scrape() возврат строки:
После этого запустим программу командой node scrape.js . В консоли должно появиться слово test . Работоспособность кода мы подтвердили, нужное значение попадает в консоль. Теперь можно заняться веб-скрапингом.
▍Шаг 1: настройка
Сначала надо создать экземпляр браузера, открыть новую страницу и перейти по URL. Вот как мы всё это сделаем:
Разберём этот код.
В этой строке мы создаём экземпляр браузера и устанавливаем параметр headless в false . Это позволяет нам наблюдать за тем, что происходит.
Здесь создаём новую страницу в браузере.
Переходим по адресу http://books.toscrape.com/ .
Тут добавляем задержку в 1000 миллисекунд для того, чтобы дать браузеру время на полную загрузку страницы, но обычно этот шаг можно опустить.
Здесь закрываем браузер и возвращаем результат.
Предварительная подготовка завершена, теперь займёмся скрапингом.
▍Шаг 2: скрапинг
Как вы уже, наверное, поняли, на сайте Books To Scrape имеется большой каталог настоящих книг, снабжённых условными данными. Мы собираемся взять первую книгу, расположенную на странице, и вернуть её название и цену. Вот домашняя страница сайта. Щёлкнем по первой книге (она выделена красной рамкой).

В документации по puppeteer можно найти метод, который позволяет имитировать щелчки мышью по странице:
Конструкция вида selector <string> представляет собой селектор для поиска элемента, по которому нужно щёлкнуть. Если обнаружено несколько элементов, удовлетворяющих селектору, то щелчок будет сделан по первому из них.
Очень хорошо то, что инструменты разработчика Google Chrome позволяют, без особых сложностей, определить селектор конкретного элемента. Для того, чтобы это сделать, достаточно щёлкнуть правой кнопкой мыши по изображению и выбрать команду Inspect (Просмотреть код).

Эта команда откроет панель Elements (Элементы), в которой будет представлен код страницы, фрагмент которого, соответствующий интересующему нас элементу, будет выделен. После этого можно щёлкнуть по кнопке с тремя точками слева и в появившемся меню выбрать команду Copy → Copy selector (Копировать → Копировать селектор).

Отлично! Теперь у нас имеется селектор и всё готово для того, чтобы сформировать метод click и вставить его в программу. Вот как это будет выглядеть:
Теперь программа имитирует щелчок по первому изображению товара, что приводит к открытию страницы этого товара.
На этой новой странице нас интересует название книги и её цена. Они выделены на нижеприведённом рисунке.

Для того, чтобы добраться до этих значений, мы будем пользоваться методом page.evaluate() . Этот метод позволяет использовать методы JavaScript для работы с DOM, наподобие querySelector() .
Для начала вызовем метод page.evaluate() и присвоим возвращённое им значение константе result :
В этой функции мы можем выбирать необходимые элементы. Для того, чтобы понять, как описать то, что нам нужно, снова воспользуемся инструментами разработчика Chrome. Для этого щёлкнем правой кнопкой по названию книги и выберем команду Inspect (Просмотреть код).

В панели Elements (Элементы) можно увидеть, что название книги — это обычный заголовок первого уровня, h1 . Выбрать этот элемент можно с помощью следующего кода:
Так как нам нужен текст, содержащийся в этом элементе, нам понадобится воспользоваться свойством .innerText . В итоге приходим к следующей конструкции:
Такой же подход поможет нам выяснить то, как взять со страницы цену книги.

Можно заметить, что строчке с ценой соответствует класс price_color . Мы можем использовать этот класс для того, чтобы выбрать элемент и прочитать содержащийся в нём текст:
Теперь, когда мы вытащили со страницы название книги и её цену, мы можем возвратить всё это из функции в виде объекта:
В результате получается следующий код:
Здесь мы считываем со страницы название книги и цену, сохраняем их в объекте и возвращаем этот объект, что приводит к записи его в result .
Теперь осталось лишь вернуть константу result и вывести её содержимое в консоль.
Полный код этого примера будет выглядеть так:
Теперь можно запустить программу с помощью Node:
Если всё сделано правильно, в консоль будет выведено название книги и её цена:
Собственно говоря, всё это и есть веб-скрапинг и вы только что сделали первые шаги в этом занятии.
Пример №3: улучшаем программу
Тут у вас могут появиться вполне резонные вопросы: «Зачем щёлкать по ссылке, ведущей к странице книги, если и её название, и цена, отображаются на домашней странице? Почему бы не взять их прямо оттуда? И, если мы смогли это сделать, почему бы не прочитать названия и цены всех книг?».
Ответ на эти вопросы заключается в том, что существует множество подходов к веб-скрапингу! К тому же, если ограничиться данными, выводимыми на домашней странице, можно столкнуться с тем, что названия книг будут укорочены. Однако, все эти размышления дают вам отличную возможность попрактиковаться.
▍Задача
Ваша цель — считать все заголовки книг и их цены с домашней страницы и вернуть их в виде массива объектов. Вот какой массив получился у меня:

Можете приступать. Не читайте пока дальше, попробуйте сделать всё сами. Надо сказать, что эта задача очень похожа на ту, которую мы только что решили.
Получилось? Если нет — тогда вот подсказка.
▍Подсказка
Главное отличие этой задачи от предыдущего примера заключается в том, что тут нам надо пройтись по списку данных. Вот как это можно сделать:
Если и сейчас вам не удаётся решить задачу, в этом нет ничего страшного. Это — вопрос практики. Вот один из возможных вариантов её решения.
▍Решение задачи
Итоги
Из этого материала вы узнали о том как пользоваться браузером Google Chrome и библиотекой Puppeteer для создания системы веб-скрапинга. А именно, мы рассмотрели структуру кода, способы программного управления браузером, методику создания копий экрана, методы имитации работы пользователя со страницей и подходы к чтению и сохранению данных, размещаемых на веб-страницах. Если это было ваше первое знакомство с веб-скрапингом, надеемся, теперь у вас есть всё необходимое для того, чтобы вытащить из интернета всё, что вам нужно.
Уважаемые читатели! Пользуетесь ли вы библиотекой Puppeteer и браузером Google Chrome без пользовательского интерфейса?
Автоматизация однотипных действий, ч.2
Всем привет, с одной стороны я знал, что желание избавиться от однотипного труда довольно популярно, но всё же не ожидал такого отклика. Спасибо за ваши отзывы.
А теперь продолжим изучать создание сценариев действий в Clickermann.

Для того, чтобы вы могли начать простую автоматизацию уже сегодня, изучим возможность записи своих действий и их использования, а также общие нюансы создания скрипта.
Прежде всего изучим горячие клавиши запуска и остановки программы. Возможно те из вас, кто уже попробовал запустить свой скрипт столкнулись с проблемой его остановки, ведь он перехватывает управление мышкой и дотянутся до кнопки «стоп» становится непросто.
Для этого есть следующие комбинации:
Alt + A – Запуск сценария;
Alt + A – Пауза сценария (после его запуска);
Alt + S – Полная остановка;
Alt + R – Начать запись сценария;
Перед созданием сценария, вам необходимо подумать, при каком окне и размерах окна, будет стартовать ваша программа. Например если выработаете в сочетании окон Word, Chrome, Excel и запишите свой сценарий при таком расположении значков:

То при таком, она уже работать не будет(если не внедрять проверку с анализом экрана, но об этом позже):

Тоже самое касается размера окна, поэтому я советую просто открыть все окна в полноэкранный размер и записывать скрипты исходя из этого.
После того как вы определитесь с этим, следует также учесть эти стартовые порядки и размеры при каждом следующем запуске скрипта, например сначала вы открываете все файлы и только потом запускаете скрипт.
Немаловажный момент касается цикличности исполнения. Например если ваш скрипт подразумевает повторы, условно:
1) Открыть браузер
2) Нажать на закладку с адресом сайта
3) Скопировать информацию
4) Занести в таблицу эксель
5) Перейти к п.1
То при следующем открытии браузера уже будет открыта загруженная страница сайта и возможно не будет той закладки по которой вы переходите, соответственно все дальнейшие действия будут неверными, поэтому нам нужно изменить свой скрипт так, чтобы он возвращал все положения окон в их изначальное состояние на старте, например так:
1) Открыть браузер
2) Нажать на закладку с адресом сайта
3) Скопировать информацию
3.5) Закрыть браузер
4) Занести в таблицу эксель

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

Тогда у вас будет достаточно времени, чтобы подойти и отключить скрипт.
Теперь о записи действий, у неё есть неоспоримый плюс — скорость и простота, достаточно включить запись действий (Alt + R) и один раз в ручную совершить эти действия, после чего остановить запись. Тут следует учесть, что запись идёт непрерывно по всем движениям мышки, я записал 10-ти секундные движения, у меня получилось следующее:

Как вы понимаете отредактировать такое будет невозможно, но если вам не нужны просто движения мышкой без кликов и действий, то открываем настройки и в разделе «Интерпретатор» убираем галочку «Записывать движения мыши» и жмём «применить».

Теперь наш скрипт получился намного короче, но разобраться в нём всё ещё довольно сложно:

Также запись не позволяет вам использовать такие функции как анализ экрана, анализ текста скопированного в буфер, какие либо вариации в сценарии в зависимости от происходящего.
Поэтому данный способ идеально подходит для коротких, не вариативных действий. Он позволяет без помощи редактора и соответственно без знания команд производить простые манипуляции.
Далее мы начнём углубляться в настоящее программирование своих сценариев.

Представим, что нам нужно совершить выполнение какой либо последовательности действий несколько раз. Например обработать все входящие письма пришедшие нам на почту. При этом эта последовательность не является всей нашей программой, например наш скрипт проверяет почту, но основная его цель — копировать данные из одной таблички excel в другую. Как бы вы написали этот код?
Вероятно так (напишем псевдокодом):

Работает? Да, но выглядит не очень, трудно читать и понимать.
Чтобы внести порядок в наш скрипт, мы будет использовать переменные и циклы.
Переменная — своего рода корзина, куда мы помещаем значение. Это может быть слово, буква, но чаще всего число. С этим числом можно производить арифметические действия, сравнивать его с чем либо и на основе этого, поведение нашего скрипта будет изменяться.
Цикл — выделение участка кода в отдельный блок, с целью его многократного использования.
Допустим, мы точно знаем, что нам каждый день приходит 5 писем, тогда мы можем сделать так:

ВАЖНО: общепринято выделять внутренности цикла отступом для пущей красоты и удобства понимания. для этого выделяем участок кода внутри цикла и нажимаем TAB или пробел. Вуаля, вы восхитительны.
У нас появляются новые команды:
DEFINE — объявление \ создание переменной, в скобочках указываем её название и через запятую значение, название всегда начинается со значка доллара, то есть правильно называть переменные $counter, $x, $letter, неправильно — counter, _z, $переменная.
WHILE — команда открывающая цикл, в скобочках пишем логическое выражение, например сравниваем переменную с другим числом. Сравнить можно так:
>= «Больше или равно»
<= «Меньше или равно»
пока условие будет выполняться, блок кода внутри цикла будет исполняться, на нашем примере он исполнится 5 раз. Давайте отследим логическую цепочку, в первый раз переменная VAR заходит в проверку цикла со значением 0.
0 меньше 5? Да! — выполняем код
Затем мы встречаем команду:
INC — увеличить значение переменной, в скобочках указываем какую переменную и на сколько увеличивать ($var, 1). Поэтому наша переменная увеличивается на 1 и становится один. Без увеличения переменной мы получим бесконечный цикл и бесконечное повторение действий внутри цикла.
после чего встречаем новую команду:
END_CYC — противоположность команде while — закрывает цикл. таким образом каждый раз при соответствии условий будет исполняться всё что между этими двумя командами.
Вернёмся к нашей переменной var, она увеличилась на 1 и снова отправляется на проверку соответствия условию.
1 меньше 5? Да! — выполняем код. Увеличиваем переменную до 2.
2 меньше 5? Да! — выполняем код. Увеличиваем переменную до 3.
3 меньше 5? Да! — выполняем код. Увеличиваем переменную до 4.
4 меньше 5? Да! — выполняем код. Увеличиваем переменную до 5.
5 меньше 5? Нет! — теперь цикл больше не будет исполняться, действие программы пойдет к первой команде после цикла (ниже чем END_CYC ).
Таким образом наш код стал меньше, красивее и легче в редактировании, ваш босс увидел как быстро вы принимаете задания и стал давать вам 10 заданий, соответственно приходит 10 писем, в прошлом коде вам бы пришлось копировать и вставлять, считать сколько раз вы уже вставили, не лишнего ли теперь? А сейчас достаточно поменять условие на WHILE($var < 10) и вот, всё будет точно работать 10 раз.

Это вы, сравниваете количество ваших новых задач на столе по высоте пальца
Хорошо, а что на счёт вариативности кода? Если дальнейшие действия зависят от воли случая?
Для этого у нас есть операторы IF и SWITCH
IF — оператор сравнения, такое мы уже с вами видели в скобочках цикла WHILE. Оператор IF проверяет соответствие условию и если оно верно, выполняет действия в своем блоке кода.

Мы создаем переменную, кладем в неё значение 2. Оператор IF проверяет, равна ли var двойке, если да воспроизведется звуковой файл, если нет, пропустит эту строку. Конец блока кода оператора IF определяется оператором END_IF
Хорошо, мы разобрались с одним вариантом, но как быть если у нас два варианта, разных действий. Для этого применяется конструкция IF — ELSE

Теперь оператор IF проверяет на соответствие переменной двойке, если она равна 2, воспроизведется файл end.wav, при любом другом значении переменной var будет воспроизведен файл start.wav
Бывают случаи, когда нам мало двух условий, например вы публикуете фотографии в 10 социальных сетях, под каждую социальную сеть, вы подготовили разные фотографии и положили их на 10-ти разных листах в документе excel, как сделать так, чтобы в нужную социальную сеть, попадало нужное фото?
SWITCH — оператор множественного выбора. В данном примере вы проверяем переменную post в зависимости от её значения от 1 до 10 открывается соответствующая страница документа. Данный оператор содержит в себе операторы CASE и END_SWITCH. Оператор Case() содержит в скобках значение переменной, при которой именно его действия будут выполнятся,своего рода это дверь, которая открывается тогда, когда переменная(ключ) подходит к его условию(замку) ну и END_SWITCH заканчивает блок кода выбора действий.

Также можно заметить оператор DEFAULT который срабатывает, если переменная $post содержит в себе значение не подходящее ни под один CASE.
Таким образом, применяя IF и SWITCH вы можете создать по настоящему вариативную программу и уже начинать называть себя программистом =)
В следующем посте, расскажу о подпрограммах и удобстве которое они дают, как производить анализ экрана и на основе этого выбирать действия. А пока небольшое домашнее задание, создайте скрипт который в готовый файл excel пишет числа по порядку и скрывает строку.
Работать это должно примерно так:
Для этого нам потребуется функция:
KEYSTRING — имитирует ввод с клавиатуры переданного ему значения. Можно вводить числа, например KEYSTRING(«2») или строки KEYSTRING(«Hello World») или переменные KEYSTRING($var) обратите внимание, в случае переменной кавычки внутри скобок не нужны. Ваши решения можете писать тут же в комментариях и подсмотреть решения других, если что-то не получается, но настоятельно рекомендую не делать этого сразу, а попытаться решить самостоятельно.

367 постов 4.8K подписчика
Правила сообщества
-Ставьте наши теги, если Ваш пост о программе, приложении или браузере(в том числе о расширениях, дополнениях в нему), его недоработке, баге, обновлении. Это может быть пост — обзор или отзыв.
-При возникновении споров относитесь с уважением друг к другу, а так же приводите аргументы.
Разрешено всё, что не запрещено правилами Пикабу.
Не нужно никаких задержек добавлять, это поведение настраивается. Достаточно нажать кнопку режим выполнения и выбрать фиксированное количество повторов равное 1.


Вообще интересно, но намного интереснее на конкретных примерах.
Скажем, «вот у меня есть данные в эксель, их надо вставить в форму на определенной странице браузера. Также надо вставить дату, её надо взять из той же строки экселя и вот таким образом вставить в форму страницы браузера. Ну а дальше просто — клики на странице, вот скрип, вот так это выглядит». ИМХО.
дайте мне тоже работу, которую можно вот так автоматизировать.
MajorDom v1.0 — От голосового помощника к умному дому
В 2019 году я впервые узнал про возможность распознавания и синтеза речи на языке python. Гугл ассистент, сири, кортана и другие ассистенты тогда были еще более ограниченными и беспомощными, чем сейчас. О добавлении своих команд речи не шло от слова совсем. Тогда я и загорелся идеей создать своего голосового помощника, который не будет уступать даже Джарвису Тони Старка.
В процессе работы над ядром, начал задумываться, где этого ассистента хостить. Держать ноут постоянно включенным не вариант, а других компьютеров у меня не было. На помощь пришли одноплатные компьютеры raspberry pi. Я хотел, чтобы мой голосовой ассистент мог включать и выключать свет, управлять светодиодной лентой и шторами. С такими задачами отлично справляется ардуино. Оставалось только найти способ передавать команды с распбери. Использовать wifi и bluetooth не хотел с самого начала. Нашел в интернете информацию про модули nrf24l01, попробовал, понравилось.
Такая система работала довольно неплохо. Но было два ключевых недостатка:
Радиус действия ограничивался чувствительностью микрофона. С хорошим микрофоном все работало идеально в пределах комнаты, но не дальше.
Для каждого параметра каждого устройства надо было добавлять одинаковые голосовые команды, в которых отличались только адрес и сообщение. Неудобно, но пока терпимо.
Для решения первой задачи, в голосового ассистента я добавил http интерфейс на джанго, который мог принимать аудиофайл или строку. В комбинации с мобильным приложением на котлине, я получил беспроводной микрофон, таким образов расширив зону работы до радиуса действия роутера, то есть с комнаты до всей квартиры и даже чуть больше. Носить телефон по дому не всегда было удобно, так что через пару дней появилось приложение и на часах на wear os, что оказалось невероятно удобным решением.
Но я захотел большего: иметь доступ к своему помощнику всегда, а не только дома. Самым простым вариантом оказалось использование телеграм-бота как интерфейс ввода-вывода. Но меня не покидало ощущение, что бот — это что-то не то. Я решил оставить его только как временное решение, пока занят разработкой чего-то лучше.
Я хотел получить возможность использовать свое мобильное приложение для доступа к ассистенту на расстоянии. Надо было всего лишь придумать способ отправить запрос на локальный джанго сервер, не находясь при этом в локальной сети. Я был готов открывать и пробрасывать порты на роутере, но провайдер не дал мне белый ip. Тогда я попробовал ngrok. В первое время работало хорошо, но в бесплатной версии сервер периодически падал и менял адрес. Вариант с впн-туннелем я отбросил почти сразу. Стоимость vps была равна стоимости подписки на ngrok, но реализация была в разы сложнее.
Тогда я вспомнил, что у меня есть бесплатный хостинг для php сайтов на beget и переизобрел Long Polling и очереди. Реализация была максимально простой: приложение отправляло запрос на хостинг. Там php код добавлял тело (json) запроса в конец массива и записывал в локальный файл. Малина дома каждую секунду отправляла запрос на чтение этого файла, после чтения массив чистился. Таким образом мне удалось отправлять команды домой из любой точки планеты страны! Аналогичным образом я сделал получение ответа от ассистента: продублировал реализацию и поменял роли. Два файла и четыре эндпоинта на бесплатном хостинге на пыхе дали мне стабильную двустороннюю связь с моим домашним помощником. Чуть позже научил ассистента самостоятельно отправлять мне сообщения, например, с номером аудитории следующей пары в начале каждой перемены. Не успел всем похвастаться в колледже, как кто-то стал спамить мне домой. Пришлось добавить авторизацию: логин и пароль задавались хардкодом в приложении, а на сервере была проверка в стиле.
if ($login == ‘markparker’ && $password == ‘MyVeryStrongP@ssw0rd!’) <>;
Репозитории приложения были приватные, а сервер был вообще без репы (зачем репа на один файл до 100 строк?), так что такого уровня безопасности мне более чем хватало.
Чуть позже в системе появился первый автоматический триггер команды. Через небольшой костыль в моем приложении я смог ловить событие, когда на телефоне срабатывает будильник. Этот триггер запускал первый полноценный сценарий: одновременно открывались шторы, ассистент озвучивал время, погоду и расписание пар в колледже. Если в комнате все еще было темно, плавно включалась лампа. В этот момент я чувствовал себя настоящим Тони Старком.
Тогда я захотел добавить больше автоматических сценариев, используя датчики движения, присутствия, освещенности и так далее. В этот момент стал сильнее ощущаться второй недостаток, о котором я писал ранее. Появилось много дублирования кода, работать с которым становилось уже не так удобно. В проекте была только сущность команды, не было понятия устройств и триггеров. И тогда до меня дошло, как сильно вырос мой голосовой ассистент: я уже делал полноценный умный дом, а не вопросно-ответного помощника.
Это осознание привело меня к решению отделить голосового ассистента и сделать умный дом самостоятельным проектом, ориентируясь уже на управление устройствами, а не на голосовые команды. И я решил делать это сразу по-взрослому, с полноценным сервером, базами данных, авторизацией и мобильным приложением. Чуть позже преподаватель в колледже подсказал, что вместо моего костыля с записью массива в файл на php, можно использовать вебсокеты. Именно так я и реализую позже управление устройствами через интернет. Спасибо, Александр Анатолиевич!
В остальном общая концепция не изменилась: хаб в виде одноплатного компьютера управляет ардуинами через радиомодуль nrf24l01. Подробнее про архитектуру я расскажу в следующей статье.