Парсинг товаров сайта средствами 1С 8

Опубликовал Павел Опарин (opx) в раздел Обмен - Интеграция с WEB

В этой статье я постараюсь описать процесс парсинга сайтов средствами 1С с примером. Это статья не является инструкцией к применению, а лишь демонстрирует возможности 1С.

Что мы имеем?

   1. Сайт в интернете, на котором располагается список товаров. В моем случае – это интернет магазин салона «Связной»
   2. Понимание основ сайтостроения… хотя бы знание HTML тегов
   3. Умение кодить в 1С 8

Все вышеперечисленные пункты в арсенале? Тогда читаем далее

Для начала парсинга стоит определиться с тем что мы хотим спарсить и какая у нас будет иерархия. В моем случае это - категория сотовых телефонов. Верхний уровень иерархии будет - производители. Почему именно так? Потому что я так захотел. Вы же вправе использовать любую иерархию. Далее нам будут интересны такие поля как: Наименование, Цена, Картинка и Описание... ну и пожалуй захватим операционную систему, чтобы пример получился более наглядным.

  1. Создаем внешнюю обработку. Те, кто не знают как это сделать - дальше могут не читать
  2. Создаем форму обработки с командной панелью снизу и сверху (они могут быть полезными)
  3. Размещаем на ней Панель и обзываем первую страницу "СамСайт"
  4. Кладем на страницу "СамСайт" ПолеHTMLДокумента и обзываем его к примеру "Сайт"
  5. Переименовываем кнопку "Выполнить", которая находится на нижней панели в "Загрузить сайт"
  6. Описываем процедуру нажатия на эту кнопку так:
ЭлементыФормы.Сайт.Перейти("http://www.svyaznoy.ru/catalog/phone/224"); //Категория с мобильными телефонами
  1. Проверяем работу нашей обработки. У меня появился сайт связного. А у Вас?


Дальше сложнее. Все еще хочешь парсить сайты? Тогда читай.

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

  1. Создадим табличную часть "Производители" с реквизитами "Отметка" (Булево), "Наименование" (Строка 100) и "Ссылка" (Строка 300).
  2. Добавляем еще одну страницу на панели и обзываем ее "Производители"
  3. Размещаем на этой странице одноименную табличную часть
  4. Добавляем на нижней панели кнопку "Заполнить производителей" с кодом:
Для Каждого Стр из ЭлементыФормы.Сайт.Документ.body.all Цикл
    Если Стр.tagName = "H1" и Стр.innerText = "Производители" Тогда
        Для Каждого опСтр из Стр.nextSibling.children Цикл
            новСтр = Производители.Добавить();
            новСтр.Наименование = опСтр.innerText;
            новСтр.Ссылка = опСтр.firstChild.href;
        КонецЦикла;
        Возврат;
    КонецЕсли;
КонецЦикла;

   Здесь напрашиваются небольшие пояснения:
   tagName - имя HTML тега в HTML документе
   nextSubling - следующий элемент HTML документа от текущего
   children - список дочерних элементов
   firstChild - первый дочерний элемент от текущего

  1. Проверяем. При проверке важно находиться на странице "СамСайт", чтобы заполнять производителей


Производители заполнены. Теперь к самим телефонам

  1. Создаем табличную часть "Товары" с реквизитами "Производитель" (Строка 100), "Наименование" (Строка 100), "Цена" (Число 10,2), "Картинка" (Строка 300), "Описание" (Строка Неограниченная), "ОС" (строка 100), "Ссылка" (Строка,300)
  2. Добавляем еще одну страницу на панели и обзываем ее "Товары"
  3. Размещаем на этой странице одноименную табличную часть
  4. Добавляем на нижней панели кнопку "Заполнить товары" с кодом:
Для Каждого Стр из Производители Цикл
    //Если отметку сняли - то не трогаем этого производителя
    Если Не Стр.Отметка Тогда
        Продолжить;
    КонецЕсли;
    Форма = ПолучитьФорму("ФормаТоваров");
    Форма.ЭлементыФормы.Сайт.Перейти(Стр.Ссылка);
    Форма.ТекущийПроизводитель = Стр;
    Форма.ОткрытьМодально();
