Работа с HTML в 1С 8.3: загрузка, разбор и вывод на форму
Содержание
HTML в 1С попадает тремя путями: лежит готовым файлом на диске, приходит ответом от стороннего сайта или сервиса по HTTP, либо хранится строкой в реквизите. Загрузить его означает одно из двух — либо прочитать как текст, чтобы разобрать и вытащить данные, либо отрисовать, чтобы человек увидел страницу внутри формы.
От задачи зависит объект платформы. Для текста хватит ТекстовыйДокумент или ЧтениеТекста. Чтобы сохранить разметку (жирный, таблицы, ссылки) — ФорматированныйДокумент с методом УстановитьHTML. Чтобы вытащить значения конкретных тегов — построитель DOM. Чтобы показать на экране — поле HTML-документа на управляемой форме. Ниже — рабочий код встроенного языка 8.3 под каждый сценарий.
ТекстовыйДокумент или ЧтениеТекста — одна строка кода, сразу получаете содержимое файла.ФорматированныйДокумент.УстановитьHTML() — перенос в поле форматированного документа с жирным, списками, таблицами.ПостроительDOM + ЧтениеHTML — строите дерево и обходите его как объекты.Чтение HTML-файла в текстовую переменную
Самый частый случай — файл уже на диске, нужно его содержимое в строке. Два equivalent способа.
Через ТекстовыйДокумент — удобно, когда файл небольшой и нужен весь сразу:
ТекстДокумент = Новый ТекстовыйДокумент;
ТекстДокумент.Прочитать("C:\Обмен\page.html", КодировкаТекста.UTF8);
HTMLСтрока = ТекстДокумент.ПолучитьТекст();
Через ЧтениеТекста — то же самое чуть короче:
Чтение = Новый ЧтениеТекста("C:\Обмен\page.html", КодировкаТекста.UTF8);
HTMLСтрока = Чтение.Прочитать();
Чтение.Закрыть();
Второй параметр — кодировка. Если его не указать, платформа попытается определить её сама, но на старых страницах в windows-1251 автоопределение иногда промахивается и текст превращается в «кракозябры». Про это отдельный раздел ниже.
На веб-клиенте прямого доступа к файловой системе сервера нет. Там файл сначала помещают во временное хранилище (ПоместитьФайл), а уже потом читают по полученному адресу. В толстом и тонком клиенте с файлом на локальном диске работает код выше.
Если файл лежит в двоичных данных (реквизит, присоединённый файл), его сначала выгружают во временный файл методом ПолучитьФайл/Записать, а затем читают тем же ЧтениеТекста.
Перевод HTML в форматированный документ
Когда разметку надо сохранить — показать пользователю оформленный текст, дать редактировать или вывести на печать — подходит объект ФорматированныйДокумент. Его метод УстановитьHTML принимает строку с разметкой и разбирает её в структуру с абзацами, таблицами, ссылками и начертанием.
ФД = Новый ФорматированныйДокумент;
Картинки = Новый Соответствие;
ФД.УстановитьHTML(HTMLСтрока, Картинки);
Второй параметр Картинки — соответствие, где ключ это имя картинки из тега <img>, а значение — объект Картинка. Если изображений нет или они не нужны, передаётся пустое соответствие, как в примере.
Обратная операция — выгрузить содержимое поля обратно в HTML:
HTMLРезультат = "";
Картинки = Новый Соответствие;
ФД.ПолучитьHTML(HTMLРезультат, Картинки);
Здесь HTMLРезультат и Картинки — выходные параметры: после вызова в первом будет разметка, во втором — соответствие извлечённых картинок.
ФорматированныйДокумент понимает ограниченное подмножество HTML и CSS. Сложная вёрстка с flex, внешними стилями и скриптами упростится: часть оформления потеряется, скрипты отбросятся. Для точной отрисовки сложной страницы берите поле HTML-документа (последний раздел) — там работает полноценный движок.
Чтобы форматированный документ увидел пользователь, на управляемой форме заводят реквизит типа ФорматированныйДокумент и размещают его как элемент. Присвоение ФД этому реквизиту сразу отрисует содержимое. Такой подход удобен для писем, комментариев и описаний, где нужен минимальный набор форматирования без полноценной вёрстки.
Форматированный документ можно и напечатать: метод ПолучитьТекстовыйДокумент переводит его в обычный текст, а для оформленной печати содержимое переносят в макет табличного документа.
Получение HTML-страницы по HTTP и HTTPS
Если разметка не на диске, а на внешнем сайте или в сервисе, её забирают GET-запросом. Базовый объект — HTTPСоединение.
Соединение = Новый HTTPСоединение(
"example.com", // сервер без протокола
443, // порт: 80 для http, 443 для https
, // пользователь
, // пароль
, // прокси
15, // таймаут, сек
Новый ЗащищённоеСоединениеOpenSSL); // для https
Запрос = Новый HTTPЗапрос("/catalog/page.html");
Ответ = Соединение.Получить(Запрос);
Если Ответ.КодСостояния = 200 Тогда
HTMLСтрока = Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
Иначе
Сообщить("Сервер вернул код " + Ответ.КодСостояния);
КонецЕсли;
Разберём ключевые моменты:
- Для
httpsобязателен седьмой параметрНовый ЗащищённоеСоединениеOpenSSL. Без него соединение по 443 порту упадёт с ошибкой, а начинающие часто ищут проблему в адресе, хотя дело в отсутствии защищённого соединения. - Адрес сервера передаётся без
http://иhttps://— только домен. Протокол задаётся портом и признаком защищённого соединения. - Путь страницы (
/catalog/page.html) идёт вHTTPЗапрос, а не в адрес сервера. КодСостояния = 200стоит проверять всегда: при404или500тело запроса окажется не той страницей, которую вы ждёте, а сообщением об ошибке.
Часть сайтов отвечает 403 Доступ запрещён, если запрос пришёл без заголовка User-Agent — так отсекают простые роботы. Заголовки задаются до вызова Получить:
Запрос = Новый HTTPЗапрос("/catalog/page.html");
Запрос.Заголовки.Вставить("User-Agent", "Mozilla/5.0");
Ответ = Соединение.Получить(Запрос);
Для разовой загрузки файла по ссылке есть готовая функция ПолучитьФайлИзИнтернета из библиотеки стандартных подсистем (БСП). Она сама разбирает адрес, протокол и прокси. Но в типовых конфигурациях её вызывают через обёртки БСП, а не напрямую, поэтому в универсальном коде надёжнее HTTPСоединение.
Если сервер отдаёт данные через POST (форма авторизации, API), в HTTPЗапрос заполняют тело методом УстановитьТелоИзСтроки и нужные заголовки, а вместо Получить вызывают ОтправитьДляОбработки.
Разбор HTML и извлечение данных через DOM
Строка с разметкой сама по себе бесполезна, если нужны конкретные значения — цены из таблицы, ссылки из меню, текст из нужного блока. Разбирать её регулярными выражениями или поиском по подстроке хрупко: любая смена вёрстки ломает код. Надёжнее построить дерево DOM и обходить его как объекты.
Связка — ЧтениеHTML плюс ПостроительDOM:
ЧтениеHTML = Новый ЧтениеHTML;
ЧтениеHTML.УстановитьСтроку(HTMLСтрока);
Построитель = Новый ПостроительDOM;
ДокументDOM = Построитель.Прочитать(ЧтениеHTML);
Дальше получаем нужные теги по имени и читаем их атрибуты и текст:
Ссылки = ДокументDOM.ПолучитьЭлементыПоИмениТега("a");
Для Каждого Элемент Из Ссылки Цикл
Адрес = Элемент.ПолучитьАтрибут("href");
Надпись = Элемент.ТекстовоеСодержимое;
Сообщить(Надпись + " → " + Адрес);
КонецЦикла;
Что здесь важно:
ПолучитьЭлементыПоИмениТегавозвращает коллекцию всех тегов с указанным именем —"a","td","img","div".ПолучитьАтрибутдостаёт значение атрибута:hrefу ссылки,srcу картинки,classу блока.ТекстовоеСодержимоеотдаёт весь видимый текст внутри тега без разметки.
Для адресной выборки по идентификатору блока есть ПолучитьЭлементПоИдентификатору:
Блок = ДокументDOM.ПолучитьЭлементПоИдентификатору("price");
Если Блок <> Неопределено Тогда
Цена = Блок.ТекстовоеСодержимое;
КонецЕсли;
Проверяйте результат на Неопределено. Если тега с таким id на странице нет, ПолучитьЭлементПоИдентификатору вернёт Неопределено, и обращение к его свойствам вызовет ошибку. На «живых» сайтах вёрстка меняется — защита от пустого результата обязательна.
Частая задача — собрать данные из таблицы: прайс, список заказов, реестр. Таблица в HTML это тег <table>, строки — <tr>, ячейки — <td>. Обход идёт в два вложенных цикла:
Строки = ДокументDOM.ПолучитьЭлементыПоИмениТега("tr");
Для Каждого Строка Из Строки Цикл
Ячейки = Строка.ПолучитьЭлементыПоИмениТега("td");
Если Ячейки.Количество() >= 2 Тогда
Товар = Ячейки[0].ТекстовоеСодержимое;
Цена = Ячейки[1].ТекстовоеСодержимое;
Сообщить(Товар + ": " + Цена);
КонецЕсли;
КонецЦикла;
Проверка Количество() >= 2 страхует от строки-заголовка или от строк с объединёнными ячейками, где нужных столбцов нет. Обращение к ячейкам идёт по индексу с нуля: Ячейки[0] — первый столбец, Ячейки[1] — второй.
ЧтениеHTML лоялен к «грязной» разметке: незакрытые теги и ошибки вёрстки он чаще всего переварит, в отличие от строгого ЧтениеXML, который на некорректном HTML просто упадёт.
Кодировки и специальные символы
Половина проблем с загрузкой HTML — не в коде, а в кодировке. Если на экране вместо русских букв «Ð¿Ñивеѻ или «ïðèâåò», значит текст прочитали не в той кодировке, в которой он записан.
Правило простое: читать надо в той кодировке, в которой файл сохранён.
- Современные страницы — почти всегда
UTF-8:КодировкаТекста.UTF8. - Старые русские сайты и выгрузки из 1С 7.7 — часто
windows-1251: передавайте строку"windows-1251"вместо константы. - Кодировку страницы подсказывает тег в её «шапке»:
<meta charset="utf-8">или<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">.
Для HTTP-ответа кодировку указывают прямо в ПолучитьТелоКакСтроку:
// если сайт в 1251
HTMLСтрока = Ответ.ПолучитьТелоКакСтроку("windows-1251");
Отдельная тема — HTML-мнемоники (сущности). В разметке неразрывный пробел пишется как , амперсанд как &, кавычки как ". Когда вы разбираете строку через ЧтениеHTML и DOM, платформа раскрывает их автоматически — в ТекстовоеСодержимое придёт нормальный символ.
А вот если вы режете HTML по подстроке «вручную» через СтрНайти и Сред, мнемоники останутся как есть, и их придётся заменять самому:
Текст = СтрЗаменить(Текст, " ", " ");
Текст = СтрЗаменить(Текст, "&", "&");
Текст = СтрЗаменить(Текст, """, """");
Это ещё один довод за разбор через DOM, а не через поиск по строке: декодирование сущностей, вложенность тегов и регистр атрибутов построитель берёт на себя.
Показ HTML на управляемой форме
Чтобы человек увидел страницу внутри 1С — с картинками, ссылками и стилями — используют элемент формы «Поле HTML-документа». Он рисует разметку встроенным движком браузера, а не упрощает её, как форматированный документ.
Порядок действий:
- В конфигураторе на форме добавьте реквизит типа
Строканеограниченной длины, напримерHTMLТекст. - Перетащите реквизит на форму как элемент. В свойствах элемента задайте вид «Поле HTML-документа».
- В коде присвойте реквизиту строку с разметкой — поле тут же её отрисует:
&НаСервере
Процедура ЗагрузитьСтраницу()
Чтение = Новый ЧтениеТекста("C:\Обмен\page.html", КодировкаТекста.UTF8);
HTMLТекст = Чтение.Прочитать();
Чтение.Закрыть();
КонецПроцедуры
После того как поле отрисовало страницу, к её содержимому можно обратиться программно через свойство Документ элемента — это тот же DOM, что и при разборе построителем:
&НаКлиенте
Процедура ПослеЗагрузки()
ДокументСтраницы = Элементы.ПолеHTML.Документ;
// дальше читаем теги: ПолучитьЭлементыПоИмениТега и т.д.
КонецПроцедуры
Свойство Документ доступно только на клиенте и только после того, как поле закончило отрисовку. Поэтому обращаться к нему из процедуры загрузки сразу нельзя — дождитесь события, которое поле вызывает после формирования документа.
Поле HTML-документа умеет открывать и внешние адреса: достаточно присвоить реквизиту не разметку, а ссылку вида https://.... Так в 1С встраивают, например, онлайн-карты или личные кабинеты сервисов.
Чем `ТекстовыйДокумент` отличается от `ФорматированныйДокумент` при загрузке HTML?
ТекстовыйДокумент даёт сырую строку с тегами — он нужен для разбора и извлечения данных. ФорматированныйДокумент разбирает разметку и сохраняет оформление: его показывают пользователю и дают редактировать.Как достать из HTML значение одного конкретного поля, а не всё подряд?
ПостроительDOM, затем возьмите нужный узел по идентификатору методом ПолучитьЭлементПоИдентификатору или переберите теги ПолучитьЭлементыПоИмениТега. Текст узла — в свойстве ТекстовоеСодержимое.Почему запрос по `https` не проходит, а по `http` работает?
HTTPСоединение нужно передать Новый ЗащищённоеСоединениеOpenSSL. Без него платформа пытается открыть обычное соединение на 443 порту и получает ошибку.Можно ли разбирать HTML регулярными выражениями вместо DOM?
СтрНайти и СтрЗаменить, но это ненадёжно: любое изменение вёрстки ломает разбор, а HTML-сущности придётся раскрывать вручную. DOM устойчив к «грязной» разметке и декодирует сущности сам.Как показать пользователю готовую HTML-страницу внутри формы?
Строка и присвойте этому реквизиту разметку или адрес страницы. Поле отрисует её встроенным браузером.