Каталог решений - Управление Selenium Web Driver из 1С (или парсинг из 1С по-взрослому)

Управление Selenium Web Driver из 1С (или парсинг из 1С по-взрослому)

Управление Selenium Web Driver из 1С (или парсинг из 1С по-взрослому)

В наличии

Нет-нет, да и появляются заказы на парсинг сайтов. Обычно, для этих целей использую net. + Selenium nuget package. Однако, если клиент использует стек технологий 1С, то предпочтительней было бы не пороть отсебятину, а применять уже знакомую пользователям парадигму работы с ИТ-продуктами — без каких-либо лишних прослоек, вроде net. или JS. И вот я задался вопросом: «а можно ли?».

Категория:

Описание

Что такое Selenium — можно почитать здесь: https://habr.com/ru/post/152653/.

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

Основным назначением продукта является организация автоматического тестирования, но можно придумать и что-то еще.

Selenium в настоящий момент поддерживает управление следующими браузерами:

 

БраузерОперационная системаРазработчик
Chromium/Google ChromeWindows/macOS/LinuxGoogle
FirefoxWindows/macOS/LinuxMozilla
EdgeWindows 10Microsoft
Internet ExplorerWindowsSelenium Project
SafarimacOS El Capitan и более новыеApple
OperaWindows/macOS/LinuxOpera

 

В своих экспериментах я решил использовать связку Selenium + Chrome + Windows10.

При этом, основной проблемой являлось то, что с Selenium’ом работает только ограниченный круг third-part — библиотек (которые предоставляют сервис высокого уровня абстракции (читай — удобства)).

В частности, официально, поддерживаются такие языки:

— C#

— Ruby

— Java

— Pyton (конечно-же))

— JS

https://www.selenium.dev/downloads/

Гениальнейшая (без сарказма и иронии) платформа 1с, почему-то в перечень этих библиотек не входит(.

Покопавшись немного в документации, я нашел информацию, что на самом деле-то Selenium, при запуске, поднимает локальный web-server, который обладает развесистым низкоуровневым API. Сложность работы с ним состоит в том, что он именно "низкоуровневый", то-есть для получения результата требует много кода.

Вот его описание:  https://www.w3.org/TR/webdriver/

Приготовившись читать много доки на не нашем языке, я приступил к делу.

 

1. Установка Selenium

Поскольку программный комплекс состоит как-бы из 3х звеньев (нативный web-driver + сервер Selenium + браузер), то имея уже установленный на компьютере Хром, мне осталось поставить Web-driver и сервер Selenium.

 

1.1. Установка Web-driver’а

Web-driver качается вот отсюда: https://chromedriver.chromium.org/

Однако, как оказалось, драйвер очень чувствителен к версии браузера, установленного на компьютере и чтобы подобрать и скачать нужную версию, требуется пройти квест. Почему они не сделали js-скрипт на странице, который сам определяет версию браузера и выдают нужный драйвер — я не понял. Короче вот: https://chromedriver.chromium.org/downloads/version-selection.

Если кратко, то чтобы скачанное запустилось, следует:

1. зайти в настройки браузера (3 точки — сверху, справа — Настройки — О браузере)

 

 

2. Из версии браузера убрать последнюю часть и получившееся добавить к строке адреса https://chromedriver.storage.googleapis.com/LATEST_RELEASE_.

Должно получиться так: https://chromedriver.storage.googleapis.com/LATEST_RELEASE_96.0.4664

3. Перейдя по получившемуся адресу, вы попадете на страницу с версией драйвера, которая подойдет под вашу версию браузера.

4. Решительно копируйте ее в буфер обмена и добавляйте к строке https://chromedriver.storage.googleapis.com/index.html?path=

Должно получиться так: https://chromedriver.storage.googleapis.com/index.html?path=96.0.4664.45/ (в конце еще следует добавить прямой слеш). 

5. После перехода по полученному адресу, вы попадете на нужную страницу закачек, на которой следует выбрать драйвер под Вашу ОС.

Качаем и кладем в папочку (без кириллицы и без пробелов в пути к папочке — в общем все как обычно).

 

1.2. Установка сервера Selenium

Здесь все просто: https://www.selenium.dev/downloads/

Качаем отсюда и кидаем в ту-же папку, что и web-driver.

 

2. Запуск

В принципе, в простейшем случае (без организации грида (сетки из множества конечных узлов Selenium)) современная версия комплекса сама себя настраивает. Чтобы все заиграло одной машине, достаточно в каталоге со скачанными образами сервера и веб-драйвера создать bat-файл с таким вот содержимым: 

//////////////////////

java -jar selenium-server-4.1.0.jar standalone
pause

//////////////////////

Произойдет запуск сервера в режиме одиночки.

Если все скачано и запущено прямо, то в запустившейся консоли не должно быть сообщений об ошибках).

По-умолчанию сервер стартует на порту 4444.

