При автоматизации взаимодействия с внешними веб-сервисами, особенно когда требуется загрузить отчеты или документы в формате PDF, разработчики 1С часто сталкиваются с проблемой: вместо ожидаемого PDF-файла сохраняется обычная HTML-страница, иногда даже упакованная в архив. Давайте вместе разберемся, почему это происходит и как эффективно решить эту задачу средствами 1С.
Наш кейс основан на попытке загрузить список лекарственных средств с сайта Росздравнадзора в формате PDF. Изначальная попытка, как это часто бывает, привела к получению HTML-страницы вместо PDF.
Рассмотрим основной подход, который позволил успешно загрузить PDF-файл в одной из рабочих конфигураций 1С. Мы проанализируем, какие параметры и заголовки оказались ключевыми.
Инициализация соединения и запроса:
Начнем с создания объектов для установки защищенного HTTP-соединения и формирования запроса. Обратите внимание на использование ЗащищенноеСоединениеOpenSSL() для работы с HTTPS.
Адрес = "roszdravnadzor.gov.ru";
HTTPСоединение = Новый HTTPСоединение(Адрес,443,,,,,Новый ЗащищенноеСоединениеOpenSSL(),Ложь);
HTTPЗапрос = Новый HTTPЗапрос("services/turnover");
Здесь мы указываем адрес хоста и порт 443 для HTTPS. Объект HTTPЗапрос инициализируется с относительным путем к ресурсу, который обрабатывает запрос на сервере.
Формирование параметров запроса:
Ключевым моментом является правильная передача параметров, которые сервер ожидает для генерации PDF. В нашем случае, это строка параметров, аналогичная той, что отправляется браузером при нажатии кнопки "Скачать PDF".
Параметры="q_label=244857651&dt_from=01.01.2025&dt_to=&q_type_ls=&q_org=&q_dt_ru_from=&q_dt_ru_to=&q_no_ru=&q_tn=&q_mnn=&q_series=&q_producer=&q_country=&pdf=1";
HTTPЗапрос.УстановитьТелоИзСтроки(Параметры, "UTF-8");
Здесь особенно важен параметр pdf=1, который явно указывает серверу, что нам нужен именно PDF-файл. Метод УстановитьТелоИзСтроки() используется для передачи параметров в теле POST-запроса с кодировкой UTF-8. Это соответствует стандартному типу содержимого application/x-www-form-urlencoded.
Настройка HTTP-заголовков:
Заголовки играют критическую роль в том, как сервер обрабатывает запрос. Мы должны максимально точно эмулировать заголовки, которые отправляет браузер при успешной загрузке.
HTTPЗапрос.Заголовки.Вставить("accept-encoding", "gzip, deflate, br, zstd");
HTTPЗапрос.Заголовки.Вставить("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7");
HTTPЗапрос.Заголовки.Вставить("content-type", "application/x-www-form-urlencoded");
accept-encoding: Сообщает серверу, какие методы сжатия поддерживает клиент.accept: Указывает, какие типы содержимого клиент готов принять. Включение application/pdf или */* (любой тип) здесь очень важно.content-type: Информирует сервер о формате данных, передаваемых в теле запроса. Для наших параметров это application/x-www-form-urlencoded.В первоначальной попытке было много заголовков, включая User-Agent, Referer, Origin, Cookie и другие. Иногда их избыток или неточность могут мешать. В данном случае, сокращенный набор заголовков оказался достаточным.
Отправка запроса и сохранение ответа:
Наконец, отправляем сформированный запрос и сохраняем полученный ответ в файл.
HTTPОтвет = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос,"F:\data\123.pdf");
Метод ОтправитьДляОбработки() выполняет запрос и сохраняет тело ответа сервера в указанный файл. Если сервер действительно вернул PDF, то файл 123.pdf будет корректным PDF-документом.
Важно отметить: Этот код был успешно протестирован на платформе 1С 8.3.25.1546 в файловой базе, управляемом приложении (УТ11). Это указывает на то, что для современных версий платформы данный подход является рабочим.
Если базовое решение не сработало, или вы столкнулись с более сложными сценариями, давайте углубимся в детали и рассмотрим дополнительные аспекты.
Одна из основных причин, по которой вместо PDF вы получаете HTML, заключается в том, что сервер фактически отправляет HTML-страницу, даже если вы просили PDF. Это может быть из-за неправильных параметров, отсутствия авторизации, или если сервер не смог сгенерировать PDF и вернул страницу ошибки или страницу с результатами поиска в HTML.
Мы настоятельно рекомендуем после получения ответа от сервера проверять заголовок Content-Type объекта HTTPОтвет.
Получение заголовка:
Ответ = Соединение.ОтправитьДляОбработки(Запрос, ПолныйПутьФайла);
ТипСодержимого = Ответ.Заголовки.Получить("Content-Type");
Анализ типа:
Если ТипСодержимого равно application/pdf, то файл, скорее всего, является PDF. Если же это text/html или что-то иное, значит, сервер вернул HTML-страницу. В этом случае, сохранение файла с расширением `.pdf` не сделает его PDF-документом.
Если Ответ.КодСостояния = 200 Тогда
ТипСодержимого = Ответ.Заголовки.Получить("Content-Type");
Если Найти(ТипСодержимого, "application/pdf") > 0 Тогда
// Файл успешно скачан и это PDF
Сообщить("PDF-файл успешно загружен.");
Иначе
// Получен HTML или другой тип контента
Сообщить("Внимание! Вместо PDF получен файл типа: " + ТипСодержимого + ". Возможно, это HTML-страница.");
// Можно сохранить с другим расширением, например, .html
// Ответ.ПолучитьТелоКакСтроку() или обработать другим способом
КонецЕсли;
Иначе
Сообщить("Ошибка при загрузке файла. Код состояния: " + Ответ.КодСостояния);
КонецЕсли;
Некоторые веб-сервисы, особенно при генерации отчетов, сначала возвращают HTTP-ответ с кодом состояния перенаправления (например, 302 Found) и заголовком Location, который указывает на URL, откуда можно скачать сгенерированный файл. Браузеры автоматически следуют этим перенаправлениям, но в 1С это нужно обрабатывать явно.
Разберем по шагам, как это сделать:
Проверка кода состояния: После первого запроса проверьте Ответ.КодСостояния. Если оно равно 301, 302, 303 или 307, то это перенаправление.
Извлечение нового URL: Получите URL для перенаправления из заголовка Location.
Если Ответ.КодСостояния = 302 Тогда
НовыйURL = Ответ.Заголовки.Получить("Location");
Если Не ПустаяСтрока(НовыйURL) Тогда
// Выполняем новый GET-запрос по НовомуURL
// ... (создаем новый HTTPЗапрос и Соединение, если нужно)
// ... (отправляем GET-запрос)
КонецЕсли;
КонецЕсли;
Выполнение второго запроса: Обычно, для скачивания файла по новому URL достаточно выполнить простой GET-запрос. Убедитесь, что вы правильно формируете новое HTTPСоединение и HTTPЗапрос для этого URL.
Мы выяснили, что код успешно работает в УТ11, но не работает в УТ10 с режимом совместимости "Версия 8.2.13". Это критичный момент. Режим совместимости ограничивает функциональность конфигурации до уровня указанной версии платформы. Это может проявляться в:
Рекомендация: Если есть возможность, переходите на более новые версии платформы 1С и отключайте режим совместимости или устанавливайте его на максимально актуальную версию.
Если обновление платформы или изменение режима совместимости невозможно, распространенным решением для обхода ограничений встроенных HTTP-объектов 1С в старых версиях является использование WinHttps. Это COM-объект, предоставляемый операционной системой Windows, который дает более низкоуровневый и гибкий контроль над HTTP/HTTPS запросами.
Давайте проанализируем преимущества WinHttps:
Использование WinHttps требует создания COM-объекта (например, Новый COMОбъект("WinHttp.WinHttpRequest.5.1")) и работы с его методами (Open, SetRequestHeader, Send, ResponseText, ResponseBody и т.д.). Это более сложный подход, но он предоставляет мощные инструменты для решения проблем совместимости.
Как мы уже упоминали, HTTP-заголовки играют важную роль. Рассмотрим подробнее некоторые из них:
User-Agent: Идентифицирует клиентское приложение. Некоторые серверы могут блокировать запросы от неизвестных или "небраузерных" User-Agent.Referer: Указывает URL предыдущей веб-страницы, с которой был сделан запрос. Используется для проверки источника запроса.Origin: Похож на Referer, но используется для кросс-доменных запросов.Cookie: Используется для поддержания сессии. Если сайт требует авторизации или использует сессионные куки, их передача обязательна.Совет: Используйте инструменты разработчика в браузере (например, F12 в Chrome/Firefox) для анализа HTTP-запросов, которые ваш браузер отправляет при успешной загрузке PDF. Постарайтесь максимально точно эмулировать эти заголовки в своем коде 1С.
Для надежной работы всегда включайте в код проверку КодСостояния ответа сервера (200 для успеха, 4xx для клиентских ошибок, 5xx для серверных ошибок) и обрабатывайте возможные исключения, которые могут возникнуть при выполнении HTTP-запросов (например, проблемы с сетью, таймауты).
Например:
Попытка
Ответ = Соединение.ОтправитьДляОбработки(Запрос, ПолныйПутьФайла);
Если Ответ.КодСостояния = 200 Тогда
Сообщить("Файл успешно загружен.");
Иначе
Сообщить("Ошибка загрузки. Код состояния: " + Ответ.КодСостояния +
" " + Ответ.Заголовки.Получить("Status-Text"));
КонецЕсли;
Исключение
Сообщить("Произошла ошибка при выполнении HTTP-запроса: " + ОписаниеОшибки());
КонецПопытки;
Для более глубокого понимания принципов работы с веб-сервисами, давайте посмотрим на аналогичное решение, реализованное на языке Python. Хотя это не код 1С, он демонстрирует те же универсальные принципы и подход к решению проблемы, подтверждая наши выводы относительно заголовков и проверки типа содержимого.
import requests
import logging
import os
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
# --- Параметры препарата (указываем здесь!) ---
drug_name = "гелофузин"
series_number = "244857651"
mnn = ""
# --- Константы ---
save_folder = r"C:\Users\Иван\Downloads"
safe_drug_name = drug_name.replace(" ", "")
save_filename = f"Выписка_{safe_drug_name}.pdf"
save_path = os.path.join(save_folder, save_filename)
# --- Логирование ---
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler("download_log.txt", encoding="utf-8"),
logging.StreamHandler()
])
def download_pdf():
url = "https://roszdravnadzor.gov.ru/services/turnover"
headers = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Accept-Language": "ru,en;q=0.9,en-GB;q=0.8,en-US;q=0.7",
"Cache-Control": "max-age=0",
"Connection": "keep-alive",
"Content-Type": "application/x-www-form-urlencoded",
"Origin": "https://roszdravnadzor.gov.ru" ,
"Referer": "https://roszdravnadzor.gov.ru/" ,
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "same-origin",
"Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0",
"Cookie": "uid=434360480391640124; sp_test=1; sputnik_session=1746516644741|3",
}
payload = {
"q_label": drug_name,
"dt_from": "",
"dt_to": "",
"q_type_ls": "",
"q_org": "",
"q_dt_ru_from": "",
"q_dt_ru_to": "",
"q_no_ru": "",
"q_tn": "",
"q_mnn": mnn,
"q_series": series_number,
"q_producer": "",
"q_country": "",
"pdf": "1",
}
# Сессия с повторами
session = requests.Session()
retries = Retry(
total=3,
backoff_factor=2,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["POST"]
)
adapter = HTTPAdapter(max_retries=retries)
session.mount("https://", adapter)
session.mount("http://", adapter)
try:
logging.info("Отправка POST-запроса на %s", url)
response = session.post(url, headers=headers, data=payload, timeout=(10, 60))
if response.status_code == 200:
if response.headers.get('Content-Type') == 'application/pdf':
with open(save_path, "wb") as f:
f.write(response.content)
logging.info("Файл успешно сохранён: %s", save_path)
else:
logging.error("Ожидался PDF, но пришло: %s", response.headers.get('Content-Type'))
else:
logging.error("Ошибка при скачивании файла: статус %s", response.status_code)
except requests.exceptions.RequestException as e:
logging.exception("Ошибка при выполнении запроса: %s", str(e))
download_pdf()
Что мы можем увидеть в этом коде, применительно к 1С:
User-Agent, Referer, Origin и Cookie. Это подтверждает важность их максимально точной эмуляции.payload: Аналогично 1С, передаются параметры в теле POST-запроса, включая "pdf": "1".Content-Type: Явная проверка заголовка response.headers.get('Content-Type') == 'application/pdf' является ключевым шагом для подтверждения того, что мы действительно получили PDF.Retry) делает запрос более устойчивым к временным проблемам сети или сервера. Эти принципы также следует применять в 1С.В заключение, успешная загрузка PDF-файлов с веб-сайтов в 1С требует внимательного анализа HTTP-запросов, точной настройки параметров и заголовков, а также грамотной обработки ответов сервера. Надеемся, что этот подробный разбор поможет вам решить подобные задачи в ваших проектах 1С.
← К списку