Управление браузером из внешнего приложения
Общие замечания Если описывать задачу, которую мы здесь будем решать, в двух словах, то состоит она во взаимодействии настольного приложение с браузером. Мне известно о существовании Selenium WebDriver, однако, насколько я знаю, возможности его ограничиваются имитацией действий пользователя, доступом к DOM, управлением вкладками, окнами и тому подобными вещами. Мне же хотелось бы получить как можно больше полномочий, чтобы выполнять из внешнего приложения задачи, доступные только расширениям браузера. Вот, собственно, исследованием данного вопроса мы здесь и будем заниматься. Сразу изложу идею. Поскольку изначально было заявлено, что действия, которые браузер должен будет выполнять под управлением внешней программы, то, соответственно помимо самой программы нам понадобится создать расширение для браузера. Далее нам нужно будет реализовать взаимодействие между расширением и приложением. На сегодняшний день эта задача решается предельно просто, а именно посредством веб-сокетов (WebSocket). То есть идея в том, чтобы в настольном приложении запустить сервер, принимающий подключения веб-сокетов, из расширения подключиться к этому серверу и далее через это подключение обмениваться командами и данными. Связь будет двусторонней, поэтому помимо задачи, обозначенной в заголовке, эту же модель взаимодействия можно использовать и для отправки данных приложению, и, если понадобится, то и управления приложением из расширения. Наша же основная задача состоит в том, чтобы отдать из приложения команду расширению и, если команда возвратит ответ, то получить этот ответ. Сервер Для создания сервера можно использовать два подхода. Во-первых, учитывая, что нам требуется минимальная функциональность, простейший сервер можно написать самостоятельно. Инструкция имеется здесь Writing a WebSocket server in C#. Во-вторых, существуют готовые решения, со всякими продвинутыми возможностями. Несмотря на то, что продвинутые возможности нам вряд ли понадобятся, лучше все-таки воспользоваться готовым решением, это сильно упростит процесс, ну и кроме того, некоторыми возможностями, может и есть смысл воспользоваться. Непродолжительный поиск привел меня к следующему решению SuperWebSocket. Среди пакетов NuGet есть несколько от этого автора, которые реализуют веб-сокет-сервер. Я попробовал парочку, оба работают, поэтому, честно говоря, не знаю, какому отдать предпочтение. На самом деле это не так важно, учитывая, что нам не очень-то много от сервера и надо. Пакеты немного устарели, автор создал уже новую версию, но она не распространяется через NuGet, и насколько я понял, ориентирована только на .Net Core. Документация в проекте отсутствует, но автор также является автором проекта SuperSocket, который хорошо задокументирован и он пишет, что документация актуальна и для проекта SuperWebSocket тоже. Разница там только в том, какой сервер будет создан, а интерфейсы у них общие. Таким образом за документацией мы идем сюда SuperSocket 1.6 Documentation, нас интересует именно эта версия, поскольку последняя, которая 2.0 актуальна для той версии, которой в NuGet нет и мы ее использовать не будем. Тестовое приложение Для начала, чтобы уже сразу проверить сервер в работе, мы создадим простое приложение, код которого возьмем из документации с небольшими изменениями и попробуем его сконнектить с простой веб-страничкой. Создаем консольное приложение. Добавляем пакеты NuGet SuperSocket.WebSocket и SuperSocket.Engine В результате помимо этих двух пакетов будет добавлен пакет SuperSocket. У меня все эти три пакета имеют версию 1.6.6.1 После установки SuperSocket.Engine в проекте появится папка Config с файлами log4net.config и log4net.unix.config. Эти файлы нужно выделить, перейти к свойствам и для свойства «Действие при сборке» выбрать «Содержание», а для свойства «Копировать в выходной каталог» выбрать значение «Копировать более позднюю версию»
Данный код запускает окно консоли и ждет нажатия любой клавиши, после которого запускает сервер. В примере из документации используется порт 2012, но у меня он не запускался на этом порте, видимо порт используется другой программой, поэтому я убрал из номера ноль и получился порт 212. Но это может быть любой свободный порт. При подключении к серверу, он отправляет сообщение с приглашением. Когда сервер получает текстовое сообщение он отправляет ответ о том, что получено сообщение такого-то содержания. Веб-страничка выглядит так
О расширении для браузера Это основная часть того, что мы будем делать. Расширение мы будем писать для Firefox, весь код я проверял только на Firefox Developer Edition. Выбор этого браузера обусловлен не только тем, что я именно им пользуюсь. Дело в том, что только у него я нашел возможность загрузить расширение из файла. Остальные браузеры позволяют делать это только в режиме разработки, что не очень удобно, хотя, если надо, то это тоже не проблема. Загружать подобное расширение в маркеты, смысла не вижу, поскольку оно будет «открывать все двери» и вряд ли пройдет проверку безопасности. Но даже если и загружать в маркеты, то, насколько я знаю, регистрация в них бесплатна только опять-таки у мозиллы, а платить за то, чтобы поставить на свой браузер свое же расширение – это, на мой взгляд, как-то странно. Собственно, поэтому файрфокс. Для создания расширения в Visual Studio я создал проект node.js, можно создать любой проект JavaScript. В нем создал папку FirefoxExtension. Для того, чтобы включить поддержку IntelliSense для расширений попробовал подключать разные пакеты, но что-то дело не пошло. В конце концов нашел вот это. Это годится в основном для Chrome, но, поскольку API расширений сейчас разные браузеры поддерживают одни и те же, за исключением некоторых нюансов, то это вполне подойдет, хотя нюансы эти надо учитывать. Опишу пару примеров отличий. Корневое пространство имен для доступа к API в браузере Chrome называется chrome, в то время как в браузерах Firefox и Edge оно называется browser. Насколько я помню, когда разбирался в этих вопросах впервые, я написал простейшее тестовое расширение для хрома, потом запустил его в файрфоксе и оно там заработало. Так что, по всей видимости в файрфоксе доступ к корневому объекту через chrome тоже поддерживается (сейчас проверять не буду, но насколько я помню – это так). Упомянутый проект chrome.intellisense поддерживает пространство имен chrome, поэтому для того, чтобы он поддерживал и browser, я просто в файле после объявления переменной chrome, объявил переменную browser и присвоил ей chrome. Таким образом эта проблема решилась. Другой важный момент: функции API расширений в основном асинхронные, но в хроме эта модель расширений поддерживается давно, а асинхронные функции появились в языке позже и таким образом асинхронность в них достигается за счет добавления в функции коллбэка, как дополнительного параметра. В файрфоксе же поддержку этой модели расширений реализовали относительно недавно и там функции возвращают реальные промайсы. Таким образом, при использовании chrome.intellisense данное обстоятельство просто придется иметь в виду. Можно использовать что-то другое, что-то типа webextensions-polyfill и т. п., но у меня в Visual Studio задействовать это не получилось, так что ничего по этому поводу сказать не могу. Реализация расширения Теперь о самом расширении. Нам нужно, чтобы пользователь имел возможность подключаться к заданному им же порту и отключаться от него, когда потребуется. Таким образом в папку расширения помимо обязательного файла manifest.json добавим файлы default_popup.html и default_popup.js. Это будет всплывающее окошко, которое будет появляться, когда пользователь клацнет по иконке расширения в браузере и скрипт для него. Кроме того, не будем забывать о том, что окошко popup существует только когда оно видимо, таким образом мы не можем разместить вебсокет в нем или его скрипте, поскольку при закрытии окошка будет закрываться и соединение, а нам нужно, чтобы оно работало постоянно. Таким образом нам понадобится еще фалй background.js. В манифесте помимо чисто описательных пунктов нам нужно будет прописать наше всплывающее окошко, бэкграунд-скрипт, а также набор разрешений. По поводу разрешений тут, по всей видимости следовало бы разрешить все, ну по крайней мере все, к чему мы хотим иметь доступ. А поскольку мы хотим его иметь ко всему то и получается, что разрешить надо все. Я взял для примера несколько разрешений, здесь не все, и пробовать из того, что есть, мы тоже будем не все, так что это просто пример и не более. Это пока неполный код. manifest.json
default_popup.html
Сообщение представляет из себя объект, в котором обязательно присутствует поле cmd, содержащее имя команды. На принимающей стороне объявляю переменную commands, представляющую из себя объект, у которого имена ключей совпадают с именами команд, передаваемых в сообщениях, а значениями этих ключей будут функции, принимающие и обрабатывающие сообщения. Итак, со стороны окошка popup нам потребуется отправлять сообщения о включении и выключении переключателя, а также запрашивать состояние подключения, последнее нам потребуется делать при открытии окошка, чтобы установить переключатель во включенное состояние, если сокет соединен с сервером. Принимать же он должен только сообщение о закрытии соединения, чтобы переключатель установить в выключенное состояние, если соединение по каким-то причинам разорвалось пока окошко было активно (например приложение закрылось или остановило сервер. default_popup.js
Теперь осталось только реализовать команды. Для команд сообщений внутри расширения все довольно просто
eval , которая будет принимать код, который, в свою очередь, будет передан функции eval, затем исполнен и результат возвращен приложению. Таким образом полный код бэкграунд-скрипта у нас будет следующим background.js
Если мы оставим все как есть, то при попытке выполнить команду eval мы получим сообщение системы безопасности, о том, что это запрещено. Чтобы разрешить выполнение этой функции в манифест нам нужно добавить ключ content_security_policy.
С расширением закончили, перейдем к приложению. Приложение Приложение для тестов я написал на языке VB.Net, но его код настолько прост, что написание его не составить труда на любом языке. Главное мы уже сделали. Создаем приложене WinForms, добавляем в него все те же пакеты NuGet, что и в консольном приложении, которое мы создали раенне. Так же поступаем с содержимым папки Config. После чего добавляем на форму Panel вверху, на которой мы разместим все для управления. А под ней SplitContainer с вертикальным расположением панелей. Панели контейнера зальем многострочными TextBox. Сверху tbMessageToSeng , снизу tbMessageReceived . В верхний будем вводить команду, в нижнем будут появляться ответы. На панели разместим NumericUpDowun nudPort для ввода имени порта, CheckBox с видом кнопки chbRunServer , для запуска сервера. ComboBox для выбора имени команды (у нас там будут имена eval и xscript) cbCommand . И Button btnSend для отправки сообщения.Код формы
Для отправки сообщения мы сначала создаем словарик с полями cmd и code, который потом будет сериализован в JSON и отправлен расширению. А вот дальше идет цикл, в котором перебираются сессии сервера и всем отправляется одно и то же сообщение. Тут дело в том, что сервер, который мы использовали, рассчитан на многопользовательские подключения. Каждое подключение создает новую сессию. Мы не использовали несколько подключений, поэтому можем оправлять одно сообщение всем сессиям в количестве одна штука, но если нужно использовать несколько подключений, то само собой, придется как-то организовать работу с сессиями. Запускаем – проверяем Далее в FireFox открываем страничку about:debugging#/runtime/this-firefox , жмем «Загрузить временное дополнение», и выбираем наш файл manifest.json. Запускаем приложение. Вводим номер порта, например тот же 212, и жмем кнопку (она же чекбокс) «Запустить». После этого в браузере, где после загрузки расширения появился его значек на панели справа вверху(поскольку мы не добавляли значков, то будет отображаться стандартный файрфоксовский). Там появится наше окошко popup, в котором надо будет ввести тот же номер порта, что и в приложении, и подключиться с помощью кнопки подключения. Теперь все готово и можно приступать к тестированию функционала.Для начала откроем в браузере какую-нибудь веб-страничку (страничка отладчика не подойдет, как и любая специальная вкладка). В приложении выберем в комбобоксе команду xscript и введем в верхнее текстовое поле
Можно таким же образом изменить содержимое, скажем, главного заголовка страницы (если он там есть).
Мы можем сделать то же самое и с помощью команды eval, но тогда придется для запроса title ввести в текстовое поле команду
eval мы также можем сделать что-нибудь не связанное со страницей. Например, если мы захотим добавить на панель закладок букмарклет, который запускает окошко alert с заголовком страницы, то можно выполнить следующий код
|
Всего комментариев 14
Комментарии
-
Запись от Avazart размещена 01.11.2020 в 20:08 -
Так я же вроде бы все написал подробно. Речь идет о полном наборе возможностей, которые имеют расширения браузера. На самом деле я не в курсе, возможно селениум тоже это может, поскольку ознакомился с его функционалом очень обзорно, но по-моему там все довольно скромно. То есть там можно взаимодействовать с открытыми документами и т. п., но функционал самого браузера вроде как недоступен. Например та же работа с закладками, я приводил пример, в котором удаленно создал букмарклет и разместил его на панели закладок. Селениум такое может?
Запись от diadiavova размещена 01.11.2020 в 22:43 -
Запись от Avazart размещена 01.11.2020 в 23:49
Обновил(-а) Avazart 01.11.2020 в 23:51 -
Так я вроде как об автоматизации нигде и не писал. Я с таким же успехом могу спросить для чего нужен доступ извне при автоматизации. Мне для этих целей юзерскриптов хватает, в крайнем случае можно расширение написать.
В то же время есть некоторые задачи для которых возможностей того же расширения не хватает. Точнее не хватает полномочий. Данный подход, например позволяет без труда реализовать удобных редактор тех же букмарклетов. Размещаем букмарклет на панели, извлекаем код, сохраняем в файл, открываем этот фай в любом редакторе и отслеживаем изменения файла, которыми тут же обновляем букмарклет. Все можно редактировать код букмарклета в любимом редакторе и тут же переходить к браузеру и проверять в работе.Запись от diadiavova размещена 02.11.2020 в 00:08 -
Запись от Avazart размещена 03.11.2020 в 15:40 -
Цитата:
Во-первых, есть смысл обратить внимание на букмарклеты. У них есть свои ограничения, ну там по размеру, безопасности, да и редакторов для них нет, но для простых задач они подходят лучше всего. Не требуют установки расширений, работают в основных браузерах практически одинаково и, например, будучи обычными закладками, синхронизируются между устройствами.
Во-вторых, можно использовать юзерскрипты. Это подразумевает установку одного расширения, но после этого можно легко написать скрипт под большинство задач автоматизации на страницах(там нет доступа к такому функционалу, как те же закладки, но со страницами можно делать много чего), если это освоить(а это на порядок проще написания собственного расширения), то в подавляющем большинстве случаев писать собственное расширение не понадобится.
В-третьих, собственно написание расширения, но тут есть ряд моментов.
Букмарклет - это обычная закладка, в которой адрес выглядит как-то такjavascript:<you javascript code here>
. Это довольно просто, подробности любой поисковик подскажет.Запись от diadiavova размещена 03.11.2020 в 16:52
Обновил(-а) diadiavova 03.11.2020 в 16:54 -
Юзерскрипты были порождены старым расширением для файрфокса под названием GreaseMonkey. На данный момент оно уже не работает, но формат скриптов, разработанный для него, фактически стал стандартом для скриптов такого рода и на сегодняшний день есть несколько расширений, поддерживающих этот формат. Наиболее часто используется TamperMonkey, он есть для разных браузеров, но у меня как-то с ним не сложились отношения, он как-то попросил задонатить, я отказался и он перестал работать )). Мог бы нажать кнопочку, что, дескать, задоначу позже, он бы ждал, но периодически напоминал бы. Я вместо этого нашел Violentmonkey – Загрузите это расширение для Firefox (ru), поддерживает тот же формат скриптов, существует и для других браузеров Get Violentmonkey - Violentmonkey, проблем у меня с ним не было. Документация здесь Metadata Block - Violentmonkey.
Запись от diadiavova размещена 03.11.2020 в 16:54 -
Ну и по поводу расширений. Насчет книг я подсказать ничего не могу. Однако документация по расширениям браузеров существует и она довольно подробная. На сегодняшний день основные браузеры поддерживают модель расширений, изначально созданную для хрома, однако везде есть свои нюансы, поэтому, хоть документация для одного браузера и может использоваться для написания расширений для другого, эти нюансы, все-таки, должны учитываться. Так что лучше курить родную документацию для каждого браузера.
По хрому можно начать отсюда
Getting Started Tutorial - Google Chrome
По файрфоксу - отсюда Расширения браузера - Mozilla | MDN
По edge отсюда Расширения Microsoft EDGE (Chromium) - Microsoft Edge Development | Microsoft DocsЗапись от diadiavova размещена 03.11.2020 в 16:55 -
С оперой и сафари не работал, но думаю, найти тоже будет несложно.
Кроме того в старых версия файрфокса поддерживались оверлей-расширения(они же xul-расширения), на сегодняшний день поддержка таких расширений сохранилась в форке файрфокса под названием PaleMoon. Документацию по ним еще пока можно найти на mdn, но как долго она там еще будет оставаться - неизвестно. Эти расширения хороши тем, что имеют полномочия практически те же, что и настольное приложение. Если это требуется, то есть смысл заглянуть и туда. Overlay extensions - Mozilla | MDN
Но еще раз повторю, что в файрфоксе, начиная с 56-ой версии это уже не работает.Запись от diadiavova размещена 03.11.2020 в 16:56 -
Запись от Avazart размещена 03.11.2020 в 17:01
Обновил(-а) Avazart 03.11.2020 в 17:03 -
Я же все написал развернуто. Просто комментарии имеют ограничения по размеру, поэтому пришлось разбить, но есть там ссылки и на документацию по расширениям.
Запись от diadiavova размещена 03.11.2020 в 17:22 -
Запись от diadiavova размещена 03.11.2020 в 17:25 -
Запись от Avazart размещена 03.11.2020 в 18:00 -
Цитата:
Кроме того, никто не мешает отправлять их на сервер. Я понимаю, что еще сервер надо запускать, но при работе с селениумом ведь тоже внешнее приложение нужно.
Документация содержит не только справочник по апи, там очень много всего, включая статьи a la "начало работы" или "первое расширение".Запись от diadiavova размещена 03.11.2020 в 18:38