При разработке под управляемые формы часто возникает задача обработки HTML-кода непосредственно на клиенте. Например, нужно получить данные с веб-страницы и извлечь из них конкретную информацию, не нагружая сервер. Стандартный подход, который отлично работает в тонком и толстом клиентах, — использование COM-объекта. Однако в веб-клиенте этот метод внезапно перестает работать. Разберем, почему так происходит и какие существуют альтернативные способы решения этой задачи.
Проанализируем ситуацию. Вы пишете код, который должен выполняться на клиенте (&НаКлиенте), и пытаетесь создать COM-объект для парсинга HTML:
&НаКлиенте
Процедура РаспарситьHTMLНаКлиенте(Команда)
HTMLТекст = "<html><body><h3>Заголовок</h3><p>Какой-то текст.</p></body></html>";
Попытка
Док = Новый COMОбъект("HTMLFile");
Док.write(HTMLТекст);
// ... дальнейшая работа с DOM-моделью документа
Сообщить("COM-объект успешно создан!");
Исключение
Сообщить("Не удалось создать COM-объект: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
В толстом или тонком клиенте этот код выполнится успешно. Но при запуске в веб-клиенте вы получите ошибку или значение Неопределено. Причина кроется в архитектуре и безопасности веб-клиента.
Веб-клиент 1С работает внутри браузера. Браузер — это изолированная среда (так называемая "песочница" или "sandbox"), которая в целях безопасности жестко ограничивает доступ веб-страниц к локальным ресурсам компьютера пользователя, включая файловую систему и компоненты операционной системы. COM-объекты (Component Object Model) — это технология Microsoft Windows, и для их создания требуется прямой доступ к системным библиотекам. Браузер блокирует такие вызовы из соображений безопасности, чтобы вредоносный сайт не смог получить контроль над вашим компьютером.
Таким образом, использование COMОбъект на стороне клиента в веб-клиенте 1С является фундаментальным ограничением платформы. Это не ошибка, а осознанное проектное решение.
Теперь, когда мы выяснили причину, рассмотрим рабочие альтернативы.
Самый простой и очевидный способ — перенести код, работающий с COM-объектом, на сервер. На сервере нет ограничений браузера, и код выполнится без проблем.
Как это работает:
&НаСервере), которая принимает на вход строку с HTML.COMОбъект("HTMLFile"), парсим HTML и извлекаем нужные данные.Посмотрим на пример. Допустим, нам нужно извлечь текст из тега <h3>.
&НаКлиенте
Процедура РаспарситьHTML(Команда)
HTMLТекст = "<html><body><h1>Это важный заголовок</h3><p>Просто текст.</p></body></html>";
// Вызываем серверную функцию и получаем результат
Заголовок = ПолучитьЗаголовокИзHTMLНаСервере(HTMLТекст);
Сообщить("Извлеченный заголовок: " + Заголовок);
КонецПроцедуры
&НаСервере
Функция ПолучитьЗаголовокИзHTMLНаСервере(HTMLТекст)
Попытка
Док = Новый COMОбъект("HTMLFile");
Док.write(HTMLТекст);
// Ищем первый элемент с тегом h1
ЭлементЗаголовка = Док.getElementsByTagName("h1").item(0);
Если ЭлементЗаголовка <> Неопределено Тогда
Возврат ЭлементЗаголовка.innerText;
Иначе
Возврат "";
КонецЕсли;
Исключение
Сообщить("Ошибка при парсинге HTML на сервере: " + ОписаниеОшибки());
Возврат "";
КонецПопытки;
КонецФункции
Преимущества:
Недостатки:
Этот метод хорошо подходит, если объем передаваемых данных невелик.
Если задача достаточно проста и структура HTML предсказуема, можно обойтись без полноценного парсинга DOM-дерева и использовать встроенные средства работы со строками.
Вариант А: Строковые функции
Используем функции СтрНайти(), Сред(), СтрДлина() для "вырезания" нужного фрагмента.
&НаКлиенте
Процедура НайтиЗаголовокСтроками(HTMLТекст)
НачалоТега = СтрНайти(HTMLТекст, "<h3>");
КонецТега = СтрНайти(HTMLТекст, "</h3>");
Если НачалоТега > 0 И КонецТега > НачалоТега Тогда
// Вычисляем позицию начала текста и его длину
ПозицияНачала = НачалоТега + СтрДлина("<h3>");
ДлинаТекста = КонецТега - ПозицияНачала;
Результат = Сред(HTMLТекст, ПозицияНачала, ДлинаТекста);
Сообщить(Результат); // "Это важный заголовок"
КонецЕсли;
КонецПроцедуры
Вариант Б: Регулярные выражения
Это более мощный и гибкий способ поиска по шаблону. В 1С нет встроенного объекта для регулярных выражений, но можно использовать COM-объект V8.RegExp, который, к счастью, доступен на клиенте. Однако более современный и кроссплатформенный подход — использование объекта Соответствие, который является частью платформы и работает в веб-клиенте.
К сожалению, прямой работы с регулярными выражениями в 1С нет. Для этого обычно используют внешние компоненты или, как мы уже выяснили, COM-объекты, недоступные в веб-клиенте. Поэтому этот вариант в чистом виде на клиенте не реализуем. Однако идея поиска по шаблону остается актуальной, и ее можно реализовать с помощью JavaScript.
Преимущества (для строковых функций):
Недостатки:
<h1 class="main"> вместо <h1>) сломает логику.Это самый правильный и мощный способ для работы с HTML в веб-клиенте. Раз уж мы работаем в браузере, давайте использовать его сильные стороны! У браузера есть встроенный, высокопроизводительный движок для работы с JavaScript и парсинга HTML.
Основная идея: мы делегируем задачу парсинга браузеру, написав небольшой скрипт на JavaScript, а затем забираем результат обратно в 1С.
Разберем по шагам:
HTMLПоле) с типом "Строка". Перетащите его на форму и в свойствах элемента формы установите Вид в "Поле HTML документа". Это поле сможет не только отображать HTML, но и выполнять JavaScript.window.parent.notifyIC(data), чтобы отправить данные в 1С. В 1С это событие будет перехвачено в обработчике ПриНажатии нашего поля HTML-документа.Посмотрим на комплексный пример:
Сначала настроим форму. У нас есть реквизит HTMLПоле (Строка) и элемент формы "HTMLПоле" (Вид: Поле HTML документа). Для этого элемента создадим обработчик события ПриНажатии.
// Код в модуле формы
// Обработчик события от JavaScript
&НаКлиенте
Процедура HTMLПолеПриНажатии(Элемент, Событие)
// В Событие.Данные придет результат из JS
Сообщить("Получен результат из JavaScript: " + Событие.Данные);
КонецПроцедуры
// Процедура, которая запускает парсинг
&НаКлиенте
Процедура РаспарситьЧерезJS(Команда)
// HTML-код, который мы хотим разобрать
ПередаваемыйHTML = "<html><body><div id='content'><p>Ненужный текст.</p><h1>Нужный заголовок!</h3></div></body></html>";
// Формируем HTML-документ со скриптом
ТекстСкрипта = СформироватьТекстСкрипта(ПередаваемыйHTML);
// Помещаем наш скрипт в поле на форме, что приведет к его исполнению
Элементы.HTMLПоле.УстановитьТекст(ТекстСкрипта);
КонецПроцедуры
&НаСервереБезКонтекста
Функция СформироватьТекстСкрипта(HTMLДляПарсинга)
// В 1С неудобно писать многострочный JS, поэтому используем массив строк
ТекстJS = Новый Массив;
ТекстJS.Добавить("<html><head><script>");
ТекстJS.Добавить("function parseAndReturn() {");
ТекстJS.Добавить(" const parser = new DOMParser();");
// Передаем HTML как строку в JS. Важно экранировать спецсимволы.
ТекстJS.Добавить(" const htmlString = " + XMLСтрока(HTMLДляПарсинга) + ";");
ТекстJS.Добавить(" const doc = parser.parseFromString(htmlString, 'text/html');");
ТекстJS.Добавить(" const header = doc.querySelector('h1');");
ТекстJS.Добавить(" let result = 'Заголовок не найден';");
ТекстJS.Добавить(" if (header) {");
ТекстJS.Добавить(" result = header.textContent;");
ТекстJS.Добавить(" }");
// Отправляем результат обратно в 1С
ТекстJS.Добавить(" window.parent.notifyIC(result);");
ТекстJS.Добавить("}");
ТекстJS.Добавить("</script></head>");
// Запускаем нашу функцию сразу после загрузки страницы
ТекстJS.Добавить("<body onload='parseAndReturn()'>");
ТекстJS.Добавить("</body></html>");
Возврат СтрСоединить(ТекстJS, Символы.ПС);
КонецФункции
Как это работает:
РаспарситьЧерезJS мы формируем HTML-документ, который содержит наш JavaScript-код.XMLСтрока() используется для корректного экранирования кавычек и других символов внутри HTML-строки, чтобы она стала валидной строкой JavaScript.HTMLПоле. Браузер тут же его исполняет.onload в теге <body> запускает нашу JS-функцию parseAndReturn().DOMParser для создания DOM-дерева из строки и querySelector для поиска нужного элемента (в данном случае, <h1>).window.parent.notifyIC(result).ПриНажатии у элемента HTMLПоле, помещая данные в параметр Событие.Данные.HTMLПолеПриНажатии получает данные и выводит их пользователю.Преимущества:
Недостатки:
Мы рассмотрели несколько способов парсинга HTML в веб-клиенте 1С. Выбор конкретного метода зависит от вашей задачи: