Как получить названия городов с сайта через HTTP-запрос в 1С, если сайт возвращает HTML, а не JSON?

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

При работе с внешними ресурсами из 1С разработчики часто сталкиваются с необходимостью получать данные по HTTP-запросу. Однако не всегда можно предсказать формат ответа сервера. В этой статье мы разберем ситуацию, когда вы ожидаете получить данные в формате JSON, но вместо этого сервер возвращает HTML-страницу. Мы рассмотрим, как выяснить истинную причину ошибки и какие подходы можно использовать для извлечения нужной информации из HTML или получения ее из более надежных источников.

Выясняем причину ошибки: HTML вместо JSON

Наш читатель столкнулся с проблемой при попытке получить названия городов с сайта ru.wiktionary.org. Изначально была сделана попытка использовать объект ЧтениеJSON для обработки ответа, что привело к ошибке "Непредвиденный символ при чтении JSON". Давайте проанализируем его код и выясним причину:


ЗащищенноеСоединение = Новый ЗащищенноеСоединениеOpenSSL;
Соединение = Новый HTTPСоединение("ru.wiktionary.org",,,,,, ЗащищенноеСоединение);
Запрос = Новый HTTPЗапрос("/wiki/Категория:Названия_городов_России/ru");
Ответ = Соединение.Получить(Запрос);

Если Ответ.КодСостояния = 200 Тогда
    ЧтениеJSON = Новый ЧтениеJSON;
    // Ошибка происходит здесь, так как Ответ.ПолучитьТелоКакСтроку() содержит HTML, а не JSON.
    ЧтениеJSON.УстановитьСтроку(Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8)); 
    ЧтениеJSON.Прочитать;
КонецЕсли;

Как справедливо было замечено в обсуждении, основной причиной ошибки "Непредвиденный символ при чтении JSON" является то, что Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8) на самом деле возвращает HTML-код веб-страницы, а не JSON-строку. Объект ЧтениеJSON предназначен исключительно для работы со стандартизированным форматом JSON и не может интерпретировать HTML-разметку как допустимый JSON. Таким образом, при попытке начать чтение HTML-строки как JSON, он натыкается на первый же символ HTML-тега (например, '<') и сообщает об ошибке.

Для того чтобы успешно получить названия городов, нам необходимо изменить подход, учитывая, что мы имеем дело с HTML. Рассмотрим, какие методы можно применить в такой ситуации.

Методы получения данных из HTML в 1С

