Управление браузером через php

Обновлено: 07.07.2024

От автора: статья дает обзор концепций, технологий и техник программирования, связанных с автоматическим запуском тестов под управлением WebDriverJS на Windows 10 и Microsoft Edge. Ручное прокликивание разных браузеров, пока они запускают ваш код, локально или удаленно – быстрый способ проверить код. Так можно визуально проверить, что все работает ровно так, как вы предполагали с точки зрения макета и функциональности. Тем не менее, это не решение для тестирования полного кода сайта во всех браузерах и на всех типах устройств, доступных клиентов. Здесь нам поможет автоматизированное тестирование WebDriver API.

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

WebDriverJS API Link

WebDriver API – стандарт, который абстрагирует специфические привязки устройств/браузеров от разработчика, чтобы написанные вами тесты (на выбранном вами языке) можно было один раз написать и запускать в нескольких разных браузерах через WebDriver. В некоторые браузеры уже встроены возможности WebDriver, для других необходимо загружать исполняемый файл для пары браузер/ОС.

Автоматизированное браузерное тестирование с помощью WebDriver API

Управление браузером через WebDriver API

В спецификации WebDriver на W3C задокументированы API, доступные разработчикам для программного управления браузером. На рисунке ниже показан пример страницы с основными коллекциями WebDriver и API, с помощью которых можно получать/устанавливать свойства браузера.


JavaScript. Быстрый старт

Изучите основы JavaScript на практическом примере по созданию веб-приложения

Автоматизированное браузерное тестирование с помощью WebDriver API

Написание тестов

У вас есть выбор из поддерживаемых языков в WebDriver. Языки, поддерживаемые главным проектом Selenium/WebDriverJS:

JavaScript (via Node)

Тесты могут быть разные: от проверки макета страницы, возвращаемых с сервера значений, ожидаемого поведения при взаимодействии до проверки рабочего потока (например, проверка корректности работы корзины).

Позже мы сможем продемонстрировать запуск тестов для React примера и примеров Backbone.js и Vue.js простой сменой URL.

Для демонстрации мы напишем тесты на JS, запускаемые на узле, которые:

Добавят элементы to-do и проверят, что введенный элементы создались.

Изменят элементы с помощью двойного клика, нажатия клавиши backspace и ввода дополнительного текста.

Удалят элемент через API мыши.

Пометят элемент списка, как выполненный.

Настройка базового окружения для автоматизированного тестирования

Начнем с настройки машины на Windows 10 под запуск WebDriver через JS. Вызовы WebDriver с машины почти всегда будут асинхронные. Чтобы код было легче читать, мы использовали ES2016 async/await, а не Promises или колбеки.

Вам понадобится install node.js новее v7.6 или Babel для кросскомпиляции для поддержки функции async/await. Редактирование и отладку кода будем выполнять в Visual Studio Code.

WebDriverJS для Microsoft Edge

У каждого браузера будет бинарный файл, который должен будет храниться локально для взаимодействия с браузером. Этот бинарный файл будет вызываться вашим кодом через Selenium WebDriver API. Последние загрузки и документацию для Microsoft Edge WebDriver можно найти по ссылке.

Версия Edge, под которой вы хотите запускать тесты, должна совпадать с версией MicrosoftWebDriver.exe. Мы будем использовать стабильную версию Edge (16.16299) с соответствующим файлом MicrosoftWebDriver.exe версии 5.16299.

Поместите MicrosoftWebDriver.exe в папку, из которой будет запускаться тест. Если запустить бинарный файл, откроется окно консоли с URL и портом, через который WebDriverJS будет обрабатывать посылаемые запросы.

WebDriverJS для других браузеров

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

Apple Safari: Вместе с Safari 10+

Microsoft Internet Explorer: IEDriver для Selenium project

Selenium WebDriverJS для JavaScript

Чтобы взаимодействовать с только что скачанным через JS бинарным файлом, необходимо установить библиотеку автоматизации Selenium WebDriver. Ее можно легко установить как node пакет:

