<img src="https://secure.leadforensics.com/133892.png" alt="" style="display:none;">

Зачем нужен парсинг в среде 1С?

Наверное, любой сотрудник службы маркетинга, имеющий дело с интернет-бизнесом, или аналитик, желающий знать положение своей фирмы на рынке или среди конкурентов, сталкивался с задачей сбора информации на различных сайтах по специфике своего бизнеса.

Обычно цель таких работ – мониторинг цен конкурентов, отслеживание новинок в ассортименте, получение новостей, сбор и аналитическая обработка любой другой информации, не являющейся интеллектуальной собственностью.

Когда за эти задачи берется «не айтишник», это превращается в часы и дни монотонной работы по копированию-вставке однотипной информации с сайтов, что не только само по себе плохо и не эффективно, но еще и приводит к ошибкам – можно вставить повторно одно и то же, случайно затереть или переместить вставленные данные и так далее.

А если эти данные необходимо ввести в 1С и связать с какими-то объектами учета, это требует дополнительных доработок для организации работы пользователя – новые формы, проверки ввода, выполнение нескольких обновлений конфигурации, трата времени на обучение пользователей и т.д.

В современных условиях перехода к цифровизации бизнеса и «четвертой промышленной революции» такой подход, конечно же, является тупиковым, не масштабируемым и не эффективным по всем параметрам. Поэтому необходимо внедрять решения, исключающие человеческий труд, который должен использоваться только для анализа собранных данных и другой «интеллектуальной» работы.

В общемировой практике такие задачи автоматического сбора данных из общедоступных источников существуют со времен появления Интернета и первых поисковых систем. Они называются «парсинг» или «скрапинг», а программы-парсеры можно реализовать на всех популярных языках и платформах. Примеры самых популярных инструментов для парсинга:

  • Python: BeautifulSoup, Scrapy
  • PHP: Simple HTML DOM, phpQuery
  • JavaScript: jQuery, Cheerio, Osmosis
  • Java: JSOUP, TagSoup
  • .NET: Html Agility Pack
  • C++ : htmlcxx, libhtml++

Так же их можно реализовать и на 1С, ведь в нем есть объекты HTTPСоединение, HTTPЗапрос и возможности работы со структурой html-документа (DOM). В сочетании с регламентными заданиями можно реализовать периодический парсинг без участия человека, а совместно с http-сервисами можно организовать еще и публикацию собранных данных внутри сети предприятия – для обмена между разными базами 1С или другими информационными системами.

Новые возможности в платформе

Начиная с версии 8.3.13 в 1С появились новые функции, упрощающие работу с html-документами, это, в частности, поиск по фильтрам, аналогичный отбору по селекторам в jQuery и перечисленных выше библиотеках. Это позволяет парсить веб-страницы на 1С, применяя всего лишь базовые основы CSS. Не нужно изучать и мучиться с XPath, перебором элементов или полноценно погружаться в веб-фреймворки и незнакомые технологии!

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

Все новые функции с кратким описанием:

Функция

Краткое описание

ДокументHTML.УдалитьПоКатегории()

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

ДокументHTML.ИзвлечьТолькоТекст()

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

ДокументHTML.НайтиПоФильтру()

Функция поиска элементов по селекторам и тегам, основной инструмент парсинга

ДокументHTML.УдалитьПоФильтру()

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

Пример задачи парсинга

Рассмотрим практический пример задачи извлечения данных. Допустим, мы хотим написать свою конфигурацию для учета инвестиций в акции и другие инструменты. Но вся необходимая информация хранится на разных сайтах или доступна только через REST API. Придется парсить разные источники, чтобы получать данные об инструментах, новостях, показателях компаний и т.д. Преимущество реализации на 1С в том, что мы можем написать несколько парсеров для разных источников и представить сводку в удобном виде, используя преимущества быстрой разработки на 1С – базу данных, конструктор форм и все другие средства.

Возьмем за основу и начальную точку сайт «Тинькоф-инвестиции» и проанализируем структуру страницы с информацией о акциях – на примере Apple https://www.tinkoff.ru/invest/stocks/AAPL/. Выделим блоки с нужной информацией и их CSS-селекторы:

Блок информации

Селектор

Название акции

Класс «SecurityHeaderPure__showName_1DvWf»

О компании

Класс «SecurityInfoPure__info_3Mgld», тег <p> внутри

Сайт компании

