Как найти узел HTML по CSS-селектору или XPath в 1С:Предприятии 8.3

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

При интеграции 1С с внешними веб-сервисами, парсинге сайтов (например, государственные реестры, каталоги товаров или курсы валют) разработчики часто сталкиваются с необходимостью программно разобрать HTML-код страницы. В отличие от современных веб-технологий, где поиск по CSS-селекторам является стандартом де-факто, встроенные механизмы платформы 1С:Предприятие накладывают определенные ограничения. Особенности работы зависят от того, где выполняется код — на клиенте или на сервере, а также под какой операционной системой (Windows или Linux) запущен сервер 1С.

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

Вариант 1. Поиск на клиенте через интерактивное поле HTML-документа

Если задача выполняется в интерактивном режиме на клиенте (в форме обработки или управляемого приложения), мы имеем полный доступ к стандартному объектному движку рендеринга. Поле HTML-документа на форме инициализирует полноценный браузерный контекст, в котором доступны все привычные методы JS.

Разберем пример, когда нам нужно найти первый же элемент ввода input на форме документа. Для этого мы можем напрямую обратиться к свойству Документ элемента управления:


&НаКлиенте
Процедура НайтиЭлементНаФорме()
    // Получаем DOM-документ из элемента формы
    ДокументHTML = Элементы.ТвоеПолеHTMLДокумента.Документ;
    
    // Используем стандартный JS-метод querySelector для поиска по CSS-селектору
    ПервыйВвод = ДокументHTML.querySelector("input");
    
    Если ПервыйВвод <> Неопределено Тогда
        Сообщить("Найден элемент с типом: " + ПервыйВвод.type);
        // Можем получить значение или атрибуты
        ЗначениеПоля = ПервыйВвод.value;
    КонецЕсли;
КонецПроцедуры

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

Вариант 2. Использование COM-объекта на сервере под управлением Windows

Когда парсинг необходимо выполнить на стороне сервера, но при этом сервер работает под управлением операционной системы Windows, мы можем прибегнуть к использованию системных COM-объектов. Наиболее популярным решением является создание объекта HtmlFile, который под капотом использует библиотеки Internet Explorer.

Рассмотрим пример загрузки HTML-кода из файла и поиска ссылки по CSS-селектору:


&НаСервере
Функция ПолучитьСсылкуИзФайлаHTML(ПолноеИмяФайла, СтрокаВыражения)
    
    // Считываем сырой HTML-код из файла
    ТекстовыйДокумент = Новый ТекстовыйДокумент;
    ТекстовыйДокумент.Прочитать(ПолноеИмяФайла, КодировкаТекста.UTF8);
    ТекстHTML = ТекстовыйДокумент.ПолучитьТекст();
    
    // Создаем COM-объект HtmlFile
    ДокументHTML = Новый COMОбъект("HtmlFile");
    ДокументHTML.open("text/html");
    ДокументHTML.write(ТекстHTML);
    ДокументHTML.close(); // Обязательно закрываем поток записи
    
    Попытка
        // Находим элемент по селектору (например, "a.download-link")
        НайденныйЭлемент = ДокументHTML.querySelector(СтрокаВыражения);
        Если НайденныйЭлемент <> Неопределено Тогда
            Возврат НайденныйЭлемент.href;
        КонецЕсли;
    Исключение
        ЗаписьЖурналаРегистрации("Парсинг HTML", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
        Возврат "";
    КонецПопытки;
    
    Возврат "";
КонецФункции

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

  1. Полноценная поддержка CSS-селекторов любой сложности.
  2. Корректно обрабатывает невалидный HTML (автоматически исправляет ошибки разметки, закрывает теги).

Недостатки метода:

Вариант 3. Кроссплатформенный XPath через конвертацию в ДокументDOM

Для создания универсального решения, которое будет стабильно работать и на Windows, и на Linux, нам необходимо использовать исключительно встроенные объекты платформы 1С. Платформа предоставляет класс ПостроительDOM, который умеет читать HTML и XML.

Однако есть архитектурный нюанс: объект ДокументHTML, получаемый после чтения через ЧтениеHTML, не поддерживает язык запросов XPath и не имеет методов querySelector. В то же время объект ДокументDOM имеет полноценный метод ВычислитьВыражениеXPath(), но инициализировать его напрямую из «грязного» веб-HTML зачастую не получается из-за строгих требований XML-стандарта.

Рассмотрим обходной путь решения этой проблемы. Мы загружаем HTML-документ, записываем его тело в XML-формате, а затем считываем этот поток как полноценный ДокументDOM. Разберем этот алгоритм по шагам:


&НаСервере
Функция НайтиУзлыЧерезXPath(ПутьКФайлу, ВыражениеXPath) Экспорт
    
    // Step 1: Читаем исходный файл как HTML
    ЧтениеHTML = Новый ЧтениеHTML;
    ЧтениеHTML.ОткрытьФайл(ПутьКФайлу, "UTF-8");
    
    ПостроительDOM = Новый ПостроительDOM;
    ДокументHTML = ПостроительDOM.Прочитать(ЧтениеHTML);
    ЧтениеHTML.Закрыть();
    
    // Step 2: Конвертируем HTML-структуру в строго валидный XML-формат
    ЗаписьXML = Новый ЗаписьXML;
    ЗаписьXML.УстановитьСтроку("UTF-8");
    
    ЗаписьDOM = Новый ЗаписьDOM;
    // Записываем только тело документа (Body), чтобы избежать проблем с заголовками мета-данных
    ЗаписьDOM.Записать(ДокументHTML.Тело, ЗаписьXML);
    
    СтрокаXML = ЗаписьXML.Закрыть();
    
    // Step 3: Читаем полученную XML строку в ДокументDOM
    ЧтениеXML = Новый ЧтениеXML;
    ЧтениеXML.УстановитьСтроку(СтрокаXML);
    
    ДокументDOM = ПостроительDOM.Прочитать(ЧтениеXML);
    ЧтениеXML.Закрыть();
    
    // Step 4: Выполняем XPath запрос к DOM-модели
    Разыменователь = Новый РазыменовательПространствИменDOM(ДокументDOM);
    РезультатXPath = ДокументDOM.ВычислитьВыражениеXPath(ВыражениеXPath, ДокументDOM, Разыменователь);
    
    МассивРезультатов = Новый Массив;
    ТекущийЭлемент = РезультатXPath.ПолучитьСледующий();
    Пока ТекущийЭлемент <> Неопределено Цикл
        МассивРезультатов.Добавить(ТекущийЭлемент);
        ТекущийЭлемент = РезультатXPath.ПолучитьСледующий();
    КонецЦикла;
    
    Возврат МассивРезультатов;
    
КонецФункции

Проблема «Недопустимое имя XML» и пути ее решения

При использовании описанного выше метода на реальных сайтах (например, при парсинге реестров на сайте ФНС) вы можете столкнуться с ошибкой: Недопустимое имя XML во время выполнения метода ЗаписьDOM.Записать().

Причина кроется в том, что современный стандарт разметки HTML5 допускает вольности, которые абсолютно недопустимы в строгом XML (XHTML):

Для борьбы с этой проблемой мы рекомендуем перед передачей HTML-текста парсеру выполнять его предварительную очистку с помощью функций работы со строками или регулярных выражений. В частности, удаление секций скриптов и стилей часто полностью решает проблему валидации:


// Пример простой очистки HTML перед парсингом
Функция ОчиститьHTMLОтСкриптов(ИсходныйТекст)
    // Удаляем все блоки <script>...</script>
    Результат = ИсходныйТекст;
    Пока Истина Цикл
        ПозНачала = Найти(НРег(Результат), "<script");
        Если ПозНачала = 0 Тогда Прервать; КонецЕсли;
        ПозКонца = Найти(НРег(Результат), "</script>");
        Если ПозКонца = 0 Тогда Прервать; КонецЕсли;
        
        Результат = Лев(Результат, ПозНачала - 1) + Сред(Результат, ПозКонца + 9);
    КонецЦикла;
    Возврат Результат;
КонецФункции

Вариант 4. Обход дерева DOM средствами 1С (Безопасный кроссплатформенный парсинг)

Если нам необходимо гарантированно избежать ошибок валидации XML на сервере под Linux и при этом не хочется использовать регулярные выражения, мы можем реализовать ручной обход дерева элементов HTML с помощью объекта ОбходДереваDOM. Этот метод работает медленнее, чем нативный XPath, но он полностью безопасен и не зависит от валидности XML-структуры.

Проанализируем пример функции, которая обходит все узлы документа и ищет элементы по заданному имени тега и классу CSS:


&НаСервере
Функция НайтиУзлыПоТегуИКлассу(ПутьКФайлу, ИскомыйТег, ИскомыйКласс) Экспорт
    
    ЧтениеHTML = Новый ЧтениеHTML;
    ЧтениеHTML.ОткрытьФайл(ПутьКФайлу, "UTF-8");
    
    ПостроительDOM = Новый ПостроительDOM;
    ДокументHTML = ПостроительDOM.Прочитать(ЧтениеHTML);
    ЧтениеHTML.Закрыть();
    
    СписокНайденныхУзлов = Новый Массив;
    
    // Создаем стандартный объект обхода дерева DOM 1С
    Фильтр = Новый ОпределенныйКлассФильтраDOM(); // Если требуется кастомная фильтрация
    Обходчик = ДокументHTML.СоздатьОбходДерева(ДокументHTML.Тело, ПолучитьПоказательПолнотыОбхода(), Фильтр);
    
    ТекущийУзел = Обходчик.СледующийУзел();
    Пока ТекущийУзел <> Неопределено Цикл
        
        // Проверяем, является ли узел элементом HTML
        Если ТекущийУзел.ТипУзла = ТипУзлаDOM.Элемент Тогда
            
            // Сверяем имя тега (приводим к нижнему регистру для надежности)
            Если НРег(ТекущийУзел.ИмяУзла) = НРег(ИскомыйТег) Тогда
                
                // Проверяем наличие нужного класса в атрибутах
                АтрибутClass = ТекущийУзел.ПолучитьАтрибут("class");
                Если АтрибутClass <> Неопределено И Найти(АтрибутClass, ИскомыйКласс) > 0 Тогда
                    СписокНайденныхУзлов.Добавить(ТекущийУзел);
                КонецЕсли;
                
            КонецЕсли;
            
        КонецЕсли;
        
        ТекущийУзел = Обходчик.СледующийУзел();
    КонецЦикла;
    
    Возврат СписокНайденныхУзлов;
    
КонецФункции

&НаСервере
Функция ПолучитьПоказательПолнотыОбхода()
    // Возвращаем битовую маску для обхода только элементов (Element Nodes)
    Возврат 1; 
КонецФункции

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

Вариант 5. Использование внешней утилиты HTML Tidy (Best Practice для продакшена)

Для крупных проектов, где критически важна скорость парсинга больших объемов данных, а также стабильность работы под Linux, отличным решением будет предварительная обработка скачанной страницы через консольную утилиту HTML Tidy.

Эта утилита доступна для установки во всех дистрибутивах Linux (через apt-get install tidy) и на Windows. Она за доли секунды преобразует любой «битый» HTML в идеальный, валидный XHTML, который без единой ошибки прочитается построителем DOM 1С.

Давайте разберем, как вызвать эту утилиту из кода 1С:


&НаСервере
Функция ОчиститьHTMLЧерезTidy(ПутьКФайлуИсточнику, ПутьКВыходномуФайлу)
    
    // Формируем параметры запуска для tidy, заставляя утилиту преобразовать код в XHTML
    ПараметрыЗапуска = " -asxhtml --numeric-entities yes --output-xhtml yes --quiet yes -o """ 
                       + ПутьКВыходномуФайлу + """ """ + ПутьКФайлуИсточнику + """";
    
    // Запускаем консольное приложение в зависимости от ОС сервера
    СистемнаяИнформация = Новый СистемнаяИнформация;
    Если Найти(ВРег(СистемнаяИнформация.ОперационнаяСистема), "LINUX") > 0 Тогда
        ИмяПриложения = "tidy";
    Иначе
        ИмяПриложения = "tidy.exe";
    КонецЕсли;
    
    КодВозврата = Неопределено;
    ЗапуститьПриложение(ИмяПриложения + ПараметрыЗапуска, , Истина, КодВозврата);
    
    // Код возврата 0 или 1 означает успешное исправление разметки
    Возврат (КодВозврата = 0 Или КодВозврата = 1);
    
КонецФункции

После обработки исходного файла этой функцией вы можете гарантированно использовать быстрый кроссплатформенный парсинг через XPath (описанный в Варианте 3) без малейшего риска падения системы по ошибкам несовместимости XML.

Резюме и рекомендации

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

← На главную