Если описывать задачу, которую мы здесь будем решать, в двух словах, то состоит она во взаимодействии настольного приложение с браузером. Мне известно о существовании Selenium WebDriver, однако, насколько я знаю, возможности его ограничиваются имитацией действий пользователя, доступом к DOM, управлением вкладками, окнами и тому подобными вещами. Мне же хотелось бы получить как можно больше полномочий, чтобы выполнять из внешнего приложения задачи, доступные только расширениям браузера. Вот, собственно, исследованием данного вопроса мы здесь и будем заниматься.
Сразу изложу идею. Поскольку изначально было заявлено, что действия, которые браузер должен будет выполнять под управлением внешней программы, то, соответственно помимо самой программы нам понадобится создать расширение для браузера. Далее нам нужно будет реализовать взаимодействие между расширением и приложением. На сегодняшний день эта задача решается предельно просто, а именно посредством веб-сокетов (WebSocket). То есть идея в том, чтобы в настольном приложении запустить сервер, принимающий подключения веб-сокетов, из расширения подключиться к этому серверу и далее через это подключение обмениваться командами и данными. Связь будет двусторонней, поэтому помимо задачи, обозначенной в заголовке, эту же модель взаимодействия можно использовать и для отправки данных приложению, и, если понадобится, то и управления приложением из расширения. Наша же основная задача состоит в том, чтобы отдать из приложения команду расширению и, если команда возвратит ответ, то получить этот ответ.

Тестовое приложение

Для начала, чтобы уже сразу проверить сервер в работе, мы создадим простое приложение, код которого возьмем из документации с небольшими изменениями и попробуем его сконнектить с простой веб-страничкой.
Создаем консольное приложение. Добавляем пакеты NuGet
SuperSocket.WebSocket и SuperSocket.Engine
В результате помимо этих двух пакетов будет добавлен пакет SuperSocket. У меня все эти три пакета имеют версию 1.6.6.1
После установки SuperSocket.Engine в проекте появится папка Config с файлами log4net.config и log4net.unix.config. Эти файлы нужно выделить, перейти к свойствам и для свойства «Действие при сборке» выбрать «Содержание», а для свойства «Копировать в выходной каталог» выбрать значение «Копировать более позднюю версию»

Это основная часть того, что мы будем делать. Расширение мы будем писать для 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 разместим поле для ввода номера порта, кнопку для сохранения номера порта, чтобы не вводить каждый раз, и кнопку подключения. Последняя – это кнопка-переключатель, код взял отсюда.
default_popup.html

Команды, которые мы будем принимать это connect, disconnect и connectionInfo. Создаем переменную socket.
Кроме того, нам нужно будет добавить две функции, для подписки на события сокета и отписки от них. Когда соединение будет разрываться, мы будем отписываться от всех событий и затирать ссылку на сокет, а при подключении будем создавать новый экземпляр и подписываться на его события.
А вот что делать с командами, поступающими от приложения? Здесь проблема в том, какие именно команды нам нужны. Например можно создать команду, которая будет выполнять определенный JavaScript код на активной вкладке браузера.
То оно должно будет вернуть приложению заголовок активной страницы. Но мы не можем предусмотреть таким образом все. В этой связи много команд лучше не реализовывать, поскольку это сильно усложнит использование расширения и вряд ли охватит все возможности. Лучше сделать наиболее часто используемые команды и главную команду eval , которая будет принимать код, который, в свою очередь, будет передан функции eval, затем исполнен и результат возвращен приложению.
Таким образом полный код бэкграунд-скрипта у нас будет следующим
background.js

Здесь помимо скриптов мы еще установили правила для стилей и соединений. Для стилей это нужно из-за того, что обычно по умолчанию в расширениях разрешены встроенные в страницу стили, а поскольку у нас для default-src установлено значение ‘self’, то для неуказанных параметров применяться будет именно оно, стало быть его нужно добавить. Ну и для connect-src мы указали, что будем соединяться только с локалхостом через вебсокет. Само собой можно добавить, скажем wss://127.0.0.1:* или если мы собираемся использовать соединения с разными адресами, протоколами и т. д., то все тоже надо прописать. Но поскольку в данном случае все это вроде как не нужно, я думаю, того что есть будет достаточно.
С расширением закончили, перейдем к приложению.

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


Для браузера Firefox загружаем драйвер geckodriver для своей версии ОС и прописываем путь к нему в переменной окружения PATH .

Пробуем управлять браузером удаленно:


Объект WebDriver имеет большое количество методов, предназначенных для поиска элементов на странице. Эти методы можно поделить на две группы: find_element_*() и find_elements_*() . Методы первой группы возвращают одиночный объект WebElement , который представляет первый из найденных на странице элементов. Методы второй группы возвращают список объектов WebElement , соответствующих запросу.

  • find_element_by_id() — элемент с указанным id
  • find_element_by_name() — элемент с указанным значением атрибута name
  • find_element_by_tag_name() — элемент с указанным именем тега
  • find_element_by_class_name() — элемент с указанным CSS-классом
  • find_element_by_css_selector() — элемент с указанным CSS-селектором
  • find_element_by_link_text() — элемент <a> с указанным текстом
  • find_element_by_partial_link_text() — элемент <a> , содержащий указанный текст
  • find_elements_by_name() — элементы с указанным значением атрибута name
  • find_elements_by_tag_name() — элементы с указанным именем тега
  • find_elements_by_class_name() — элементы с указанным CSS-классом
  • find_elements_by_css_selector() — элементы с указанным CSS-селектором
  • find_elements_by_link_text() — элементы <a> с указанным текстом
  • find_elements_by_partial_link_text() — элементы <a> , содержащие указанный текст

Находим поля «Имя пользователя» и «Пароль»:

Свойства и методы объекта WebElement :

  • tag_name — имя тега
  • get_attribute() — значение атрибута с указанным именем
  • text — текст, содержащийся в элементе
  • clear() — удаляет текст, введенный в текстовое поле
  • is_displayed() — возвращает True , если элемент видимый и False в противном случае
  • is_enabled() — возвращает True для элемента ввода, если элемент активизирован и False в противном случае
  • is_selected() — возвращает True для checkbox , если элемент выбран и False в противном случае
  • location — словарь с ключами x и y позиции элемента на странице

Клик на элементе страницы

Объект WebElement имеет метод click() , имитирующий клик мышкой на элементе. Этот метод можно использовать для перехода по ссылке, для отправки данных формы или инициирования любого другого действия, которое может быть запущено кликом на элементе.

Заполнение и отправка формы

Для отправки данных формы:

  • находим нужные поля <input>
  • заполняем их, используя метод send_keys()
  • вызываем метод submit() для любого элемента формы или метод click() для кнопки Submit


Отправка кодов специальных клавиш

Selenium включает модуль для отправки в браузер нажатий специальных клавиш: Up, Down, Left, Right, Enter, Return, Home, PageUp, PageDown, End, Escape, BackSpace, Delete, F1…F12, Tab.

Добра, ЛОР. Подскажите, пожалуйста, как можно эмулировать браузер на PHP.

Или есть какие-либо другие методы для того, чтобы Яндекс не ругался на бота?



есть молоток, есть гвоздь. к чему я это - как построить звездолет?

Искать по запросу headless browser.


Парсишь поисковую выдачу? Браузер не поможет.


И это никак не обойти? Задержку какую-нибудь добавить, имитацию пользователя?


This. Посмотри, какие запросы делает браузер к Яндексу, имитируй их на пыхе.


Задержку какую-нибудь добавить, имитацию пользователя

Ты много знаешь пользователей, которые сутками сидят и долбят поиск?

По теме - для начала нужно применять куки, которые отдаёт яндекс (особенно spravka).


но, скорее всего, платно


На пхп не знаю, но настоятельно советую puppeteer. Ты в джаваскрипт умеешь? Если да, то это идеальный вариант. Но тебя всё равно вычислят и набьют еб.


А разве такое вообще бывает? Кое-что, к примеру DOM, можно строить и изменять с помощью соответствующих либ(их иногда для сложных случаев парсинга юзают), но полностью эмулировать браузер на php не реально. Тут вам нужен Headless Chrome c remote-debugging, Selenium, phantomjs или что-то вроде того.

Нет такого нормального, все написано для node.js



Премного благодарю за инструмент. Немного прочитал и это мощно. Будем пытаться завести

В любом случае вы придёте к тому, что после N запросов будет каптча тупо потому что живой человек обычно не делает 24/7 монотонно запросы. Так что либо периодически менять IP (со сбросом кук, разумеется), либо подключать сервисы платного разгадывания каптчи (на них тысячи индусов за копейки в прямом смысле разгадывают каптчи, которые ты им суёшь через API). Либо и то, и другое.


Парсинг 24/7 не нужен. Он всего лишь должен будет единожды собирать данные по указанным ключам


Спасибо за ссылку, интересный проект этот Puppeteer. Нужно будет пощупать его в деле как нибудь.


если для тестов, то ничего нет лучше cypress. а то, просто запускается браузер без графического интерфейса headless, запускается веб-сокет сервер и безголовому браузеру отдаем приказы по веб-сокетам. есть еще питоновская версия (требует опыта работы с asyncio), но на js кошернее


Иногда просто безголовый браузер нужен, а иногда - для решение для тестов. Спасибо, посмотрю и cypress.

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