КонецЦикла;
  1. Создаем форму обработки "ФормаТоваров"
  2. Кладем на "ФормаТоваров" ПолеHTMLДокумента и называем его "Сайт"
  3. На событие ДкументСформирован у ПоляHTMLДокумента пишем код:
Если ЭлементыФормы.Сайт.Документ.body.all.length>1 Тогда
    ГрузимТовары();
КонецЕсли;
  1. Создаем переменную в модуле формы
Перем ТекущийПроизводитель Экспорт;
  1. Создаем процедуру ГрузимТовары():
Процедура ГрузимТовары()
    Для Каждого Стр из ЭлементыФормы.Сайт.Документ.body.all Цикл
        Если Стр.className = "ct_desc cleared" Тогда
            новСтр = Товары.Добавить();
            Для Каждого опСтр из Стр.children Цикл
                Если опСтр.className = "pic_and_comp" Тогда
                    новСтр.Картинка = СтрЗаменить(Сред(опСтр.firstChild.style.backgroundImage,5),")","")
                КонецЕсли;
                Если опСтр.className = "name" Тогда
                    новСтр.Наименование = опСтр.innerText;
                    новСтр.Ссылка = опСтр.firstChild.href;
                КонецЕсли;
                Если опСтр.className = "price" Тогда
                    новСтр.Цена = Число(СтрЗаменить(СтрЗаменить(опСтр.innerText,"-","")," ",""));
                КонецЕсли;
                Если опСтр.className = "desc" Тогда
                    новСтр.Описание = опСтр.innerText;//опСтр.innerHTML - если нужно вместе с тегами
                КонецЕсли;
            КонецЦикла;
        КонецЕсли;
    КонецЦикла;
    Закрыть();
КонецПроцедуры
  1. Проверяем. Все работает.

Дело осталось за "операционной системой" и еще надо загрузить картинки. Давайте по порядку. Чтобы получить "ОС" нам надо открыть этот товар и считать "ОС" оттуда. Для этого делаем следующее:

  1. Добавляем на нижней панели кнопку "Доп Инфо" с кодом:
Для Каждого Стр из Товары Цикл
    Форма = ПолучитьФорму("ФормаДопИнфо");
    Форма.ЭлементыФормы.Сайт.Перейти(Стр.Ссылка);
    Форма.ТекущийТовар = Стр;
    Форма.ОткрытьМодально();
КонецЦикла;
  1. Создаем форму обработки "ФормаДопИнфо"
  2. Кладем на "ФормаДопИнфо" ПолеHTMLДокумента и называем его "Сайт"
  3. На событие ДкументСформирован у ПоляHTMLДокумента пишем код:
Если ЭлементыФормы.Сайт.Документ.body.all.length>1 Тогда
    ГрузимДопИнфо();
КонецЕсли;
  1. Создаем переменную в модуле формы
Перем ТекущийТовар Экспорт;
  1. Создаем процедуру ГрузимДопИнфо():
Процедура ГрузимДопИнфо();
    Для Каждого Стр из ЭлементыФормы.Сайт.Документ.body.all Цикл
        Если Стр.className = "card_spec" Тогда
            Для Каждого опСтр из Стр.children Цикл
                Если Найти(опСтр.innerText,"Операционная система:") Тогда
                    ТекущийТовар.ОС = СокрЛП(СтрЗаменить(опСтр.innerText,"Операционная система:",""));
                КонецЕсли;
            КонецЦикла;
        КонецЕсли;
    КонецЦикла;
    Закрыть();
КонецПроцедуры
  1. Проверяем и переходим к последнему пункту


Заметили как похожи две последние инструкции? То-то же. Стремился к универсальности. Ну и наконец последний этап - сохраним все изображения к примеру на диск "С" в папку "Svyaznoy". Поехали:

  1. Добавляем на нижней панели кнопку "Сохранить Картинки" с кодом:
Для Каждого Стр из Товары Цикл
    путьСамФайл = Лев(Стр.Картинка,Найти(Стр.Картинка,".jpg/") + 3);
    самФайл = СтрЗаменить(СтрЗаменить(ПутьСамФайл,"http://static.svyaznoy.ru/upload/iblock/",""),"/","");
    Стр.Картинка = СохранитьКартинкуСайта("C:\Svyaznoy",ПутьСамФайл,СамФайл);
КонецЦикла;
  1. Добавляем функцию СохранитьКартинкуСайта:
Функция СохранитьКартинкуСайта(КаталогСохранения,КартинкаНаСайте,КартинкаУНас)
    ИмяФайлаКартинки = КаталогСохранения + "\" + КартинкаУНас;
    ГетЗапрос = Новый COMОбъект("WinHttp.WinHttpRequest.5.1");
    ГетЗапрос.SetTimeouts(10000, 10000, 10000, 10000);
    БазовыйУРЛ = КартинкаНаСайте;
    Хидер1 = "Content-Type";
    Хидер2 = "image/jpg"; // Тип рисунка.
    ГетЗапрос.Open("GET", БазовыйУРЛ, False); // Синхронный режим.
    ГетЗапрос.setRequestHeader(Хидер1, Хидер2);
    ГетЗапрос.Send();
    СтатусОтправки = ГетЗапрос.status;
    Если СтатусОтправки <> 200 Тогда
        Сообщить("Ошибка отправки запроса на: "
                 + КартинкаНаСайте);
        Возврат "";
    КонецЕсли;       

    Стрим = Новый COMОбъект("ADODB.Stream");
    Стрим.Mode = 3;
    Стрим.Type = 1;
    Стрим.Open();
    Стрим.Write(ГетЗапрос.responseBody);

    Стрим.SaveToFile(ИмяФайлаКартинки, 2);
    Стрим.Close();
    Возврат ИмяФайлаКартинки;
КонецФункции

На этом наша эпопея с парсингом закончена. Это всего лишь пример того, как это можно сделать. Приложив сюда немного своего кода - можно сделать парсер для любого сайта.

Имея парсер 1С - я могу спарсить все, кроме этого парсера. Имея два парсера 1С - я могу спарсить все :)

Скачать файлы

Наименование Файл Версия Размер
Обработка парсинга
.epf 15,72Kb
02.08.11
490
.epf 15,72Kb 490 Скачать

См. также

Комментарии
1. Павел Опарин (opx) 426 02.08.11 18:17 Сейчас в теме
Комментарии можете и сюда писать, но на своем блоге я их читаю чаще
2. Ийон Тихий (cool.vlad4) 41 02.08.11 18:36 Сейчас в теме
Я честно говоря 1С в этом плане невзлюбил...использую C# + htmlagilitypack(для более сложных случаев Watin)+ разбор xml (xpath/xslt или даже xquery- особенно удобно в качестве хранимых процедур в ms sql)...для пользователей делаю как правило консольные утилитки - url строка - и через stdin получение результата/либо через файл/либо через базу.
3. Ийон Тихий (cool.vlad4) 41 02.08.11 18:40 Сейчас в теме
Что для этого может понадобится....fiddler(локальный прокси, перехватывающий запросы для дебаггинга ), любой преобразователь html-xml(главное потом не забыть делать тесты именно на конечном xml, а не на html)-tidy,sgmlreader, htmlagilitypack и т.д. Что еще, какая-нибудь xpath тулза, их много...
4. BigB (BigB) 156 02.08.11 21:30 Сейчас в теме
Вот тут http://infostart.ru/public/61194/ парсер JSONа. Может чем и пригодится.
5. Алексей Константинов (alexk-is) 6088 02.08.11 22:02 Сейчас в теме
6. Павел Опарин (opx) 426 02.08.11 22:41 Сейчас в теме
(5)Это вы про то, что надо было код подкрасить? Просто лень качать и устанавливать что-то. Было бы здорово, если б разработчики добавили такой функционал прямо в редактор на сайте
7. Павел Опарин (opx) 426 02.08.11 22:42 Сейчас в теме
(4) И казалось бы... Причем тут крокодилы :) ) Без обид, но это совсем другая тема
8. Яков Коган (Yashazz) 1977 02.08.11 22:48 Сейчас в теме
Ну и что? Очередной примитивный парсинг, частный случай. Такого тут бывало дофига.
Причём волшебная аббревиатура DOM тут так и не прозвучала. А жаль, я было понадеялся на настоящий обзор.
9. Павел Опарин (opx) 426 03.08.11 01:38 Сейчас в теме
(8) Что по-вашему должно было войти в настоящий обзор?
10. Павел Опарин (opx) 426 03.08.11 10:43 Сейчас в теме
Если у кого-нибудь получилось по этой инструкции спарсить сайт - напишите. Будет интересно
11. Freest (freest) 03.08.11 11:47 Сейчас в теме
Когда-то давно писал парсер, цепляя эксплорер через COM соединение и пользуя его внутренний парсер.. скорость парсинга удручающая. С выходом 8.2 переписал, используя ЧтениеHTML и ПостроительДОМ, скорость возросла в десяток раз.

