В этой статье мы подробно разберем, как эффективно парсить веб-сайты с использованием платформы 1С, особенно когда нужные нам данные не обнаруживаются прямым парсингом HTML-кода. Мы рассмотрим типичную ситуацию, когда содержимое страницы генерируется динамически с помощью JavaScript, и выясним, какие подходы следует применять в таких случаях. Мы пройдем путь от первоначальной попытки парсинга до продвинутых методов, таких как анализ сетевых запросов и использование безголовых браузеров.
Представим ситуацию: мы пытаемся получить определенные данные со страницы веб-сайта. Наш код в 1С выглядит примерно так, используя стандартные объекты для работы с HTML:
Если Ответ.КодСостояния = 200 Тогда
Построитель = Новый ПостроительDOM;
ЧтениеHTML = Новый ЧтениеHTML;
ЧтениеHTML.УстановитьСтроку(Ответ.ПолучитьТелоКакСтроку);
ДокументHTML = Построитель.Прочитать(ЧтениеHTML);
ЧтениеHTML.Закрыть;
СписокЭлементов = ДокументHTML.ПолучитьЭлементыПоИмени("td");
КонецЕсли;
Мы получаем ответ от сервера (Ответ.КодСостояния = 200, что означает успешную загрузку), используем ПостроительDOM для создания HTML-документа и пытаемся найти нужные нам элементы, например, все теги <td>. Однако, к нашему удивлению, СписокЭлементов оказывается пустым или не содержит ожидаемых данных, хотя при просмотре страницы в браузере эти данные явно присутствуют.
Почему такой подход может не работать? Основная причина заключается в динамической загрузке контента.
Современные веб-сайты активно используют JavaScript для создания интерактивного и динамического пользовательского опыта. Это означает, что при первом получении HTML-документа с помощью HTTPСоединение, он может содержать только базовую структуру страницы. Все "ценные" данные, которые мы видим в браузере, подгружаются позже с помощью скриптов, которые выполняются уже на стороне клиента, то есть в браузере пользователя.
Рассмотрим подробнее механизмы динамической загрузки:
ПостроительDOM, который работает только с исходным HTML, никогда не увидит эти данные, так как они были добавлены после того, как он завершил свою работу.<div id="root"></div>, а все остальное содержимое будет построено скриптами.Таким образом, когда мы получаем исходный HTML-код с помощью HTTPСоединение и HTTPЗапрос, мы видим лишь "скелет" страницы без динамически загруженного содержимого. Для работы с таким контентом нам потребуются более продвинутые подходы.
Наиболее эффективный и производительный способ получения данных с динамически генерируемых страниц — это анализ сетевых запросов, которые выполняет сам браузер, и последующее прямое взаимодействие с соответствующими API-сервисами. Этот подход позволяет нам "обойти" необходимость рендеринга страницы и получить данные непосредственно из источника.
Наш первый и критически важный шаг — понять, откуда браузер берет данные, которые мы не видим в исходном HTML. Для этого мы будем использовать инструменты разработчика нашего веб-браузера:
F12 (или через меню: "Дополнительные инструменты" -> "Инструменты разработчика").F5 или кнопка перезагрузки в браузере), чтобы записать все запросы с самого начала.GET, POST, PUT, DELETE и т.д. Чаще всего для получения данных используются GET или POST.https://api.example.com/data?param=value). Это ключевая информация.GET) или в теле запроса (для POST). Они могут включать фильтры, идентификаторы, ключи пагинации и т.п.Content-Type (тип данных в теле запроса), Authorization (токены авторизации, если требуется), например, для получения токена авторизации RuStore с подписью RSA, User-Agent (идентификатор клиента), Cookie (сессионные данные) и Referer (откуда пришел запрос).Наша цель: Найти тот конкретный запрос (или последовательность запросов), который возвращает нужные нам данные в удобном для обработки формате (JSON или XML). Обычно такие запросы имеют тип fetch, xhr или json. Для тестирования HTTP-сервисов (GET и POST) можно использовать специализированные обработки.
После того как мы идентифицировали нужный API-запрос и собрали всю необходимую информацию о нем (URL, метод, параметры, заголовки), наша задача — повторить его средствами 1С. Для этого мы будем использовать объекты HTTPСоединение и HTTPЗапрос, или воспользоваться готовым инструментом Post1С — аналогом Postman на платформе 1С.
Рассмотрим пример воспроизведения GET-запроса к API:
// Пример воспроизведения GET-запроса к API для получения данных
Процедура ПолучитьДанныеСАПИ()
// 1. Извлекаем информацию из анализа сетевого трафика
// Предположим, мы обнаружили запрос к: https://api.example.com/data?limit=10&page=1
ИмяСервера = "api.example.com"; // Доменное имя сервера
Ресурс = "/data"; // Путь к ресурсу на сервере
// 2. Создаем HTTP-соединение
// Истина в последнем параметре указывает на использование HTTPS
Соединение = Новый HTTPСоединение(ИмяСервера, , , , , , Истина);
// 3. Создаем HTTP-запрос
Запрос = Новый HTTPЗапрос(Ресурс);
// 4. Устанавливаем параметры запроса (Query Parameters)
// Эти параметры добавляются к URL после знака ?
Запрос.УстановитьПараметр("limit", "10");
Запрос.УстановитьПараметр("page", "1");
// Если параметров много, можно использовать Структуру:
// ПараметрыЗапроса = Новый Структура("limit,page", 10, 1);
// Запрос.УстановитьПараметры(ПараметрыЗапроса);
// 5. Устанавливаем необходимые заголовки запроса
// 'User-Agent' часто рекомендуется для имитации запроса от браузера
Запрос.УстановитьЗаголовок("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36");
// Если API требует авторизации (например, токен в заголовке Authorization)
// Запрос.УстановитьЗаголовок("Authorization", "Bearer Ваш_Секретный_Токен");
// Если нужны Cookies (из вкладки 'Network' -> 'Request Headers' -> 'Cookie')
// Запрос.УстановитьЗаголовок("Cookie", "session_id=abc; other_cookie=xyz");
// 6. Отправляем GET-запрос и получаем ответ
Попытка
Ответ = Соединение.Получить(Запрос);
Исключение
Сообщить("Ошибка при выполнении GET-запроса к " + ИмяСервера + Ресурс + ": " + ОписаниеОшибки());
Возврат;
КонецПопытки;
// 7. Обрабатываем полученный ответ
Если Ответ.КодСостояния = 200 Тогда // Успешный ответ
ТелоОтвета = Ответ.ПолучитьТелоКакСтроку();
// Предполагаем, что ответ от API в формате JSON
Попытка
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(ТелоОтвета);
Данные = JSON.Прочитать(ЧтениеJSON); // Парсим JSON в структуру 1С
ЧтениеJSON.Закрыть();
// Теперь переменная 'Данные' содержит структуру (Структура, Массив, Соответствие)
// которую мы можем обрабатывать.
// Например, если 'Данные' - это Массив:
Если ТипЗнч(Данные) = Тип("Массив") Тогда
Для Каждого ЭлементДанных Из Данные Цикл
Сообщить("Получен элемент ID: " + ЭлементДанных.ID + ", Имя: " + ЭлементДанных.Наименование);
// ... дальнейшая обработка данных, например, запись в справочник, документ ...
КонецЦикл;
ИначеЕсли ТипЗнч(Данные) = Тип("Структура") Тогда
Сообщить("Получена структура данных. Количество записей: " + Данные.КоличествоЗаписей);
// ... обработка структуры ...
КонецЕсли;
Исключение
Сообщить("Ошибка при парсинге JSON-ответа: " + ОписаниеОшибки());
КонецПопытки;
Иначе // Ошибка HTTP-запроса
Сообщить("GET-запрос завершился с ошибкой. Код состояния: " + Ответ.КодСостояния);
Сообщить("Тело ответа (возможно, с описанием ошибки): " + Ответ.ПолучитьТелоКакСтроку());
КонецЕсли;
КонецПроцедуры
Для POST-запросов, кроме установки параметров в URL (что делается для GET), нам также необходимо передать тело запроса. Тело запроса может быть в различных форматах, чаще всего это JSON или application/x-www-form-urlencoded.
// Пример воспроизведения POST-запроса к API для отправки данных
Процедура ОтправитьДанныеНаАПИ()
// 1. Идентификация API-запроса (URL, метод POST, тело запроса, заголовки)
// Предположим, мы отправляем данные для создания новой записи
ИмяСервера = "api.example.com";
Ресурс = "/create_record";
// 2. Создаем HTTP-соединение
Соединение = Новый HTTPСоединение(ИмяСервера, , , , , , Истина);
// 3. Создаем HTTP-запрос, явно указываем метод POST
Запрос = Новый HTTPЗапрос(Ресурс);
Запрос.Метод = "POST";
// 4. Устанавливаем необходимые заголовки для POST-запроса
// 'Content-Type' очень важен для POST, он сообщает серверу, в каком формате тело запроса
Запрос.УстановитьЗаголовок("Content-Type", "application/json"); // Или "application/x-www-form-urlencoded"
Запрос.УстановитьЗаголовок("User-Agent", "1C Client");
// Другие заголовки, такие как Authorization, Cookie, если требуются
// 5. Формируем тело запроса (например, JSON)
СтруктураДанных = Новый Структура;
СтруктураДанных.Вставить("наименование", "Новая запись из 1С");
СтруктураДанных.Вставить("количество", 100);
СтруктураДанных.Вставить("активен", Истина);
// Преобразуем структуру 1С в строку JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
JSON.Записать(ЗаписьJSON, СтруктураДанных);
ТелоЗапросаJSON = ЗаписьJSON.Закрыть();
// 6. Устанавливаем тело запроса
Запрос.УстановитьТелоИзСтроки(ТелоЗапросаJSON, КодировкаТекста.UTF8); // Важно указать правильную кодировку
// 7. Отправляем POST-запрос
Попытка
Ответ = Соединение.Послать(Запрос); // Для POST-запросов используем метод Послать()
Исключение
Сообщить("Ошибка при выполнении POST-запроса к " + ИмяСервера + Ресурс + ": " + ОписаниеОшибки());
Возврат;
КонецПопытки;
// 8. Обрабатываем ответ
Если Ответ.КодСостояния = 200 Или Ответ.КодСостояния = 201 Тогда // 201 Created для успешного создания
Сообщить("Данные успешно отправлены. Ответ сервера: " + Ответ.ПолучитьТелоКакСтроку());
// Дополнительная обработка ответа, если сервер вернул ID новой записи и т.п.
Иначе
Сообщить("Ошибка отправки данных. Код состояния: " + Ответ.КодСостояния);
Сообщить("Тело ответа (возможно, с описанием ошибки): " + Ответ.ПолучитьТелоКакСтроку());
КонецЕсли;
КонецПроцедуры
Если сайт активно использует JavaScript не только для загрузки данных, но и для сложной логики взаимодействия (например, данные появляются после клика по кнопке, прокрутки страницы, выполнения сложных расчетов на клиенте), или если API недоступен, не задокументирован, или его крайне сложно воспроизвести напрямую, тогда нам потребуется эмулировать полноценный браузер. В этом случае на помощь приходят безголовые браузеры.
Это полноценный веб-браузер (например, Google Chrome или Mozilla Firefox), который работает без графического интерфейса пользователя. Несмотря на отсутствие визуального окна, он полностью функционален: может выполнять JavaScript, рендерить страницы, имитировать действия пользователя (клики, ввод текста, скроллинг) и получать HTML-код уже после того, как JavaScript выполнился и полностью загрузил все данные на странице.
Самые популярные инструменты для работы с безголовыми браузерами включают Selenium, Puppeteer (для Chromium-браузеров) и Playwright (для Chromium, Firefox, WebKit). Эти библиотеки написаны на таких языках, как Python, Node.js, Java, C#. Прямой интеграции этих библиотек с 1С нет.
Как правило, интеграция 1С с безголовым браузером осуществляется через следующие механизмы:
Selenium, Puppeteer или Playwright для открытия страницы, выполнения всех необходимых действий (например, ожидания загрузки, нажатия на кнопки) и получения результирующего HTML-кода или извлеченных данных. Затем этот скрипт запускается из 1С с помощью функций ЗапуститьПриложение() или объекта WScript.Shell, а результаты передаются обратно в 1С через временный файл, стандартный вывод (stdout) или через несложный протокол обмена данными.Представим, что у нас есть Python-скрипт (например, parser.py), который использует библиотеку Selenium для открытия страницы и ожидания полной загрузки данных. Этот скрипт будет выводить рендеренный HTML в стандартный вывод.
// Пример Python-скрипта (parser.py)
// Для запуска необходима установка: pip install selenium webdriver_manager beautifulsoup4
// И соответствующий WebDriver для вашего браузера (например, chromedriver для Chrome)
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
import time
import sys
import json
def parse_page(url_to_parse):
chrome_options = Options()
chrome_options.add_argument("--headless") # Запуск в безголовом режиме
chrome_options.add_argument("--disable-gpu")
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--window-size=1920,1080") # Установка размера окна для корректного рендеринга
chrome_options.add_argument("--disable-dev-shm-usage") # Для Docker окружений
# Установка сервиса Chrome Driver
# ChromeDriverManager автоматически скачивает и управляет версией chromedriver
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service, options=chrome_options)
try:
driver.get(url_to_parse)
# Ожидаем, пока JavaScript отработает и загрузит данные
# Здесь можно указать ожидание появления конкретного элемента
# или просто фиксированную паузу, если нет надежного индикатора
try:
# Пример: ждем, пока элемент с ID "main-content-loaded" станет видимым
WebDriverWait(driver, 20).until(
EC.presence_of_element_located((By.ID, "main-content-loaded"))
)
print("Элемент 'main-content-loaded' найден, страница загружена.")
except Exception:
print("Элемент не найден или таймаут ожидания. Продолжаем с текущим состоянием страницы.")
time.sleep(5) # Если нет конкретного элемента, ждем фиксированное время
# Получаем HTML-код после рендеринга и выполнения JavaScript
rendered_html = driver.page_source
# Здесь можно выполнить дополнительный парсинг уже внутри Python
# Например, с использованием BeautifulSoup4 (pip install beautifulsoup4)
# from bs4 import BeautifulSoup
# soup = BeautifulSoup(rendered_html, 'html.parser')
# # Пример: найти все элементы <div> с классом "data-item"
# data_items = soup.find_all('div', class_='data-item')
# extracted_data = []
# for item in data_items:
# # Извлекаем текст или атрибуты из найденных элементов
# extracted_data.append(item.get_text(strip=True))
# print(json.dumps(extracted_data, ensure_ascii=False)) # Выводим в JSON формате
print(rendered_html) # Выводим весь рендеренный HTML в стандартный вывод
except Exception as e:
print(f"Ошибка при парсинге URL {url_to_parse}: {e}", file=sys.stderr)
sys.exit(1) # Выход с ошибкой
finally:
driver.quit() # Важно закрыть браузер
if __name__ == "__main__":
if len(sys.argv) > 1:
parse_page(sys.argv[1])
else:
print("Usage: python parser.py <url_to_parse>", file=sys.stderr)
sys.exit(1)
Затем из 1С мы можем вызвать этот скрипт и получить его стандартный вывод (stdout), который будет содержать рендеренный HTML или извлеченные данные.
// Пример вызова Python-скрипта из 1С
// Предполагает, что Python установлен и доступен в PATH,
// а также установлены необходимые библиотеки (Selenium, webdriver_manager)
Процедура ПолучитьДанныеЧерезHeadlessБраузер()
ИмяФайлаСкрипта = "C:\МоиСкрипты\parser.py"; // Укажите актуальный путь к вашему скрипту
URLДляПарсинга = "https://www.пример-сайта-с-динамикой.com/data";
// 1. Формируем команду для запуска Python-скрипта
// Убедитесь, что Python и Selenium установлены и доступны в PATH на машине, где будет выполняться 1С
// Команда будет выглядеть примерно так: python "C:\МоиСкрипты\parser.py" "https://www.пример-сайта-с-динамикой.com/data"
Команда = "python """ + ИмяФайлаСкрипта + """ """ + URLДляПарсинга + """";
// 2. Создаем объект Shell для запуска внешних команд
Попытка
WShell = Новый COMОбъект("WScript.Shell");
Исключение
Сообщить("Ошибка инициализации WScript.Shell. Проверьте настройки безопасности ОС.");
Возврат;
КонецПопытки;
// 3. Запускаем скрипт и перенаправляем его вывод в файл
// Для получения стандартного вывода (stdout) из WScript.Shell.Run
// необходимо использовать метод Exec, а не Run, или перенаправлять вывод в файл.
// Рассмотрим вариант с перенаправлением вывода в файл для простоты.
ИмяВременногоФайлаВывода = КаталогВременныхФайлов() + "headless_output_" + Строка(Новый УникальныйИдентификатор()) + ".txt";
ИмяВременногоФайлаОшибок = КаталогВременныхФайлов() + "headless_errors_" + Строка(Новый УникальныйИдентификатор()) + ".txt";
// Добавляем перенаправление вывода скрипта в файл
// 2> для stderr, 1> для stdout
КомандаДляЗапуска = Команда + " 1>""" + ИмяВременногоФайлаВывода + """ 2>""" + ИмяВременногоФайлаОшибок + """";
Сообщить("Запуск команды: " + КомандаДляЗапуска);
// Запускаем скрипт, ждем его завершения (второй параметр - WaitOnReturn = Истина)
// Третий параметр (WindowStyle) - 0 скрывает окно консоли
КодВозврата = WShell.Run(КомандаДляЗапуска, 0, Истина);
// 4. Обрабатываем результаты
Если КодВозврата = 0 Тогда
Сообщить("Скрипт Python выполнен успешно.");
// Читаем содержимое файла с результатом
Если ФайлСуществует(ИмяВременногоФайлаВывода) Тогда
ТекстРезультата = Новый ТекстовыйДокумент;
ТекстРезультата.Прочитать(ИмяВременногоФайлаВывода);
СтрокаРезультата = ТекстРезультата.ПолучитьТекст();
Сообщить("Полученные данные (часть): " + Лев(СтрокаРезультата, 500) + "..."); // Выводим часть для примера
// Если скрипт возвращает JSON:
Попытка
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(СтрокаРезультата);
ДанныеИзСкрипта = JSON.Прочитать(ЧтениеJSON);
ЧтениеJSON.Закрыть();
Сообщить("Данные успешно распарсены из JSON.");
// ... дальнейшая обработка 'ДанныеИзСкрипта' ...
Исключение
Сообщить("Ошибка парсинга JSON из вывода скрипта: " + ОписаниеОшибки());
Сообщить("Полный вывод скрипта: " + СтрокаРезультата);
КонецПопытки;
УдалитьФайлы(ИмяВременногоФайлаВывода);
Иначе
Сообщить("Файл с результатами скрипта не найден.");
КонецЕсли;
// Проверяем файл ошибок, если он не пустой
Если ФайлСуществует(ИмяВременногоФайлаОшибок) Тогда
ТекстОшибок = Новый ТекстовыйДокумент;
ТекстОшибок.Прочитать(ИмяВременногоФайлаОшибок);
СтрокаОшибок = ТекстОшибок.ПолучитьТекст();
Если Не ПустаяСтрока(СтрокаОшибок) Тогда
Сообщить("Ошибки скрипта Python: " + СтрокаОшибок);
КонецЕсли;
УдалитьФайлы(ИмяВременногоФайлаОшибок);
КонецЕсли;
Иначе
Сообщить("Ошибка выполнения скрипта Python. Код возврата: " + КодВозврата);
Если ФайлСуществует(ИмяВременногоФайлаОшибок) Тогда
ТекстОшибок = Новый ТекстовыйДокумент;
ТекстОшибок.Прочитать(ИмяВременногоФайлаОшибок);
Сообщить("Сообщение об ошибке из скрипта: " + ТекстОшибок.ПолучитьТекст());
УдалитьФайлы(ИмяВременногоФайлаОшибок);
КонецЕсли;
УдалитьФайлы(ИмяВременногоФайЛАВЫВОДА);
КонецЕсли;
КонецПроцедуры
Независимо от выбранного подхода, существуют общие рекомендации, которые помогут вам в процессе парсинга:
Всегда начинайте с тщательного изучения страницы в браузере. Используйте вкладку "Элементы" (Elements) в инструментах разработчика. После полной загрузки страницы, когда данные уже отображаются, вы можете использовать инструмент "Выбрать элемент на странице" (обычно иконка курсора со стрелкой) для "инспектирования" элемента, содержащего нужные вам данные.
Это позволит вам точно определить имена тегов, классы (class) и идентификаторы (id) элементов. Например, если вы видите <div class="product-name">Название товара</div>, то product-name — это класс, по которому можно искать. Эти селекторы затем могут быть использованы в вашем парсере (например, в Python-скрипте с BeautifulSoup) или как ориентир для поиска в ответе API, если он возвращает HTML-фрагменты.
В некоторых случаях данные появляются с небольшой задержкой, даже если они не подгружаются через отдельные AJAX-запросы, а просто рендерятся чуть позже. При использовании безголового браузера, вы можете использовать явные ожидания (например, WebDriverWait в Selenium), чтобы дождаться появления конкретного элемента на странице, или просто добавить небольшую фиксированную паузу (time.sleep()), чтобы дать JavaScript время для выполнения.
Если вы все же получаете HTML-код, но он сложный, плохо сформирован или требует более сложной навигации, рассмотрите использование специализированных библиотек для парсинга. В Python это BeautifulSoup или lxml, которые предоставляют мощные механизмы для поиска элементов по CSS-селекторам или XPath. Это намного удобнее и надежнее, чем базовые возможности ПостроительDOM в 1С, который может быть ограничен в работе с невалидным или очень сложным HTML.
Мы выяснили, что прямой парсинг исходного HTML-кода с использованием ПостроительDOM в 1С часто не работает для современных сайтов, которые динамически загружают контент с помощью JavaScript. Наиболее предпочтительный и производительный подход — это анализ сетевых запросов и прямое обращение к API. Мы должны научиться "подслушивать" общение браузера с сервером и воспроизводить эти запросы средствами 1С.
Если же прямое взаимодействие с API невозможно или крайне затруднительно из-за сложной клиентской логики, тогда безголовый браузер является мощным, но более ресурсоемким и сложным в интеграции решением. Он позволяет эмулировать полноценную работу браузера, включая выполнение JavaScript.
Всегда начинайте с изучения вкладки "Сеть" и "Элементы" в инструментах разработчика вашего браузера — это ваш главный помощник в решении подобных задач. Понимание того, как работает сайт, — это половина успеха в его парсинге.