Как исправить проблему с кодировкой (кракозябры, вопросительные знаки) при парсинге сайта из 1С?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При получении текстовых данных с веб-сайтов через HTTP-запросы разработчики часто сталкиваются с тем, что вместо привычного русского текста в 1С загружаются либо знаки вопроса ???, либо нечитаемые символы (так называемый «модзибакэ» или «кракозябры») вида Ii?ou/ia?aoiau или Ïîðò. Особенно остро эта проблема проявляется при работе со старыми ресурсами, использующими кодировку Windows-1251 (CP1251).

В этой статье мы подробно разберем, почему возникает эта ошибка, проанализируем механизмы работы платформы 1С с различными кодировками и рассмотрим несколько надежных способов решения проблемы: от современных кроссплатформенных методов до обходных путей для устаревших систем.

Выясняем причину искажения кодировки

Основная причина появления символов вроде Ii?ou/ia?aoiau заключается в неверном декодировании полученного байтового потока. Когда мы используем COM-объект WinHttp.WinHttpRequest.5.1 и обращаемся к его свойству ResponseText, библиотека пытается самостоятельно преобразовать бинарное тело ответа в строку.

Если веб-сервер в HTTP-заголовке ответа Content-Type не указывает кодировку явно (например, отсутствует параметр charset=windows-1251), библиотека WinHttpRequest декодирует данные по умолчанию в кодировку ISO-8859-1 (Latin-1). Происходит следующее:

  1. Русская буква «П» в кодировке Windows-1251 передается байтом 0xCF.
  2. Библиотека интерпретирует байт 0xCF по таблице ISO-8859-1 и превращает его в латинскую букву «Ï».
  3. Русская строчная «о» передается байтом 0xEE, который в ISO-8859-1 превращается в «î».

В результате весь русский текст превращается в смесь латинских букв с умлаутами и спецсимволов. Обычные попытки перекодировать уже испорченную строку средствами 1С не принесут результата, так как исходная информация о байтах уже частично утеряна. Нам необходимо работать не с искаженным текстом, а непосредственно с «сырыми» двоичными данными (байтами).

---

Решение 1. Современный нативный метод платформы 1С (Рекомендуемый)

Если вы работаете на платформе 1С:Предприятие 8.3, забудьте об использовании нестабильных и платформозависимых COM-объектов. В платформе есть мощные встроенные инструменты для выполнения HTTP-запросов, которые работают как под Windows, так и под Linux, а также не зависят от разрядности клиента или сервера (32/64-бит).

Рассмотрим пример использования объектов HTTPСоединение и HTTPОтвет с явным указанием кодировки при получении тела запроса:


// Выделяем имя сервера и путь к ресурсу из общего адреса
ИмяСервера = "example.com";
ПутьКРесурсу = "/page_win1251.html";

// Создаем HTTP-соединение
Соединение = Новый HTTPСоединение(ИмяСервера);
Запрос = Новый HTTPЗапрос(ПутьКРесурсу);

Попытка
    Ответ = Соединение.Получить(Запрос);
    Если Ответ.КодСостояния = 200 Тогда
        // Самый важный момент: принудительно указываем кодировку для декодирования байтов
        ТекстСтраницы = Ответ.ПолучитьТелоКакСтроку("windows-1251");
        
        // Теперь переменная ТекстСтраницы содержит корректный русский текст
        Сообщить(Лев(ТекстСтраницы, 150));
    Иначе
        Сообщить("Ошибка сервера: " + Ответ.КодСостояния);
    КонецЕсли;
Исключение
    Сообщить("Ошибка подключения: " + ОписаниеОшибки());
КонецПопытки;

Если вам требуется сначала получить двоичные данные ответа, а затем преобразовать их в строку в определенном месте кода, используйте встроенную функцию ПолучитьСтрокуИзДвоичныхДанных():


ДвоичныеДанныеТела = Ответ.ПолучитьТелоКакДвоичныеДанные();
// Преобразуем бинарный поток в нормальную кириллицу
ТекстСтраницы = ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанныеТела, "windows-1251");

---

Решение 2. Использование ADODB.Stream в оперативной памяти (без создания файлов)

Если ваша конфигурация работает на старых версиях платформы (или даже на 1С 7.7) и вы вынуждены использовать WinHttpRequest, вы можете обойти проблему искажения символов. Для этого нельзя обращаться к WinHttp.ResponseText.

Вместо этого мы берем свойство ResponseBody (которое возвращает исходный массив байтов) и перекодируем его с помощью COM-объекта ADODB.Stream прямо в оперативной памяти, избегая медленных операций записи на жесткий диск.

Проанализируем этот алгоритм:


URL = "http://example.com/page_win1251.html";

WinHttp = Новый COMОбъект("WinHttp.WinHttpRequest.5.1");
WinHttp.Open("GET", URL, Ложь);
WinHttp.Send();

Если WinHttp.Status = 200 Тогда
    // Получаем "сырые" байты ответа (SafeArray)
    БайтыОтвета = WinHttp.ResponseBody; 
    
    // Инициализируем поток ADO в памяти
    Поток = Новый COMОбъект("ADODB.Stream");
    Поток.Type = 1; // adTypeBinary (работаем с двоичными данными)
    Поток.Open();
    Поток.Write(БайтыОтвета); // Записываем байты в поток
    
    // Переключаем поток в текстовый режим для декодирования
    Поток.Position = 0; // Возвращаем указатель в начало
    Поток.Type = 2; // adTypeText (переключаем на чтение текста)
    Поток.Charset = "windows-1251"; // Задаем правильную кодировку источника
    
    // Читаем готовый раскодированный текст в переменную 1С
    ТекстСтраницы = Поток.ReadText(); 
    Поток.Close();
    
    Сообщить(ТекстСтраницы);
Иначе
    Сообщить("Ошибка HTTP: " + WinHttp.Status);
КонецЕсли;

Преимущества данного подхода: мы не создаем временных файлов на диске, не засоряем операционную систему и не тратим время на операции ввода-вывода (I/O), которые сильно замедляют работу системы при парсинге больших объемов данных.

---

Решение 3. Обходной путь через временный файл (Решение автора темы)

Если по каким-то причинам предыдущие методы не могут быть реализованы (например, ограничения прав доступа к COM-объектам работы с памятью в вашей системе безопасности), можно воспользоваться схемой с записью бинарного тела во временный файл.

Разберем этот процесс по шагам:

  1. Выполняем запрос через WinHttp.
  2. Сохраняем полученные байты во временный файл на диске с помощью потока.
  3. Читаем полученный файл стандартными средствами 1С, явно указав системную кодировку или кодировку "windows-1251".

// Создаем уникальное имя для временного файла на диске
ИмяВходящегоФайла = ПолучитьВременныйФайл("html");

WinHttp = Новый COMОбъект("WinHttp.WinHttpRequest.5.1");
WinHttp.Open("GET", "http://example.com/page_win1251.html", Ложь);
WinHttp.Send();

Если WinHttp.Status = 200 Тогда
    // Записываем бинарный ответ в файл через ADODB.Stream
    Поток = Новый COMОбъект("ADODB.Stream");
    Поток.Type = 1; // Бинарный режим
    Поток.Open();
    Поток.Write(WinHttp.ResponseBody);
    Поток.SaveToFile(ИмяВходящегоФайла, 2); // 2 = overwriting existing file
    Поток.Close();
    
    // Читаем файл встроенными средствами 1С с указанием правильной кодировки
    ТекстДок = Новый ТекстовыйДокумент;
    ТекстДок.Прочитать(ИмяВходящегоФайла, "windows-1251");
    
    ТекстСтраницы = ТекстДок.ПолучитьТекст();
    
    // Удаляем временный файл после обработки
    Попытка
        УдалитьФайлы(ИмяВходящегоФайла);
    Исключение
        // Игнорируем ошибку удаления, если файл занят
    КонецПопытки;
КонецЕсли;

Этот метод гарантирует, что 1С правильно интерпретирует каждый байт файла при чтении через ТекстовыйДокумент и на выходе мы получим чистую кириллицу без искажений.

Резюме и рекомендации

Для эффективного парсинга сайтов и исключения багов с кодировками придерживайтесь следующих правил:

← На главную