Класс «SecurityInfoPure__link_2lmEC», тег <SPAN> внутри него

Логотип компании

Класс «Avatar__image_3KvrS», ссылка в описании стиля: свойство background-image

Здесь заметно, что имена классов имеют суффикс со случайным набором символов, но это мы легко обойдем.

Проверить корректность отбора можно в консоле разработчика любого браузера (кнопка F12), используя функции document.getElementsByClassName(), document.getElementById(), document.getElementsByTagName() или отбор через jQuery, который все еще часто применяется на многих сайтах: если вызов функции «$» не выдаст ошибки, можно проверять отборы по селекторам в виде $(“.classname”), $(“#element-id”) и т.д.

Создадим код для получения текста страницы:

Заголовки = Новый Соответствие;

Заголовки.Вставить("Origin", "https://www.tinkoff.ru");

Заголовки.Вставить("Referer", "https://www.tinkoff.ru/invest/stocks/"+ВРег(Тикер)+"/");

Заголовки.Вставить("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36");

Запрос = Новый HTTPЗапрос("/invest/stocks/AAPL/", Заголовки);

Соединение = Новый HTTPСоединение("www.tinkoff.ru",,,,,, Новый ЗащищенноеСоединениеOpenSSL);

Ответ = Соединение.Получить(Запрос);

ТекстОтвета = Ответ.ПолучитьТелоКакСтроку();

Теперь подготовим объект документа для анализа и получения данных:

ЧтениеHTML = Новый ЧтениеHTML;

ЧтениеHTML.УстановитьСтроку(ТекстОтвета);

Построитель = Новый ПостроительDOM;

Док = Построитель.Прочитать(ЧтениеHTML);

Док.НормализоватьДокумент();

Дальше сформируем фильтр для передачи в функцию поиска по документу. Фильтр это конфигурация отбора в виде JSON-строки, и у нее есть два корневых элемента – type и value. Есть несколько типов и для каждого - несколько допустимых значений, полное описание вариантов есть в синтаксис-помощнике в разделе «Описание структуры JSON-конфигурации для отбора».

Такую структуру может быть неудобно каждый раз формировать вручную, если парсинг многоступенчатый и сложный, поэтому лучше сразу вынести это действие в отдельную функцию и пользоваться многократно.

В нашем случае – реализуем функцию для формирования фильтра поиска по тегу и классу элемента, для этого нужно выбрать тип «пересечение условий» («intersection») и добавить  значение «value» как массив из трех условий – на равенство тега, наличие атрибута «class» и поиск по шаблону для значения атрибута:

Функция СформироватьФильтрПоКлассуТега(Тег, Класс, ТочноеСоответствие = Истина)  

   ВидПоиска = ?(ТочноеСоответствие = Истина, "valueequals", "valuematchesregex");

   ИмяКласс = ?(ТочноеСоответствие = Истина, Класс, Класс+".+");

   ПараметрыПоиска = Новый Структура("type,value", "intersection", Новый Массив);

      ПараметрыПоиска.value.Добавить(Новый Структура("type,value", "elementname", Новый Структура("value,operation", Тег, "equals")));

      ПараметрыПоиска.value.Добавить(

         Новый Структура("type,value", "hasattribute",

            Новый Структура("value,operation", "class", "nameequals")));

      ПараметрыПоиска.value.Добавить(Новый Структура("type,value","hasattribute",

         Новый Структура("value,operation", ИмяКласс,ВидПоиска)));

      Запись = Новый ЗаписьJSON;

      Запись.УстановитьСтроку();

      ЗаписатьJSON(Запись, ПараметрыПоиска);

      Фильтр = Запись.Закрыть();

      Возврат Фильтр;

КонецФункции

Здесь в зависимости от флага «ТочноеСоответствие» используется два варианта отбора – точное равенство или проверка по регулярному выражению. Это позволяет задавать сложные шаблоны поиска селекторов. В нашем случае при неточном соответствии в переменной ИмяКласс помещаем переданное значение и добавляем символы «.+», что означает «1 или более любых символов» после указанного значения.

Теперь мы можем формировать любые фильтры для выбора элементов и извлекать из них информацию. Оформим парсинг в виде функции, возвращающей структуру с полученными данными страницы:

Функция ПарсингСтраницыАкции(Док)

Результат = Новый Структура("Ошибка,Результат", Ложь);

Данные = Новый Структура("ОписаниеКомпании, СайтКомпании, СсылкаНаКартинку");

// Описание фирмы

Фильтр = СформироватьФильтрПоКлассуТега("div","SecurityInfoPure__wrapper_", Ложь);

МассивУзлов = Док.НайтиПоФильтру(Фильтр);

Если МассивУзлов.Количество() > 0 Тогда

              Узел = МассивУзлов[0];

              УзелТекстОписания = Узел.ДочерниеУзлы[0];

              Данные.ОписаниеКомпании = УзелТекстОписания.ТекстовоеСодержимое;

              Если Узел.ДочерниеУзлы.Количество() > 1 Тогда

                      УзелСсылка = Узел.ДочерниеУзлы[1];

                      Данные.СайтКомпании = УзелСсылка.ПервыйДочерний.Гиперссылка;

              КонецЕсли;           

КонецЕсли;

Результат.Результат = Данные;

Возврат Результат;

КонецФункции

Здесь мы сочетали получение узла по селектору и получение дочерних узлов из DOM-структуры вместо нескольких выборок по селектору. В простых случаях это может быть удобнее.

Парсинг картинок

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

Используем нашу функцию и найдем узел с картинкой, затем извлечем значение его атрибута «style», убрав из начала строки имя CSS-свойства «background-image» и строку «static.tinkoff.ru/».

После этого получим картинку как двоичные данные обычным GET-запросом и сохраним во временное хранилище:

// Картинка

Фильтр = СформироватьФильтрПоКлассуТега("span","Avatar__image_",Ложь);

МассивУзлов = Док.НайтиПоФильтру(Фильтр);

Если МассивУзлов.Количество() > 0 Тогда

   Узел = МассивУзлов[0];

   Для каждого Атр Из Узел.Атрибуты Цикл

      Если Атр.ИмяУзла = "style" Тогда

         АдресКартинки = Сред(МассивУзлов[0].Атрибуты[1].ЗначениеУзла, 41);

         АдресКартинки = Лев(АдресКартинки, СтрДлина(АдресКартинки)-1);

         Запрос = Новый HTTPЗапрос(АдресКартинки, Заголовки);

         Соединение = Новый HTTPСоединение("static.tinkoff.ru",,,,,, Новый ЗащищенноеСоединениеOpenSSL);

         Ответ = Соединение.Получить(Запрос);

         Картинка = Ответ.ПолучитьТелоКакДвоичныеДанные();

         Данные.СсылкаНаКартинку = ПоместитьВоВременноеХранилище(Картинка, УникальныйИдентификатор);                         

      КонецЕсли;

   КонецЦикла;

КонецЕсли;

Ресайз картинки

Добавим еще один шаг, не относящийся к парсингу, но который поможет при выводе списка ценных бумаг на форме – уменьшим полученное изображение до размера 36*36, чтобы можно было поместить его в динамический список. Для этого используем встроенный в систему компонент «WIA.ImageFile» и его возможности трансформации изображений. Для удобства напишем функцию, которой можно передать, как ранее полученную ссылку на картинку во временном хранилище, так и двоичные данные:

Функция СгенерироватьМиниатюру(СсылкаНаКартинку = "", ДвДанные = Неопределено, УИД)

   // сохраняем в файл для обработки

   Если ЭтоАдресВременногоХранилища(СсылкаНаКартинку) Тогда

      Картинка = ПолучитьИзВременногоХранилища(СсылкаНаКартинку);

   Иначе

      Картинка = ДвДанные;

   КонецЕсли;

   ВрФайл = ПолучитьИмяВременногоФайла("png");

   Картинка.Записать(ВрФайл);

   Изобр = Новый COMОбъект("WIA.ImageFile");

   Изобр.LoadFile(ВрФайл);  

   ОбработкаИзобр = Новый COMОбъект("WIA.ImageProcess");

   ОбработкаИзобр.Filters.Add(ОбработкаИзобр.FilterInfos("Scale").FilterID);

   ОбработкаИзобр.Filters(1).Properties("MaximumWidth").Value = 36;

   ОбработкаИзобр.Filters(1).Properties("MaximumHeight").Value = 36;

   // сохраняем рузультат и возвращаем адрес

   ВрФайл = ПолучитьИмяВременногоФайла("png");

   Изобр = ОбработкаИзобр.Apply(Изобр);

   Изобр.SaveFile(ВрФайл);

   Возврат ПоместитьВоВременноеХранилище(Новый ДвоичныеДанные(ВрФайл), УИД);

КонецФункции

В результате мы получим такой список акций:

В результате мы получим такой список акций:

Таким образом, мы получили данные из определенных блоков страницы: текст, ссылку и изображение, которые теперь можем использовать для создания объекта «Ценная бумага» и сохранить его в разработанной конфигурации:

Парсинг = ПарсингСтраницыИнструмента(Тикер, ДанныеПоИнструменту.Результат.Вид, ГУИД);

О = Справочники.ЦенныеБумаги.СоздатьЭлемент();

ЗаполнитьЗначенияСвойств(О, ДанныеПоИнструменту.Результат);

О.Наименование        = Тикер;

О.ОписаниеКомпании    = Парсинг.Результат.ОписаниеКомпании;

О.СайтКомпании        = Парсинг.Результат.СайтКомпании;

О.Картинка            = Новый ХранилищеЗначения(ПолучитьИзВременногоХранилища(

                              Парсинг.Результат.СсылкаНаКартинку));

О.КартинкаМал         = Новый ХранилищеЗначения(ПолучитьИзВременногоХранилища(

                              Парсинг.Результат.СсылкаНаМалКартинку));

О.Записать();

УдалитьИзВременногоХранилища(Парсинг.Результат.СсылкаНаКартинку);

УдалитьИзВременногоХранилища(Парсинг.Результат.СсылкаНаМалКартинку);

Дальнейшее развитие парсинга средствами 1С

Парсинг динамических сайтов

В последнее время получили большое распространение сайты на основе фреймворков React, Vue, Angular, суть которых – построение и модификация структуры DOM-документа на стороне браузера, так называемые Single Page Application (SPA). Проблема парсинга таких сайтов в том, что в ответ на прямой http-запрос приходит только «скелет» сайта из одного тега DIV и большой объем скриптов, которые формируют всю структуру, используя движок браузера.

Для обхода этой проблемы используются так называемые headless-браузеры, которые могут исполнять скрипты и получать конечный сформированный документ, в node.js это, например, пакеты Nighmare или Puppeeter. Либо можно использовать инструмент для тестирования веб-страниц Selenium.

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

Можно предположить, что автоматизировать этот процесс можно с применением Vanessa Automation, но пока на практике я это не проверял.

Массированный парсинг

При необходимости обрабатывать большое количество страниц или делать это с определенной периодичностью, можно задействовать механизм регламентных заданий и выполнять сбор информации многопоточно и/или по расписанию. Этот механизм должен использоваться и в случае ограничений у сайта на число запросов – при этом можно сочетать регламентное задание с периодической сменой прокси, создав для их учета отдельный регистр.

Вынос парсера в расширение

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

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

Заключение

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

Обсудить

Вас может заинтересовать

Компании, которые собираются приступить к подготовке отчетности по МСФО нередко сталкиваются с выбором информационного продукта. Чтобы минимизировать риски и получить максимально эффективный инструмент для решения своих задач, эксперты Columbus выбрали наиболее популярные программные продукты фирмы «1С»: «1С:УХ» и «1С:ERP» и провели сравнительный анализ функциональных возможностей данных систем на примере работы c отчетами по МСФО.
С каждым годом по всему миру растёт число предприятий, которые анализируют свою деятельность по стандартам международной финансовой отчетности. Чтобы управлять активами, привлекать заемные средства, оценивать доходность капиталовложений, рисков и гарантий для успешного ведения бизнеса, компании основываются на стандартах МСФО.
Наиболее важной и приоритетной задачей любой компании является автоматизация договорной работы. Именно договор содержит ключевую информацию для финансового управления предприятием. В данной статье раскрыты особенности учета авторских договоров и договоров гражданско-правового характера в зарплатном модуле в системе 1С:ERP.
У каждого предприятия есть свои особенности автоматизации, уникальные требования, бизнес-процессы, связанные, в том числе и с отраслевой принадлежностью.
Зачастую в компании, которая уже работает в системе «1С:ERP Управление предприятием» (или «1С:Комплексная автоматизация», или «1С:Управление торговлей») или только собирается ее внедрить, возникают вопросы:
right-arrow share search phone phone-filled menu filter envelope envelope-filled close checkmark caret-down arrow-up arrow-right arrow-left arrow-down