Хотя 1С не имеет встроенных мощных инструментов для полноценного DOM-парсинга HTML, как это реализовано в браузерах или некоторых других языках программирования, у нас есть несколько способов для извлечения информации из HTML-строки. Однако важно помнить, что прямой парсинг HTML является трудоемким и уязвимым: любое изменение в структуре веб-страницы может сломать ваш код. Поэтому всегда предпочтительнее искать альтернативные источники данных.

  1. Прямой строковый парсинг HTML

    Этот метод основан на использовании стандартных строковых функций 1С. Мы будем искать определенные подстроки (например, открывающие и закрывающие теги) и извлекать текст между ними. Такой подход требует глубокого понимания структуры HTML-страницы, с которой вы работаете.

    Для работы мы можем использовать следующие функции 1С:

    • Найти(Строка, Подстрока): Находит позицию первого вхождения подстроки.
    • СтрНайти(Строка, Подстрока, Позиция): Находит позицию подстроки, начиная с указанной позиции.
    • Сред(Строка, НачальнаяПозиция, КоличествоСимволов): Извлекает часть строки.
    • СтрДлина(Строка): Возвращает длину строки.
    • СтрЗаменить(Строка, ЧтоЗаменять, НаЧтоЗаменять): Заменяет подстроки.

    Пример концептуального кода:

    Предположим, на странице есть список городов в виде <li><a href="...">Город N</a></li>. Мы хотим извлечь текст "Город N".

    
    HTMLТекст = Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
    НачалоПоиска = 1;
    СписокГородов = Новый Массив;
    
    Пока Истина Ци
        НачалоСсылки = СтрНайти(HTMLТекст, "<li><a href=", НачалоПоиска);
        Если НачалоСсылки = 0 Тогда
            Прервать;
        КонецЕсли;
    
        // Ищем начало текста города после ">"
        НачалоТекстаГорода = СтрНайти(HTMLТекст, ">", НачалоСсылки);
        Если НачалоТекстаГорода = 0 Тогда
            Прервать;
        КонецЕсли;
        НачалоТекстаГорода = НачалоТекстаГорода + 1; // Переходим за ">"
    
        // Ищем конец текста города перед "</a>"
        КонецТекстаГорода = СтрНайти(HTMLТекст, "</a>", НачалоТекстаГорода);
        Если КонецТекстаГорода = 0 Тогда
            Прервать;
        КонецЕсли;
    
        Город = Сред(HTMLТекст, НачалоТекстаГорода, КонецТекстаГорода - НачалоТекстаГорода);
        СписокГородов.Добавить(Город);
    
        НачалоПоиска = КонецТекстаГорода + 1; // Продолжаем поиск после текущей ссылки
    КонецЦикла;
    
    Для Каждого ГородИзСписка Из СписокГородов Цикла
        Сообщить(ГородИзСписка);
    КонецЦикла;
    

    Этот подход может быть очень трудоемким, особенно если HTML-структура сложная или неконсистентная. Он крайне неустойчив к изменениям в разметке сайта.

  2. Использование регулярных выражений через COM-объект `VBScript.RegExp`

    Регулярные выражения предоставляют гораздо более мощный и гибкий способ извлечения данных из строк. В 1С мы можем использовать объект COM VBScript.RegExp для работы с ними. Это значительно упрощает поиск и извлечение сложных шаблонов из полученного HTML-текста.

    Пример создания объекта:

    
    Попытка
        RegExp = Новый COMОбъект("VBScript.RegExp");
    Исключение
        Сообщить("Не удалось создать объект VBScript.RegExp. Возможно, отсутствует регистрация dll или компонент не поддерживается на данной ОС.");
        Возврат;
    КонецПопытки;
    

    Пример использования `VBScript.RegExp` для поиска городов:

    Предположим, мы ищем названия городов, заключенные в ссылки вида <a href="/wiki/Город_N">Город N</a>. Мы можем использовать регулярное выражение <a[^>]*?href="[^"]*?">([^<]+?)</a>, где ([^<]+?) захватит текст города.

    
    HTMLТекст = Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
    
    RegExp.Pattern = "<a[^>]*?href=""[^""]*?"">([^<]+?)</a>"; // Шаблон для поиска ссылок с текстом
    RegExp.Global = Истина; // Искать все совпадения, а не только первое
    RegExp.IgnoreCase = Истина; // Игнорировать регистр
    
    Совпадения = RegExp.Execute(HTMLТекст);
    СписокГородов = Новый Массив;
    
    Для Каждого Совпадение Из Совпадения Цикла
        // Первый элемент SubMatches (индекс 0) содержит текст, захваченный первой группой в Pattern
        Если Совпадение.SubMatches.Count > 0 Тогда
            Город = Совпадение.SubMatches.Item(0);
            СписокГородов.Добавить(Город);
        КонецЕсли;
    КонецЦикла;
    
    Для Каждого ГородИзСписка Из СписокГородов Цикла
        Сообщить(ГородИзСписка);
    КонецЦикла;
    

    Этот подход значительно более гибок, чем простой строковый парсинг, но всё равно требует аккуратного подбора регулярных выражений и может быть сложен в отладке при изменениях на сайте.

  3. Внешние компоненты для парсинга HTML

    Существуют внешние компоненты, разработанные специально для 1С, которые могут облегчить парсинг HTML. Эти компоненты обычно предоставляют более удобные методы для навигации по DOM-дереву HTML-документа, схожие с тем, как это делается в браузерах. Однако их использование может потребовать дополнительных усилий на установку и настройку, а также наличие зарегистрированных DLL-библиотек на клиентской или серверной машине 1С.

Альтернативные и более надежные способы получения списков городов

Исходя из всех сложностей парсинга HTML, мы настоятельно рекомендуем использовать более надежные и структурированные источники данных. Это значительно упростит разработку, повысит стабильность вашего решения и снизит затраты на поддержку.

  1. Использование специализированных API геокодирования или списков городов

    Многие сервисы предоставляют готовые API (Application Programming Interface) для получения списков городов, регионов, стран и другой географической информации. Эти API обычно возвращают данные в форматах JSON или XML, что значительно упрощает их обработку в 1С. Примерами таких сервисов являются:

    • DaData.ru: Предоставляет API для стандартизации и получения адресной информации, включая города.
    • Google Maps API: Обладает мощным геокодером и другими сервисами, хотя для большого объема запросов может быть платным.
    • OpenStreetMap Nominatim: Бесплатный сервис для поиска географических объектов.
    • Геокодер Яндекс.Карт: Аналогично Google Maps API, предоставляет богатый функционал по работе с адресами и картами.

    Работа с такими API сводится к отправке HTTPЗапроса на нужный URL и последующей обработке полученного JSON/XML-ответа с помощью ЧтениеJSON или ЧтениеXML, что гораздо надежнее и проще, чем парсинг HTML.

    Пример HTTP-запроса к API, возвращающему JSON:

    
    // Этот код является концептуальным и требует настройки URL и параметров для конкретного API
    URLСервиса = "https://api.example.com/cities/russia"; 
    ПараметрыЗапроса = Новый Соответствие;
    ПараметрыЗапроса.Вставить("api_key", "ВАШ_API_КЛЮЧ"); 
    // Дополнительные параметры, если нужны (например, фильтр по региону)
    
    HTTPЗапросAPI = Новый HTTPЗапрос(URLСервиса, ПараметрыЗапроса); 
    HTTPЗапросAPI.УстановитьЗаголовок("Accept", "application/json"); // Указываем, что ждем JSON
            
    // В реальном коде необходимо настроить HTTPСоединение с учетом особенностей сервиса (SSL, порт и т.д.)
    СоединениеAPI = Новый HTTPСоединение("api.example.com",,,,,Новый ЗащищенноеСоединениеOpenSSL);
    ОтветAPI = СоединениеAPI.Получить(HTTPЗапросAPI);
    
    Если ОтветAPI.КодСостояния = 200 Тогда
        JSONСтрока = ОтветAPI.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
                
        ЧтениеJSON = Новый ЧтениеJSON;
        ЧтениеJSON.УстановитьСтроку(JSONСтрока);
        ДанныеJSON = ПрочитатьJSON(ЧтениеJSON); // ЧтениеJSON.Прочитать() и затем ПрочитатьJSON()
                
        // Предположим, ДанныеJSON - это Массив объектов, каждый из которых имеет свойство "Название"
        Для Каждого ГородДанные Из ДанныеJSON Цикла
            Сообщить(ГородДанные.Название);
        КонецЦикла;
    Иначе
        Сообщить("Ошибка получения данных: " + ОтветAPI.СтатусПричины);
    КонецЕсли;
    
  2. Федеральная информационная адресная система (ФИАС/ГАР)

    ФИАС (Федеральная информационная адресная система), теперь известная как ГАР (Государственный адресный реестр), является государственным источником структурированной и актуальной информации об адресах в России. Вы можете получить доступ к данным ФИАС/ГАР несколькими способами:

    • Загрузка полной базы данных: На официальном сайте ФИАС/ГАР доступны для скачивания полные базы данных в различных форматах (например, XML), которые можно загрузить и периодически обновлять в своей базе 1С. Это обеспечивает локальный и быстрый доступ к данным, но требует значительных ресурсов для хранения и обработки.
    • Сторонние сервисы и API: Существуют коммерческие и бесплатные сервисы, которые предоставляют более удобный API-доступ к данным ФИАС/ГАР, абстрагируя вас от необходимости прямого парсинга больших XML-файлов.

    Использование ФИАС/ГАР гарантирует высокую актуальность и полноту адресной информации.

  3. Предварительно подготовленные базы данных

    Многие разработчики и сообщества в интернете поддерживают актуальные списки городов России в виде файлов JSON, CSV или SQL-дампов. Вы можете найти такие файлы на платформах вроде GitHub или специализированных ресурсах. Преимущества этого подхода:

    • Одноразовая загрузка: Вы загружаете данные один раз и используете их локально.
    • Быстрый доступ: Данные хранятся в вашей базе 1С, обеспечивая мгновенный доступ без сетевых задержек.
    • Простота обработки: Форматы JSON/CSV легко читаются в 1С.

    Пример загрузки данных из JSON-файла:

    
    ИмяФайла = "C:\МоиДанные\cities_russia.json"; // Путь к вашему JSON-файлу
    ТекстФайла = Новый ТекстовыйДокумент;
    
    Попытка
        ТекстФайла.Прочитать(ИмяФайла, КодировкаТекста.UTF8);
        JSONСтрока = ТекстФайла.ПолучитьТекст();
    Исключение
        Сообщить("Ошибка чтения файла: " + ОписаниеОшибки());
        Возврат;
    КонецПопытки;
    
    ЧтениеJSON = Новый ЧтениеJSON;
    ЧтениеJSON.УстановитьСтроку(JSONСтрока);
    ДанныеJSON = ПрочитатьJSON(ЧтениеJSON); // Предполагаем, что файл содержит массив городов
    
    Если ТипЗнч(ДанныеJSON) = Тип("Массив") Тогда
        Для Каждого ГородДанные Из ДанныеJSON Цикла
            // Предполагаем, что каждый элемент массива - это Соответствие с ключом "name"
            Если ТипЗнч(ГородДанные) = Тип("Соответствие") Тогда
                Сообщить(ГородДанные.Получить("name"));
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;
    

    Недостатком является необходимость периодического обновления этой базы, чтобы города были актуальными (например, при переименованиях или появлении новых населенных пунктов).

  4. Веб-сервисы, предоставляющие JSON/XML (API конкретного ресурса)

    Иногда сам сайт, с которого вы хотите получить данные, может предоставлять неявные или документированные API, которые возвращают данные в структурированном формате (JSON, XML). Например, многие Вики-проекты имеют собственные API для программного доступа к их контенту. Перед тем как приступать к парсингу HTML, всегда проверяйте наличие такого API. Это самый предпочтительный вариант, так как он обеспечивает стабильный и структурированный доступ к данным.

Заключение и рекомендации

Мы выяснили, что ошибка "Непредвиденный символ при чтении JSON" при получении данных с сайта чаще всего означает, что вы получили HTML-код вместо ожидаемого JSON. Хотя в 1С существуют методы для парсинга HTML (строковые функции, регулярные выражения), они являются трудоемкими, хрупкими и неустойчивыми к изменениям в структуре сайта.

Наиболее эффективными и надежными методами для получения списков городов являются:

  1. Использование специализированных API геокодирования или списков городов (DaData, Google Maps API, Яндекс.Карты), которые предоставляют данные в JSON или XML.
  2. Обращение к данным ФИАС/ГАР.
  3. Загрузка и периодическое обновление предварительно подготовленных баз данных городов в форматах JSON/CSV.

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

← На главную