В процессе интеграции 1С с внешними информационными системами разработчики часто сталкиваются с ситуацией, когда у целевого веб-ресурса полностью отсутствует программный интерфейс (API). При этом перед нами стоит задача автоматизировать рутинные действия пользователя: зайти на сайт, авторизоваться, заполнить несколько полей ввода, выбрать значения из выпадающих списков, прикрепить файл (например, печатную форму PDF) и отправить форму, обработав возможные всплывающие окна.
В этой статье мы подробно разберем три принципиально разных подхода к решению этой задачи, проанализируем их сильные и слабые стороны, а также приведем конкретные примеры реализации на встроенном языке 1С. Мы научимся имитировать действия пользователя как «невидимым» способом (на уровне сетевых пакетов), так и с помощью прямого управления браузером.
Выбор конкретного метода зависит от сложности структуры сайта, наличия защиты от роботов (капчи), использования современных реактивных JavaScript-фреймворков и требований к производительности системы. Рассмотрим каждый вариант во всех деталях.
---
Этот способ является наиболее производительным и «чистым» с точки зрения системного администрирования. Он не требует запуска графического интерфейса браузера, не зависит от установленного в системе ПО и может выполняться полностью в фоновом режиме на стороне сервера 1С. Суть метода заключается в том, что мы полностью воссоздаем сетевой трафик, который генерирует браузер при действиях пользователя.
Для реализации этого подхода нам понадобится предварительно выполнить детальный анализ веб-страницы. Разберем этот процесс по шагам.
Шаг 1. Анализ трафика и структуры запросов
Перед написанием кода в 1С необходимо выяснить, какие именно запросы уходят на сервер при ручном заполнении формы. Для этого откроем браузер Google Chrome (или любой другой современный браузер), перейдем на нужную страницу сайта и нажмем клавишу F12 (или сочетание клавиш Ctrl+Shift+I), чтобы открыть инструменты разработчика. Перейдем на вкладку «Сеть» (Network) и обязательно установим флажок «Preserve log» (Сохранять лог), чтобы история запросов не очищалась при переходах между страницами.
После этого вручную пройдем весь путь: авторизуемся, заполним поля, выберем файлы и нажмем кнопку отправки. В списке сетевых запросов мы увидим всю цепочку переходов. Нас в первую очередь интересуют запросы с методами POST и GET. Нам нужно зафиксировать:
Content-Type, User-Agent и Referer.Для более глубокого анализа трафика и удобного конструирования запросов можно использовать специализированные прокси-инструменты, такие как Fiddler или Charles. Для интерактивной отладки и подготовки запросов непосредственно в среде платформы можно использовать обработку для тестирования REST API и HTTP-запросов.
Шаг 2. Поддержание сессии и управление Cookies
Обычно сайты требуют предварительной авторизации. Чтобы последующие запросы на отправку документов были успешными, нам необходимо сохранять состояние авторизации. В 1С это реализуется при помощи правильной работы с объектом HTTPСоединение. Платформа 1С умеет автоматически управлять файлами cookie.
Рассмотрим пример создания соединения и авторизации на сайте:
// Определяем параметры подключения
Сервер = "example.com";
Порт = 443; // Используем защищенное соединение SSL
ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL();
// Создаем HTTP-соединение с поддержкой сохранения кук
HTTPСоединение = Новый HTTPСоединение(Сервер, Порт, , , , , ЗащищенноеСоединение);
// Сначала выполняем GET-запрос для получения первоначальных cookie (если необходимо)
ЗапросНачало = Новый HTTPЗапрос("/login");
ЗапросНачало.Заголовки.Вставить("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
ОтветНачало = HTTPСоединение.Получить(ЗапросНачало);
// В объекте HTTPСоединение куки сохраняются автоматически для последующих запросов в рамках этого объекта.
// Теперь подготовим POST-запрос для авторизации
ЗапросАвторизации = Новый HTTPЗапрос("/api/login-submit");
ЗапросАвторизации.Заголовки.Вставить("Content-Type", "application/x-www-form-urlencoded");
ЗапросАвторизации.Заголовки.Вставить("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
// Формируем тело запроса с логином и паролем
ТелоЗапроса = "username=my_login&password=my_password";
ЗапросАвторизации.УстановитьТелоИзСтроки(ТелоЗапроса, КодировкаТекста.UTF8, ИспользованиеМаркераВремени.НеИспользовать);
// Отправляем запрос авторизации
ОтветАвторизации = HTTPСоединение.ОтправитьДляОбъекта(ЗапросАвторизации);
Если ОтветАвторизации.КодСостояния = 200 Или ОтветАвторизации.КодСостояния = 302 Тогда
ЗаписатьЛогСобытий("Авторизация успешно пройдена!");
Иначе
ВызватьИсключение "Ошибка авторизации: " + ОтветАвторизации.КодСостояния;
КонецЕсли;
Шаг 3. Обход защитных CSRF-токенов
Большинство современных веб-ресурсов защищено от межсайтовой подделки запросов с помощью токенов безопасности (CSRF или XSRF токенов). Если мы попытаемся просто отправить POST-запрос с заполненными полями, сервер вернет ошибку доступа (например, код 403 Forbidden). Токен генерируется сервером индивидуально для каждой сессии и помещается в HTML-код страницы формы в виде скрытого поля (input type="hidden") или передается в мета-тегах заголовка.
Выясним, как обойти эту защиту в 1С. Нам нужно перед отправкой формы прочитать саму страницу формы с помощью GET-запроса, извлечь токен из полученного HTML-кода и добавить его в наш POST-запрос.
// Шаг A: Запрашиваем страницу с формой отправки документов
ЗапросФормы = Новый HTTPЗапрос("/document/send-page");
ЗапросФормы.Заголовки.Вставить("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
ОтветФормы = HTTPСоединение.Получить(ЗапросФормы);
HTMLКодСтраницы = ОтветФормы.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
// Шаг B: Извлекаем CSRF-токен из HTML.
// Предположим, он находится в теге <input name="csrf_token" value="значение">
CSRFТокен = "";
ПозицияТокена = Найти(HTMLКодСтраницы, "name=""csrf_token"" value=""");
Если ПозицияТокена > 0 Тогда
СтартЗначения = ПозицияТокена + СтрДлина("name=""csrf_token"" value=""");
ОстатокСтроки = Сред(HTMLКодСтраницы, СтартЗначения);
КонецЗначения = Найти(ОстатокСтроки, """");
Если КонецЗначения > 0 Тогда
CSRFТокен = Лев(ОстатокСтроки, КонецЗначения - 1);
КонецЕсли;
КонецЕсли;
Если ЗначениеЗаполнено(CSRFТокен) Тогда
ЗаписатьЛогСобытий("CSRF Токен успешно получен: " + CSRFТокен);
Иначе
ВызватьИсключение "Не удалось найти CSRF токен на странице!";
КонецЕсли;
Шаг 4. Передача файлов и полей в формате multipart/form-data
Когда на веб-странице есть кнопка прикрепления файла (например, PDF-документа), данные отправляются на сервер в особом формате — multipart/form-data. Этот формат предполагает разделение всех полей формы и содержимого файлов специальной уникальной границей (boundary). Нам нужно вручную собрать это тело в памяти 1С.
Рассмотрим, как правильно сформировать и отправить такой запрос:
// Генерируем уникальный разделитель границ данных
РазделительГраниц = "----1CWebClientBoundary" + Строка(Новый УникальныйИдентификатор);
// Подготавливаем HTTP-запрос
ЗапросОтправки = Новый HTTPЗапрос("/document/upload");
ЗапросОтправки.Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + РазделительГраниц);
ЗапросОтправки.Заголовки.Вставить("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
// Передаем полученный ранее CSRF-токен в заголовке или теле (зависит от логики сайта)
ЗапросОтправки.Заголовки.Вставить("X-CSRF-Token", CSRFТокен);
// Используем поток в памяти для формирования бинарного тела запроса
ПотокТела = Новый ПотокВПамяти();
ЗаписьДанных = Новый ЗаписьДанных(ПотокТела, КодировкаТекста.UTF8);
// 1. Записываем простое текстовое поле формы (например, "ТипДокумента")
ЗаписьДанных.ЗаписатьСтроку("--" + РазделительГраниц);
ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=""document_type""");
ЗаписьДанных.ЗаписатьСтроку(""); // Обязательная пустая строка перед значением
ЗаписьДанных.ЗаписатьСтроку("PDF_Invoices"); // Значение поля
// 2. Записываем еще одно текстовое поле (например, "Получатель")
ЗаписьДанных.ЗаписатьСтроку("--" + РазделительГраниц);
ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=""recipient_id""");
ЗаписьДанных.ЗаписатьСтроку("");
ЗаписьДанных.ЗаписатьСтроку("123456789");
// 3. Записываем файл PDF
ЗаписьДанных.ЗаписатьСтроку("--" + РазделительГраниц);
ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=""document_file""; filename=""invoice.pdf""");
ЗаписьДанных.ЗаписатьСтроку("Content-Type: application/pdf");
ЗаписьДанных.ЗаписатьСтроку("");
ЗаписьДанных.Закрыть(); // Сбрасываем буфер записи текста в поток
// Считываем сам файл PDF с диска в двоичные данные
ПутьКФайлуPDF = "C:\temp\invoice.pdf";
ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ПутьКФайлуPDF);
// Записываем бинарное содержимое PDF в наш поток
ПотокТела.Записать(ДвоичныеДанныеФайла);
// Снова открываем запись текста для добавления финальной границы
ЗаписьДанныхКонец = Новый ЗаписьДанных(ПотокТела, КодировкаТекста.UTF8);
ЗаписьДанныхКонец.ЗаписатьСтроку(""); // Перенос строки после бинарных данных
ЗаписьДанныхКонец.ЗаписатьСтроку("--" + РазделительГраниц + "--");
ЗаписьДанныхКонец.Закрыть();
// Получаем итоговые двоичные данные из потока в памяти
ИтоговоеТело = ПотокТела.ЗакрытьИПолучитьДвоичныеДанные();
ЗапросОтправки.УстановитьТелоИзДвоичныхДанных(ИтоговоеТело);
// Отправляем сформированный POST-запрос на сервер
ОтветОтправки = HTTPСоединение.ОтправитьДляОбъекта(ЗапросОтправки);
Если ОтветОтправки.КодСостояния = 200 Или ОтветОтправки.КодСостояния = 201 Тогда
ЗаписатьЛогСобытий("Файл успешно отправлен без использования браузера!");
Иначе
ВызватьИсключение "Ошибка отправки файла: " + ОтветОтправки.ПолучитьТелоКакСтроку();
КонецЕсли;
---
Бывают ситуации, когда веб-сайт построен по технологии Single Page Application (SPA), активно использует сложные скрипты шифрования данных на клиенте или защищен продвинутыми системами верификации запросов. В таких случаях низкоуровневая эмуляция запросов становится неоправданно сложной и трудоемкой. Намного проще запустить реальный браузер в автоматическом режиме и приказать ему выполнить действия программно. Для автоматизации пользовательских сценариев в системах без API также можно использовать платформу роботизации процессов One RPA.
Подход А. Использование Selenium через COM-объект в Windows
Для реализации этого метода на рабочем месте пользователя или сервере 1С под управлением Windows необходимо установить библиотеку SeleniumBasic и скачать актуальную версию веб-драйвера (например, ChromeDriver), соответствующую версии установленного браузера Google Chrome.
После регистрации библиотеки в системе мы получаем возможность управлять браузером непосредственно из кода 1С с помощью обращения к COM-объектам. Рассмотрим пример реализации:
Попытка
// Создаем экземпляр драйвера Chrome
Драйвер = Новый COMОбъект("Selenium.WebDriver");
Исключение
ВызватьИсключение "Не удалось инициализировать Selenium. Убедитесь, что установлена библиотека SeleniumBasic.";
КонецПопытки;
// Запускаем браузер Chrome и открываем нужный URL
Драйвер.Start("chrome", "https://example.com");
Драйвер.Get("/document/send-page");
// Находим поле ввода логина по его ID, заполняем значение
ЭлементЛогин = Драйвер.FindElementById("username-input");
ЭлементЛогин.Clear();
ЭлементЛогин.SendKeys("my_login");
// Находим поле пароля и заполняем его
ЭлементПароль = Драйвер.FindElementById("password-input");
ЭлементПароль.Clear();
ЭлементПароль.SendKeys("my_password");
// Нажимаем кнопку входа
КнопкаВойти = Драйвер.FindElementById("login-submit-button");
КнопкаВойти.Click();
// Ожидаем загрузки элементов следующей страницы (таймаут 10 секунд)
Драйвер.ImplicitlyWait(10000);
// Выбираем значение из выпадающего списка (Dropdown)
// Находим элемент списка
ЭлементСписок = Драйвер.FindElementById("document-type-select");
// Для работы со списками в Selenium удобно использовать объект Select
СелекторСписка = Новый COMОбъект("Selenium.Select");
СелекторСписка.Init(ЭлементСписок);
СелекторСписка.SelectByValue("PDF_Invoices"); // Выбор значения по его value атрибуту
// Заполняем поле пути к файлу. Selenium умеет передавать путь к локальному файлу
// непосредственно в input с типом "file", имитируя системный диалог выбора.
ПолеФайла = Драйвер.FindElementById("file-uploader-input");
ПолеФайла.SendKeys("C:\temp\invoice.pdf");
// Имитируем нажатие кнопки отправки формы
КнопкаОтправить = Драйвер.FindElementById("btn-submit-form");
КнопкаОтправить.Click();
// Если на странице появляется модальное окно (JavaScript alert/confirm), подтверждаем его
Попытка
Алерт = Драйвер.SwitchToAlert();
Алерт.Accept(); // Нажимаем кнопку ОК в модальном окне
Исключение
// Модальное окно не появилось, продолжаем выполнение
КонецПопытки;
// Закрываем браузер по окончании работы
Драйвер.Quit();
Подход Б. Использование современных фреймворков (Playwright / Puppeteer) через внешние скрипты
Использование COM-объектов Selenium считается классическим, но постепенно устаревающим подходом. Современные веб-разработчики для автоматизации используют более надежные инструменты, такие как Playwright или Puppeteer, работающие на платформе Node.js или Python. Они работают быстрее, стабильнее и не требуют установки COM-библиотек в операционной системе.
Выясним, как интегрировать этот стек с 1С. Мы напишем простой скрипт на языке JavaScript (для Node.js), который будет принимать параметры через аргументы командной строки, а из 1С будем вызывать его с помощью стандартного метода ЗапуститьПриложение().
Пример JS-скрипта (файл upload.js):
const { chromium } = require('playwright');
(async () => {
// Получаем аргументы командной строки
const args = process.argv.slice(2);
const filePath = args[0];
const recipient = args[1];
// Запускаем браузер в фоновом режиме (headless: true)
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
// Переходим на страницу
await page.goto('https://example.com/login');
// Заполняем форму
await page.fill('#username-input', 'my_login');
await page.fill('#password-input', 'my_password');
await page.click('#login-submit-button');
// Ожидаем перехода на страницу отправки документов
await page.waitForURL('**/document/send-page');
// Заполняем реквизиты
await page.selectOption('#document-type-select', 'PDF_Invoices');
await page.fill('#recipient-input', recipient);
// Загружаем файл PDF
await page.setInputFiles('#file-uploader-input', filePath);
// Нажимаем кнопку отправки и автоматически соглашаемся на все всплывающие окна dialog
page.on('dialog', async dialog => {
await dialog.accept(); // Автоматически нажимаем ОК в модальных окнах alert/confirm
});
await page.click('#btn-submit-form');
// Ожидаем завершения отправки по появлению сообщения об успехе
await page.waitForSelector('.success-message');
await browser.close();
process.exit(0);
})();
Теперь напишем код вызова этого внешнего скрипта из нашей базы данных 1С:
// Задаем параметры запуска
ПутьКNodeJS = "node"; // Если путь прописан в переменных окружения PATH
ПутьКJSСкрипту = """C:\scripts\upload.js""";
ПараметрФайл = """C:\temp\invoice.pdf""";
ПараметрПолучатель = "123456789";
СтрокаЗапуска = СтрШаблон("%1 %2 %3 %4", ПутьКNodeJS, ПутьКJSСкрипту, ПараметрФайл, ПараметрПолучатель);
// Запускаем приложение в синхронном режиме, ожидая завершения работы скрипта
КодВозврата = Неопределено;
ЗапуститьПриложение(СтрокаЗапуска, , Истина, КодВозврата);
Если КодВозврата = 0 Тогда
ПоказатьПредупреждение(, "Документ успешно отправлен через Playwright!");
Иначе
ВызватьИсключение "Произошла ошибка при выполнении внешнего скрипта автоматизации. Код ошибки: " + Строка(КодВозврата);
КонецЕсли;
---
Если нам необходимо предоставить пользователю возможность визуально наблюдать за процессом заполнения полей, вмешиваться в работу алгоритма в случае непредвиденных ситуаций или если запуск внешнего софта запрещен политиками безопасности компании, на помощь приходит штатный механизм платформы 1С — элемент управляемой формы Поле HTML документа.
В современных версиях платформы 1С (начиная с 8.3.14) данный элемент функционирует на базе современного высокопроизводительного движка WebKit/Blink (Chromium), что гарантирует корректную работу скриптов абсолютно любого современного сайта.
Разберем по шагам, как настроить управление страницей из формы 1С.
Шаг 1. Создание формы и загрузка страницы
Добавим на управляемую форму реквизит с типом Строка (например, АдресСайта) и перетащим его на форму в виде элемента с видом Поле HTML документа. При открытии формы выполним переход на нужный сайт:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Указываем стартовый адрес страницы
АдресСайта = "https://example.com/login";
КонецПроцедуры
Шаг 2. Программное заполнение DOM и управление элементами
После того как страница полностью загрузилась в поле HTML документа, срабатывает событие ДокументСформирован. Именно в обработчике этого события мы получаем прямой доступ к объектной модели документа (DOM) открытого сайта.
&НаКлиенте
Процедура ПолеHTMLДокументСформирован(Элемент, URL)
// Проверим, что мы находимся именно на странице авторизации
Если Найти(URL, "login") > 0 Тогда
// Получаем объект документа страницы
ДокументHTML = Элементы.ПолеHTML.Документ;
// Находим элементы ввода по ID и заполняем значения
ПолеЛогин = ДокументHTML.getElementById("username-input");
Если ПолеЛогин <> Неопределено Тогда
ПолеЛогин.value = "my_login";
КонецЕсли;
ПолеПароль = ДокументHTML.getElementById("password-input");
Если ПолеПароль <> Неопределено Тогда
ПолеПароль.value = "my_password";
КонецЕсли;
// Имитируем клик по кнопке входа
КнопкаВхода = ДокументHTML.getElementById("login-submit-button");
Если КнопкаВхода <> Неопределено Тогда
КнопкаВхода.click();
КонецЕсли;
ИначеЕсли Найти(URL, "send-page") > 0 Тогда
// Мы перешли на страницу отправки данных
ДокументHTML = Элементы.ПолеHTML.Документ;
// Заполняем текстовое поле
ПолеРеквизита = ДокументHTML.getElementById("recipient-input");
Если ПолеРеквизита <> Неопределено Тогда
ПолеРеквизита.value = "123456789";
КонецЕсли;
// Выбираем значение в списке (dropdown)
ЭлементСписка = ДокументHTML.getElementById("document-type-select");
Если ЭлементСписка <> Неопределено Тогда
ЭлементСписка.value = "PDF_Invoices";
// Важно вызвать событие изменения, так как на сайтах к нему часто привязаны JS-обработчики
СобытиеИзменения = ДокументHTML.createEvent("HTMLEvents");
СобытиеИзменения.initEvent("change", Истина, Истина);
ЭлементСписка.dispatchEvent(СобытиеИзменения);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Шаг 3. Внедрение JavaScript для сложных действий
Если стандартного управления элементами DOM через интерфейс 1С недостаточно или требуется выполнить сложную логику (например, обойти модальные окна, подменив стандартную функцию alert()), мы можем запустить произвольный JavaScript-код напрямую в контексте загруженной страницы. Для этого используется метод eval глобального объекта defaultView окна браузера.
&НаКлиенте
Процедура ВыполнитьСложныеДействияJS()
ДокументHTML = Элементы.ПолеHTML.Документ;
ОкноСтраницы = ДокументHTML.defaultView;
// Формируем текст JS-скрипта
// Данный скрипт переопределяет метод вывода алертов, чтобы они не блокировали работу программы,
// а также выполняет программный клик по кнопке отправки
КодJS =
"window.alert = function(msg) { console.log('Перехвачен алерт: ' + msg); return true; };" +
"window.confirm = function(msg) { return true; };" + // Автоматическое согласие во всех диалогах подтверждения
"var submitBtn = document.getElementById('btn-submit-form');" +
"if (submitBtn) { submitBtn.click(); }";
// Выполняем инъекцию JavaScript
Попытка
ОкноСтраницы.eval(КодJS);
Исключение
ЗаписатьЛогСобытий("Ошибка при выполнении JavaScript внутри страницы: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
---
Для того чтобы подвести итог и выбрать наиболее оптимальный инструмент для вашей конкретной задачи, проанализируем следующую сводную таблицу:
| Критерий сравнения | Вариант 1. HTTP-запросы | Вариант 2. Selenium / Playwright | Вариант 3. Поле HTML документа |
|---|---|---|---|
| Скорость работы | Максимальная (миллисекунды) | Низкая (требуется время на рендеринг браузера) | Средняя (зависит от клиентского ПК) |
| Где выполняется | И на сервере, и на клиенте | На клиенте или на сервере (в headless-режиме) | Только на клиенте (интерактивно) |
| Сложность разработки | Высокая (требуется глубокий анализ трафика) | Средняя (удобные селекторы элементов) | Средняя (требуется знание DOM и JS) |
| Зависимость от стороннего ПО | Полностью отсутствует (штатные объекты 1С) | Высокая (нужна установка Node.js/Python, браузеров) | Полностью отсутствует (штатные объекты 1С) |
| Устойчивость к изменениям сайта | Низкая (любое изменение API ломает обмен) | Средняя (чувствителен к изменению ID/классов) | Средняя (зависит от стабильности верстки) |
Таким образом, мы рекомендуем придерживаться следующей логики: