При автоматизации взаимодействия 1С 7.7 с веб-сервисами или при парсинге веб-страниц часто возникает необходимость программно извлечь данные из выпадающих списков (тег select). В 1С 7.7 нет встроенных средств для работы с HTML-документами, поэтому мы вынуждены использовать внешние COM-объекты, предоставляемые операционной системой. Рассмотрим подробнее несколько способов решения этой задачи, начиная от использования браузера Internet Explorer и заканчивая разбором HTML как XML-структуры.
Этот метод наиболее удобен, если вам нужно не просто прочитать HTML-код, а взаимодействовать со страницей в реальном времени (например, после авторизации или нажатия кнопок). Мы можем создать экземпляр браузера, загрузить страницу и обратиться к её объектной модели (DOM).
Проанализируем ситуацию, когда нам нужно получить все значения из списка с известным id. Для этого мы будем использовать коллекцию options, которая есть у каждого элемента select в объектной модели Internet Explorer.
Рассмотрим по шагам пример кода для получения данных:
// Создаем объект браузера
ИЕ = СоздатьОбъект("InternetExplorer.Application");
ИЕ.Visible = 0; // Окно браузера будет скрыто
ИЕ.Navigate("http://www.пример-сайта.рф");
// Ожидаем полную загрузку страницы
Пока ИЕ.ReadyState <> 4 Цикл
Состояние("Загрузка страницы...");
КонецЦикла;
// Получаем объект нужного нам селекта по его ID
Попытка
Селект = ИЕ.document.getElementById("ContentPlaceHolder1_DropDownList3");
КолвоВариантов = Селект.options.length;
Для Ном = 0 По КолвоВариантов - 1 Цикл
ТекВариант = Селект.options(Ном);
Значение = ТекВариант.value; // Значение атрибута value
Текст = ТекВариант.text; // Видимый текст в списке
Сообщить("Элемент №" + Ном + ": Значение = " + Значение + ", Текст = " + Текст);
КонецЦикла;
Исключение
Сообщить("Элемент на странице не найден!");
КонецПопытки;
Важный момент: Обратите внимание на различие свойств value и text. Первое обычно содержит технический идентификатор, который отправляется на сервер, а второе — то, что видит пользователь на экране.
Если у вас уже есть текст HTML-кода (например, вы получили его через WinHttp.WinHttpRequest.5.1 или прочитали из файла), запускать полноценный процесс iexplore.exe избыточно. В этом случае мы рекомендуем использовать объект htmlfile. Он работает гораздо быстрее и потребляет меньше ресурсов.
Выясним, как реализовать этот подход:
// Предположим, переменная ТекстHTML содержит ваш код со списком
HTMLДок = СоздатьОбъект("htmlfile");
HTMLДок.write(ТекстHTML);
// Ищем элемент
Селект = HTMLДок.getElementById("ContentPlaceHolder1_DropDownList3");
Если ПустоеЗначение(Селект) = 0 Тогда
Для и = 0 По Селект.options.length - 1 Цикл
Вариант = Селект.options(и);
Сообщить("Найдено: " + Вариант.text + " [" + Вариант.value + "]");
КонецЦикла;
КонецЕсли;
Иногда HTML-код страницы достаточно строгий (соответствует стандартам XHTML), что позволяет рассматривать его как обычный XML. В этом случае можно использовать объект Microsoft.XMLDOM. Этот метод требует рекурсивного обхода узлов, так как структура документа может быть вложенной.
Разберем пример рекурсивной функции для обхода узлов, предложенный участниками обсуждения:
Процедура ОбработатьУзел(Узел, Уровень=0)
КолвоУзлов = Узел.childNodes.length;
Для СчУзлов = 0 По КолвоУзлов-1 Цикл
ТекУзел = Узел.childNodes.item(СчУзлов);
// Нас интересуют только узлы типа "Элемент" (Type 1)
Если ТекУзел.nodeType <> 1 Тогда
Продолжить;
КонецЕсли;
// Если нашли тег option, выводим его данные
Если Врег(ТекУзел.tagName) = "OPTION" Тогда
Значение = ТекУзел.getAttribute("value");
Текст = ТекУзел.text;
Сообщить("Опция XML: " + Текст + " (val=" + Значение + ")");
КонецЕсли;
// Рекурсивно идем глубже
ОбработатьУзел(ТекУзел, Уровень+1);
КонецЦикла;
КонецПроцедуры
// Использование:
Иксемель = СоздатьОбъект("Microsoft.XMLDOM");
Иксемель.Load(ИмяФайла);
Корень = Иксемель.DocumentElement;
ОбработатьУзел(Корень);
Предупреждение: Данный способ может вызвать ошибку, если HTML-код страницы не является валидным XML (например, есть незакрытые теги <br> или <img>). Для "грязного" HTML лучше использовать способы с объектами Internet Explorer.
Проанализируем структуру select из примера автора. Там присутствует атрибут onchange="javascript:setTimeout('__doPostBack(...)', 0)". Это характерная черта сайтов на платформе ASP.NET. Если ваша задача — не просто прочитать список, но и выбрать в нем значение так, чтобы страница обновилась, обычного изменения selectedIndex будет недостаточно.
Чтобы имитировать выбор пользователя и заставить сайт обновить данные, выполните следующий скрипт после смены индекса:
// Выбираем второй элемент (индекс 1)
Селект = ИЕ.document.getElementById("ContentPlaceHolder1_DropDownList3");
Селект.selectedIndex = 1;
// Инициируем событие изменения, чтобы сработал JavaScript сайта
ИЕ.document.parentWindow.execScript("__doPostBack('ctl00$ContentPlaceHolder1$DropDownList3','')");
Если вам нужно очень быстро получить список всех вариантов из огромного HTML-файла, не строя дерево объектов DOM, можно воспользоваться объектом VBScript.RegExp. Посмотрим на пример шаблона:
RegExp = СоздатьОбъект("VBScript.RegExp");
RegExp.IgnoreCase = 1;
RegExp.Global = 1;
RegExp.Pattern = "<option.*?value=[""']?(.*?)[""']?>(.*?)</option>";
Matches = RegExp.Execute(ТекстHTML);
Для и = 0 По Matches.Count - 1 Цикл
Match = Matches.Item(и);
Значение = Match.SubMatches(0);
Текст = Match.SubMatches(1);
Сообщить("RegExp нашел: " + Текст + " = " + Значение);
КонецЦикла;
Подведем итог. Для надежной работы с элементами select в 1С 7.7 наиболее предпочтительным является использование COM-объекта Internet Explorer или htmlfile, так как они предоставляют готовую коллекцию options и корректно обрабатывают все нюансы HTML-разметки.