Как разобрать HTML-строку в веб-клиенте 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке под управляемые формы часто возникает задача обработки HTML-кода непосредственно на клиенте. Например, нужно получить данные с веб-страницы и извлечь из них конкретную информацию, не нагружая сервер. Стандартный подход, который отлично работает в тонком и толстом клиентах, — использование COM-объекта. Однако в веб-клиенте этот метод внезапно перестает работать. Разберем, почему так происходит и какие существуют альтернативные способы решения этой задачи.

Почему не работает COMОбъект("HTMLFile") в веб-клиенте?

Проанализируем ситуацию. Вы пишете код, который должен выполняться на клиенте (&НаКлиенте), и пытаетесь создать 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С является фундаментальным ограничением платформы. Это не ошибка, а осознанное проектное решение.

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

Решение 1: Перенос логики на сервер

Самый простой и очевидный способ — перенести код, работающий с COM-объектом, на сервер. На сервере нет ограничений браузера, и код выполнится без проблем.

Как это работает:

  1. Создаем экспортную процедуру или функцию на сервере (&НаСервере), которая принимает на вход строку с HTML.
  2. Внутри этой серверной функции создаем COMОбъект("HTMLFile"), парсим HTML и извлекаем нужные данные.
  3. Возвращаем извлеченные данные (например, в виде структуры или массива) обратно на клиент.

Посмотрим на пример. Допустим, нам нужно извлечь текст из тега <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 на сервере: " + ОписаниеОшибки());
        Возврат "";
    КонецПопытки;
КонецФункции

Преимущества:

Недостатки:

Этот метод хорошо подходит, если объем передаваемых данных невелик.

Решение 2: Парсинг на клиенте с помощью строковых функций и регулярных выражений

Если задача достаточно проста и структура HTML предсказуема, можно обойтись без полноценного парсинга DOM-дерева и использовать встроенные средства работы со строками.

Вариант А: Строковые функции

Используем функции СтрНайти(), Сред(), СтрДлина() для "вырезания" нужного фрагмента.


&НаКлиенте
Процедура НайтиЗаголовокСтроками(HTMLТекст)
    НачалоТега = СтрНайти(HTMLТекст, "<h3>");
    КонецТега = СтрНайти(HTMLТекст, "</h3>");
    
    Если НачалоТега > 0 И КонецТега > НачалоТега Тогда
        // Вычисляем позицию начала текста и его длину
        ПозицияНачала = НачалоТега + СтрДлина("<h3>");
        ДлинаТекста = КонецТега - ПозицияНачала;
        
        Результат = Сред(HTMLТекст, ПозицияНачала, ДлинаТекста);
        Сообщить(Результат); // "Это важный заголовок"
    КонецЕсли;
КонецПроцедуры

Вариант Б: Регулярные выражения

Это более мощный и гибкий способ поиска по шаблону. В 1С нет встроенного объекта для регулярных выражений, но можно использовать COM-объект V8.RegExp, который, к счастью, доступен на клиенте. Однако более современный и кроссплатформенный подход — использование объекта Соответствие, который является частью платформы и работает в веб-клиенте.

К сожалению, прямой работы с регулярными выражениями в 1С нет. Для этого обычно используют внешние компоненты или, как мы уже выяснили, COM-объекты, недоступные в веб-клиенте. Поэтому этот вариант в чистом виде на клиенте не реализуем. Однако идея поиска по шаблону остается актуальной, и ее можно реализовать с помощью JavaScript.

Преимущества (для строковых функций):

Недостатки:

Решение 3: Использование JavaScript (рекомендуемый способ)

Это самый правильный и мощный способ для работы с HTML в веб-клиенте. Раз уж мы работаем в браузере, давайте использовать его сильные стороны! У браузера есть встроенный, высокопроизводительный движок для работы с JavaScript и парсинга HTML.

Основная идея: мы делегируем задачу парсинга браузеру, написав небольшой скрипт на JavaScript, а затем забираем результат обратно в 1С.

Разберем по шагам:

  1. Добавляем на форму поле HTML-документа.
    Создайте на форме реквизит (например, HTMLПоле) с типом "Строка". Перетащите его на форму и в свойствах элемента формы установите Вид в "Поле HTML документа". Это поле сможет не только отображать HTML, но и выполнять JavaScript.
  2. Подготавливаем HTML и JavaScript.
    В коде 1С мы сформируем строку, содержащую JavaScript-функцию. Эта функция будет принимать HTML-текст, парсить его и возвращать результат в 1С.
  3. Организуем вызов и возврат данных.
    Для связи между 1С и JavaScript используется специальный механизм. Из JS мы вызовем функцию 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, Символы.ПС);
КонецФункции

Как это работает:

  1. При вызове РаспарситьЧерезJS мы формируем HTML-документ, который содержит наш JavaScript-код.
  2. Функция XMLСтрока() используется для корректного экранирования кавычек и других символов внутри HTML-строки, чтобы она стала валидной строкой JavaScript.
  3. Этот HTML-документ помещается в поле HTMLПоле. Браузер тут же его исполняет.
  4. Событие onload в теге <body> запускает нашу JS-функцию parseAndReturn().
  5. Внутри функции мы используем стандартный браузерный API: DOMParser для создания DOM-дерева из строки и querySelector для поиска нужного элемента (в данном случае, <h1>).
  6. Найденный результат отправляется в 1С через window.parent.notifyIC(result).
  7. Платформа 1С перехватывает этот вызов и генерирует событие ПриНажатии у элемента HTMLПоле, помещая данные в параметр Событие.Данные.
  8. Наш обработчик HTMLПолеПриНажатии получает данные и выводит их пользователю.

Преимущества:

Недостатки:

Итог

Мы рассмотрели несколько способов парсинга HTML в веб-клиенте 1С. Выбор конкретного метода зависит от вашей задачи:

← На главную