Теперь можно порулить.

 

3. Руление

3.1. Итак, сначала проверим: как он там.

Кидаем  вот такой GET: http://localhost:4444/status (https://www.w3.org/TR/webdriver/#status) на localhost.

Если все ок, то должна быть возвращена структура, описывающая состояние сервера:

  {
  	"value": {
  		"ready": true,
  		"message": "Selenium Grid ready.",
  		"nodes": [
  			{
  				"id": "10811e6b-17dc-4163-bead-a38221dbc603",
  				"uri": "http://ххх.ххх.ххх.ххх:4444",
  				"maxSessions": 6,
  				"osInfo": {
  					"arch": "amd64",
  					"name": "Windows 10",
  					"version": "10.0"
  				},
  				"heartbeatPeriod": 60000,
  				"availability": "UP",
  				"version": "4.1.0 (revision 87802e897b)",
  				"slots": [
  					{
  						"lastStarted": "1970-01-01T00:00:00Z",
  						"session": null,
  						"id": {
  							"hostId": "10811e6b-17dc-4163-bead-a38221dbc603",
  							"id": "b088a136-98d4-4b06-91c4-6b5a44c71a1d"
  						},
  						"stereotype": {
  							"browserName": "chrome",
  							"platformName": "WIN10"
  						}
  					},
  					{
  						"lastStarted": "1970-01-01T00:00:00Z",
  						"session": null,
  						"id": {
  							"hostId": "10811e6b-17dc-4163-bead-a38221dbc603",
  							"id": "f7f30980-09c3-481d-a202-9d8ae6600aa4"
  						},
  						"stereotype": {
  							"browserName": "chrome",
  							"platformName": "WIN10"
  						}
  					},
  					{
  						"lastStarted": "1970-01-01T00:00:00Z",
  						"session": null,
  						"id": {
  							"hostId": "10811e6b-17dc-4163-bead-a38221dbc603",
  							"id": "a820aa7d-5e18-4162-8c93-990c362e2d18"
  						},
  						"stereotype": {
  							"browserName": "chrome",
  							"platformName": "WIN10"
  						}
  					},
  					{
  						"lastStarted": "1970-01-01T00:00:00Z",
  						"session": null,
  						"id": {
  							"hostId": "10811e6b-17dc-4163-bead-a38221dbc603",
  							"id": "28bf1c2b-f351-44a2-9bdb-ea2e029e3fb1"
  						},
  						"stereotype": {
  							"browserName": "chrome",
  							"platformName": "WIN10"
  						}
  					},
  					{
  						"lastStarted": "1970-01-01T00:00:00Z",
  						"session": null,
  						"id": {
  							"hostId": "10811e6b-17dc-4163-bead-a38221dbc603",
  							"id": "a50d9495-831a-4148-89b1-8723608a3053"
  						},
  						"stereotype": {
  							"browserName": "chrome",
  							"platformName": "WIN10"
  						}
  					},
  					{
  						"lastStarted": "1970-01-01T00:00:00Z",
  						"session": null,
  						"id": {
  							"hostId": "10811e6b-17dc-4163-bead-a38221dbc603",
  							"id": "659d6bc3-7463-4c16-88f7-27395179fe62"
  						},
  						"stereotype": {
  							"browserName": "chrome",
  							"platformName": "WIN10"
  						}
  					}
  				]
  			}
  		]
  	}
  }

Ключевой момент здесь: "ready": true.

 

3.2. Теперь запускаем экземпляр сеанса (по-факту открывается Хром)

Для этого кидаем такой POST: http://localhost:4444/session (https://www.w3.org/TR/webdriver/#new-session)

с таким телом запроса:

  {
  	"capabilities": {
  		"firstMatch": [
  			{
  				"browserName": "chrome",
  				"pageLoadStrategy": "eager"
  			}
  		],
  		"timeouts": {
  			"Script Timeout": "1800,000",
  			"Page Load Timeout": "1800,000",
  			"Implicit Wait Timeout": "1800,000"
  		}
  	}
  }
  "pageLoadStrategy = игорь"

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