К чему я все это.. Я правильно понимаю, что ПолеHTMLДокумента - это фактически IE? И парсинг идет встроенным парсером эксплорера?
12. Павел Опарин (opx) 426 03.08.11 12:01 Сейчас в теме
(11) Интересная тема про DOM. Может опишите свои методы парсинга?
13. Ийон Тихий (cool.vlad4) 41 03.08.11 12:06 Сейчас в теме
(11) Да ПолеHtmlДокумента это обертка над IE. IE - это не один activex, а целый зоопарк. Часто используют shdocvw.dll и mshtml.dll. Первая это широко любимый, но медленный WebBrowser , а второй синтаксический html анализатор. Фиг знает, чего там в 8.2 быстрее, но терзают смутные сомнения, что 1С до сих пор эксплуатирует IE, возможно добавили свои примочки. Насчет быстродействия - а технология com никогда не отличалась быстротой. Здесь надо понимать задачи - если парсинг простейший как в описанной статье, то использовать браузер нецелесообразно, делайте get запрос, получайте html - трансформируйте в xml - затем получайте свою информацию (для данного случая - связного обход товаров через //div[contains(@class,'ct_el') ] затем /div[@class='name'] и т.д.) . Браузер нужен, когда используются более сложные технологии, которые другим путем не получить, например ajax.
14. Freest (freest) 03.08.11 12:20 Сейчас в теме
(12) Я не являюсь экспертом в этой области, по этому не претендую на максимально оптимальный в плане быстродействия алгоритм. Но все же опишу. HTML код страницы получаю через HTTPСоединение

Соединение = Новый HTTPСоединение(Сервер);
Соединение.Получить(Адрес,ИмяФайла);

Далее полученный код загружаем в ЧтениеHTML.

ЧтениеХТМЛ = Новый ЧтениеHTML();
ЧтениеХТМЛ.ОткрытьФайл(ИмяФайла);

Ну и далее загружем в 1с-ный парсер xml и html - ПостроительDOM.

ПостроительДОМ = Новый ПостроительDOM;
ДокументХТМЛ = ПостроительДОМ.Прочитать(ЧтениеХТМЛ);

Работа с построителемДОМ схожа с работой парсера ИЕ, методы читать в хелпе. (Та же работа с коллекциями тегов - перебор, получение именованных тегов и т.д.)


(13) Все-таки построительDOM - встроенный парсер html и xml, имхо, IEшный он не использует.
15. Павел Опарин (opx) 426 03.08.11 12:20 Сейчас в теме
(13) А что, если сайт использует куки? Как например их использует связной для подстановки города. А надо по другим городам парсить. Только браузером.
16. Freest (freest) 03.08.11 12:29 Сейчас в теме
(15) Куки можно передавать 3м параметром "заголовки" у HTTPСоединение:

Соединение.Получить(Адрес,ИмяФайла,Заголовки);
17. Ийон Тихий (cool.vlad4) 41 03.08.11 12:30 Сейчас в теме
(15) куки передаются протоколом http, любой клиент http может передать куки, необязательно браузер.
(14) а пруфлинки по поводу Построителя есть или это догадки?
18. Ийон Тихий (cool.vlad4) 41 03.08.11 12:33 Сейчас в теме
(14) Он доступен под сервером, так, что по всей видимости они написали наконец-то свой, потому и быстрее. А работа похожа на IE , также как и на Mozzila , также как и т.п. DOM он везде DOM
19. Freest (freest) 03.08.11 12:38 Сейчас в теме
(18) Ну и хорошо, потому что пруфлинков не было у меня, только личные ощущения ;)
20. makeya 05.08.11 13:45 Сейчас в теме
отличная обработка! хоть по мнению некоторых это и "примитивный парсинг", но меня натолкнуло на множество идей (хоть раньше и стояла задача по парсингу информации с сайта, парсить это средствами 1С даже в голову не приходило!).
спасибо автору за такое подробное описание и приятный код :)
21. Ийон Тихий (cool.vlad4) 41 05.08.11 14:13 Сейчас в теме
22. makeya 05.08.11 14:30 Сейчас в теме
(21) Просто идеи по созданию подобной обработки для решения своих задач (едва ли это касается этой ветки), достаточно удобно делать это средствами 1С, чтоб потом сразу же без лишних обменов данными и т.д. использовать собранную информацию. Хотя то, что вы описали во втором комментарии тоже чудно.
23. Павел Опарин (opx) 426 05.08.11 16:05 Сейчас в теме
(22) Все верно. Эта обработка и была сюда выложена для того, чтоб у людей появились собственные идеи и чтоб не делать всякие обмены и т.д.
24. Alexandr (maloi_a) 08.08.11 09:34 Сейчас в теме
Замечание.
Процедуру для кнопки "Заполнить производителей"
надо начать с
Производители.Очистить();
25. Павел Опарин (opx) 426 08.08.11 10:49 Сейчас в теме
26. Вячеслав (Medvedik) 19.08.11 09:41 Сейчас в теме
Попробую использовать материал для написания парсера сайта конкурентов :)
Сдернуть у них описание и пикчи (в свой стартап, да такой я редис), в дальнейшем - мониторить цены (тут еще яндекс.маркет нужно будет анализировать, но это другая тема).
От меня +
27. Павел Опарин (opx) 426 19.08.11 15:28 Сейчас в теме
(26) Я тоже думал о похожем стартапе. По идеи можно выдавать по 2-3 готовых интернет магазина в неделю. Главное наладить процесс. А, скажем, через год иметь более 100 сайтов и что-то да продавать. Вот такой вот я дорвейщик :)
28. игорь ром (djd.sf) 19.08.11 17:47 Сейчас в теме
(26)Это не Вы ли парсите, что яндекс не работает? :D
29. Павел Опарин (opx) 426 20.08.11 00:14 Сейчас в теме
30. ac86 (Aleanza) 25.08.11 17:59 Сейчас в теме
А работает ли это чудо с установленным ИЕ9?
31. Павел Опарин (opx) 426 26.08.11 15:01 Сейчас в теме
(30) Я не знаю. По идеи - ничто не должно препятствовать этому. Попробуйте, чтобы знать наверняка
33. Евгений Тимошенко (morfeus) 05.10.11 09:19 Сейчас в теме
Этот парсер только для одного сайта, а я делал для любого количества сайтов (Логика была в настройке закономерностей на основе тегов)
34. Павел Опарин (opx) 426 05.10.11 18:19 Сейчас в теме
(33)Я тоже сначала думал сделать универсальным с логикой на тегах, но это бы ограничило количество возможных сайтов для парсинга. Хотя может я и ошибаюсь. Я же не вижу вашей обработки.
Да и в целом - парсингом занимаются в основном программисты, а не конечные пользователи. Все равно код править.
35. Антон Шевелёв (Anton_prezident) 12.02.12 16:00 Сейчас в теме
Спасибо,теперь ясно как работать в 1с с парсингом сайта
36. Артем Боровлев (borrman) 28.03.12 23:40 Сейчас в теме
А парсинг просто с использованием рег. выражений не будет работать быстрее? Или с ними будет больше мороки, чем реальной пользы?
37. Павел Опарин (opx) 426 29.03.12 11:30 Сейчас в теме
(36)Быстрее может и будет. Я не проверял. Меня вполне устраивает этот способ парсинга. Если наловчиться и использовать такие инструменты как firebug в firefox'е, 1с ну и конечно же мой подход - парсинг одного сайта с заливкой базу данных 1с займет около 3-4 часов. Регулярные выражения - для меня тема далекая (пользовался всего пару раз), да и штатных средств у 1с для работы с ними нет. А костыли я не люблю использовать.
А так конечно каждому самому выбирать. Что удобно - тем и пользоваться.
38. Анатолий Бычин (tolyan_ekb) 85 02.05.12 13:51 Сейчас в теме
Плюсанул. Понятное введение и пример. Интересно было бы почитать про парсинг многостраничных сайтов с переходами внутри страничек.
39. Сергей Космачев (ksnik) 224 15.08.12 20:39 Сейчас в теме
(38) tolyan_ekb, в открытой обработке http://infostart.ru/public/147622/ поддерживается многостраничность!
Только заметил эту статью, благодарю автора!
40. alexey klimkin (alexlights) 18.08.12 21:43 Сейчас в теме
Хороший парсер. На основании этой обработки написал бота для браузерной игры :)
41. Max Bykov (Masich) 16.09.12 13:29 Сейчас в теме
Спасибо.
Очень полезная статья для "входа в тему".
42. porfirius 25.06.13 13:21 Сейчас в теме
Спасибо, попробуем поработать в 1с с парсингом сайта
43. Евгений Пестов (RBEvgenyPN) 3 02.09.13 18:32 Сейчас в теме
Добрый день. Скачал, попробовал, не работает. Не грузит нечего. Предположил что сайт немного видоизменили.
Немного подправил, получилось загрузить Производителей. После чего столкнулся с проблемкой.
Кто нибудь может мне растолковать как работает:"ЭлементыФормы.Сайт.Документ.body.all.length>1" на событие ДкументСформирован у ПоляHTMLДокумента. Почему то у меня пишет : "Поле объекта не обнаружено (length)". Что я делаю не так?
44. Anton Popov (sunlit) 29.10.13 01:21 Сейчас в теме
RBEvgenyPN тоже скачал и тоже не работает - расскажи, что поправил.
45. Дмитрий Гомзин (plevakin) 12.12.13 10:12 Сейчас в теме
(43) RBEvgenyPN, не то, что ЭлементыФормы.Сайт.Документ.body.all.length не работает, а даже на all ругается. Заменил на Для Каждого Стр из ЭлементыФормы.Сайт.Документ.body.childNodes Цикл вроде пошло дело, но стало ругаться на следующую строчку, пришлось добавить заглушку
Попытка рТипУзла=Стр.nodeType Исключение рТипУзла=0 КонецПопытки;
Если рТипУзла<>1 Тогда Продолжить КонецЕсли;
Но производителей все равно не грузит, видимо действительно на сайте все стало по другому и тегов H1 там больше нет.
46. Максим Кузнецов (Makushimo) 149 10.01.14 07:58 Сейчас в теме
а как вы выяснили, откуда грузить производителей и откуда сами телефоны?
от темы далек, но интересно
47. Павел Опарин (opx) 426 10.01.14 09:43 Сейчас в теме
(46)Любой сайт - это набор HTML тегов (инструкций для браузера как выводить информацию). По этим тегам и выясняем откуда грузить.
48. Дмитрий (gosizo) 22 31.01.14 21:05 Сейчас в теме
А как можно пройти авторизацию (логин и пароль есть) и затем пропарсить содержимое нужных страниц ?
49. Павел Опарин (opx) 426 01.02.14 23:08 Сейчас в теме
(48)Удивлен, что Вас заинтересовал этот метод парсинга. Сам я уже давно по-другому делаю. Этот пример скорее всего стоит пометить заголовком "Как не надо парсить сайты" )).
Способы авторизации разные бывают. Дайте сайт посмотреть.
50. Дмитрий Аистов (rustork) 23.04.14 14:08 Сейчас в теме
(49) opx, А по-другому это как? Может статейку? :-)
51. Sergey Volkov (angernaughts) 89 20.11.14 16:15 Сейчас в теме
Описал немного иной способ парсить сайт, аналогичный этому, но без использования браузера на форме - http://infostart.ru/public/314045/
52. Алексей Ахмадеев (Alexey_A) 7 08.01.16 00:03 Сейчас в теме
(10) opx, спасибо! начал путь парсинга с Вашей статьи!
к сожалению ДкументСформирован срабатывает совсем не как планируется, поэтому я сделал через предупреждение с паузой.