После этого волшебным образом должно открыться окно браузера, а ответ должен быть такой:

  {
  	"value": {
  		"sessionId": "bdfafa0bcbac62421cc184328f3b44bb",
  		"capabilities": {
  			"acceptInsecureCerts": false,
  			"browserName": "chrome",
  			"browserVersion": "96.0.4664.110",
  			"chrome": {
  				"chromedriverVersion": "96.0.4664.45 (76e4c1bb2ab4671b8beba3444e61c0f17584b2fc-refs/branch-heads/4664@{#947})",
  				"userDataDir": "C:\\Users\\ХХХХХХХ\\AppData\\Local\\Temp\\scoped_dir48368_636555220"
  			},
  			"goog:chromeOptions": {
  				"debuggerAddress": "localhost:65126"
  			},
  			"networkConnectionEnabled": false,
  			"pageLoadStrategy": "eager",
  			"platformName": "windows",
  			"proxy": {
  			},
  			"se:cdp": "ws://ххх.ххх.ххх.ххх:4444/session/187533817ed102e7eb1b766d3a467194/se/cdp",
  			"se:cdpVersion": "96.0.4664.110",
  			"setWindowRect": true,
  			"strictFileInteractability": false,
  			"timeouts": {
  				"implicit": 0,
  				"pageLoad": 300000,
  				"script": 30000
  			},
  			"unhandledPromptBehavior": "dismiss and notify",
  			"webauthn:extension:credBlob": true,
  			"webauthn:extension:largeBlob": true,
  			"webauthn:virtualAuthenticators": true
  		}
  	}
  }

Больше всего из этого нам важно поле "sessionId". На основе sessionId строятся все последующие запросы управления.

 

3.3. Заставим Хром открыть страницу Яндекса

POST: http://localhost:4444/session/bdfafa0bcbac62421cc184328f3b44bb/url (https://www.w3.org/TR/webdriver/#navigate-to)

Тело:

  {"url": "https://yandex.ru/"}

Браузер должен открыть страницу ИТ-гиганта, а запрос вернуть такой JSON:

  {
  	"value": null
  }

 

3.4. Найдем на странице заголовки новостей

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

В самом простом случае можно искать по ID, по Классу или все вместе — используя выражения xPath.

https://www.w3.org/TR/webdriver/#find-elements

Кидаем POST: http://localhost:4444/session/bdfafa0bcbac62421cc184328f3b44bb/elements

Тело:

  {
  	"using": "xpath",
  	"value": "//span[contains(@class,'news__item-content')]"
  }

Разбор синтаксиса xpath выходит за рамки этого исследования, так-что просто напишу, что данное выражение ищет по всему дереву элементов html-страницы элементы "span" с классом ‘news__item-content’ (это заголовки новостей).

Запрос возвращает такой ответ:

  {
  	"value": [
  		{
  			"element-6066-11e4-a52e-4f735466cecf": "714dd669-9d30-41ba-a02d-3c0d4fde3ccf"
  		},
  		{
  			"element-6066-11e4-a52e-4f735466cecf": "cffcd89f-7403-4368-a4fc-8da4374002a8"
  		},
  		{
  			"element-6066-11e4-a52e-4f735466cecf": "d150c945-cd8f-4342-a605-1e79cdfe8f1e"
  		},
  		{
  			"element-6066-11e4-a52e-4f735466cecf": "c452dcf6-500b-4b9c-8aee-c6034e636ca5"
  		},
  		{
  			"element-6066-11e4-a52e-4f735466cecf": "27f63f2c-b393-4646-804d-a47a38d0f320"
  		},
  		{
  			"element-6066-11e4-a52e-4f735466cecf": "1383eb44-9398-476b-a2c4-88621e8584e3"
  		},
  		{
  			"element-6066-11e4-a52e-4f735466cecf": "7729a7e1-27ae-43fb-9ff9-1b4dae4cbe4d"
  		},
  		{
  			"element-6066-11e4-a52e-4f735466cecf": "6c5cea92-4b7f-4697-a71a-4733938f030b"
  		},
  		{
  			"element-6066-11e4-a52e-4f735466cecf": "da63d035-d2f4-43f3-80c6-d14c1cdb7781"
  		},
  		{
  			"element-6066-11e4-a52e-4f735466cecf": "e31e1bd3-50cf-4cee-b415-6b8a782c0e91"
  		}
  	]
  }

Это перечень идентификаторов найденных элементов.

 

3.5. Теперь надо-бы получить текст из этих элементов

Для этой цели у Selenium’а запасен запрос https://www.w3.org/TR/webdriver/#get-element-text

Он тупо выдергивает подчиненный текст элемента (если он есть).

Get: http://localhost:4444/session/bdfafa0bcbac62421cc184328f3b44bb/element/714dd669-9d30-41ba-a02d-3c0d4fde3ccf/text

Ответ:

  {
  	"value": "Генеральная прокуратура обвинила «Мемориал» в реабилитации нацистских преступников"
  }

Элементы можно обойти в цикле и сложить в массив все строки, что попались — с проверкой на пустые строки.

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

 

3.6. В завершении, по-хорошему следует грохнуть сессию

https://www.w3.org/TR/webdriver/#delete-session

 

Если внимательно почитать документацию, то там есть еще много замечательных возможностей: https://www.w3.org/TR/webdriver/#state, https://www.w3.org/TR/webdriver/#element-interaction (взаимодействие с элементами) и т.д.

В заключении приложу к статье базу данных с реализацией всего вышеописанного в коде 1с.

Тестировалось все на платформе 1с версии 8.3.18.1334 x64.

has been added to your cart:
Оформление заказа