Парсинг сайтов из 1С на примере ломбарды.рф с помощью XPATH для ДокументDOM

01.04.19

Интеграция - Сайты и интернет-магазины

На всякую хитрую гайку всегда найдется болт с резьбой (с)

ПАРСИНГ САЙТОВ НА 1С

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

ВВЕДЕНИЕ

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

Мне пришлось использовать следующие объекты: ЧтениеJSON, ЧтениеXML, HTTPСоединение и HTTPЗапрос ну и ПостроительDOM, с помощью которого мы будем парсить через XPath.

Но давайте по-порядку.

С ЧЕГО НАЧАТЬ?

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

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

Итак, загрузив сайт ломбарды.рф мы видим следующую картину:

Здесь мы видим, что нам доступна только маленькая часть списка. За остальными нужно лезть через "Еще результаты".

Если мы посмотрим код, то по этой кнопке дергается сервис (data-url="/lombards/load_more.php?category=&sort=&minloan=&maxloan=&region="). Если мы откроем в браузере эту ссылку, то увидим вот такую интересную штуку:

Как подсказывает нам ({"content":" \n) в начале строки - это JSON. 1С умеет его читать, поэтому давайте начнем с простого - создадим HTTP-запрос и обработаем ответ.

СОЗДАНИЕ HTTP-СОЕДИНЕНИЯ, ВЫЗОВ ЗАПРОСА И ОБРАБОТКА ОТВЕТА

Для создания HTTP-соединения и запроса в 1С есть простые объекты, которые прямо так и называются:

  С = Новый HTTPСоединение(Адрес);
  З = Новый HTTPЗапрос(Урл);

После того, как мы прочитали JSON и распарсили его, получили такой вот объект:

В объекте есть два поля: content и remaining, в первом находится HTML страницы, а во втором - количество оставшихся элементов.

Давайте попробуем засунуть значение в ДокументDOM, чтобы можно было написать к нему XPath:

	П = Новый ПостроительDOM;
	Х = Новый ЧтениеXML;
	Х.УстановитьСтроку(СС["content"]);
	ДОМ = П.Прочитать(Х);
	Р = Новый РазыменовательПространствИменDOM(ДОМ);
	Результат = ДОМ.ВычислитьВыражениеXPath(".", ДОМ, Р, ТипРезультатаDOMXPath.Любой);

Итак, что тут происходит? Я прочитал JSON в соответствие СС, после чего создал построитель ДОМ, которым прочитал XML из СС["content"]. Но у меня вывалилась первая ошибка:

{ВнешняяОбработка.ЧтениеЛомбардов.Форма.Форма.Форма(20)}: Ошибка при вызове метода контекста (Прочитать)
    ДОМ = П.Прочитать(Х);
по причине:
Ошибка разбора XML:  - [8,41]
Фатальная ошибка:
Opening and ending tag mismatch: img line 7 and a

Что там у нас в 7-й строке? Тег img, который не закрывается!

<img src="http://xn--80abkzflr3g.xn--p1ai/upload/iblock/74a/74a8c12d4c7acf804a0812b501bd0d5a.jpg" alt="">

Да, мы можем прочитать данные в ДокументHTML вместо DOM, но тогда нам не будет доступен XPath. Также мы не можем просто так взять и поправить все ">" на "/>" - есть такие теги, которые содержат внутренние элементы. Надеюсь Вы теперь понимаете, почему почтенные веб-разработчики просят соблюдать стандарт и не писать "<br>" вместо "<br />" (кстати, не стоит путать стандарт XML и нечто от 1С об именовании переменных).

Благо, что у нас в img всегда есть alt, по которому мы сможем узнать, что тег надо закрыть. Итак, давайте исправим это:

	Ст = СтрЗаменить(СС["content"], "alt="""">", "alt="""" />");

Но ничего не вышло:

Ошибка разбора XML:  - [39,1]
Фатальная ошибка:
Extra content at the end of the document

Что на этот раз? Тут все просто - 1С не может прочитать неполный документ, т.е. все теги документа должны быть в одном корневом контейнере. Исправить это нетрудно:

	Ст = "<main>" + СтрЗаменить(СС["content"], "alt="""">", "alt="""" />") + "</main>";

В итоге при чтении XML в DOM у нас пока больше нет ошибок. На том же PHP у меня нет ошибок сразу - я могу любую ересь в него прочитать и применить к прочитанному XPath. Но это так - лирическое отступление.

XPATH-ВЫРАЖЕНИЯ В 1С

Для того, чтобы применить ограниченный функционал XPath в 1С прежде всего нам нужен ДокументDOM и РазименовывательПространствИменDOM. Если со смыслом первого объекта как-то можно смириться, то вникнуть в смысл второго у меня пока не получается - я просто инициализирую его через документ - и все:

	Р = Новый РазыменовательПространствИменDOM(ДОМ);
	Результат = ДОМ.ВычислитьВыражениеXPath("//div[@class='item-info']", ДОМ, Р);

В результате мы получим объект с типом "РезультатXPath". Для получения элемента нам нужно просто вызвать его функцию "ПолучитьСледующий()":

	Пока Истина Цикл 
		Узел = Результат.ПолучитьСледующий();
		Если Узел = Неопределено Тогда Прервать;
		КонецЕсли;

		// какой-то полезный код
	КонецЦикла;

У нас на странице будет 5 элементов, которые мы получили с помощью запроса "//div[@class='item-info']". Мы выбрали все элементы "div" у которых атрибут "class" равен "item-info".

Итак, мы получили элементы XML, в которых содержатся имена и адреса ломбардов. Можно лазить за ними через всю эту иерархию ДОМ'а, а можно просто применить XPath к указанным узлам. Если посмотреть внимательно на файл, то можно увидеть, что имя ломбарда содержится в div'е с классом "item-info__title", там же и ссылка на страницу с телефоном (в действительности - на ее редирект). А адрес находится в теге "<address>". Давайте напишем для них XPath-выражения:

	Результат1 = ДОМ.ВычислитьВыражениеXPath("//div[@class='item-info']", ДОМ, Р);

	Пока Истина Цикл 
		Узел = Результат1.ПолучитьСледующий();
		Если Узел = Неопределено Тогда Прервать;
		КонецЕсли;
		Результат2 = ДОМ.ВычислитьВыражениеXPath("./div[@class='item-info__title']/h4/a/text()", Узел, Р, ТипРезультатаDOMXPath.Строка);
		Результат3 = ДОМ.ВычислитьВыражениеXPath("./div[@class='item-info__title']/h4/a/@href", Узел, Р, ТипРезультатаDOMXPath.Строка);
		
		Результат = Результат + "
		|ИМЯ: " + Результат2.СтроковоеЗначение + "
		|Урл: " + Результат3.СтроковоеЗначение;
	КонецЦикла;

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

ИМЯ:
                        VIPLOMBARD                    
Урл: /lombards/yuvelirnye/10370
ИМЯ:
                        Chronoland                    
Урл: /lombards/yuvelirnye/10374
ИМЯ:
                        AUTO-PERSPECTIVA                    
Урл: /lombards/avtomobiley/10442
ИМЯ:
                        Кредиты Населению Автоломбард                    
Урл: /lombards/avtomobiley/10443
ИМЯ:
                        Гольфстрим (Профсоюзная, 127Б)                    
Урл: /lombards/avtomobiley/10504

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

ИТОГ

В ходе парсинга я столкнулся со следующими проблемами:

  1. Редирект. По указанной в Урл странице находится страница с перманентным редиректом. Взять из нее адрес не составит никакого труда. а определить ее можно по 301-й ошибке в ответе веб-сервера.
  2. Наличие в комментарии двух минусов подряд ("--"). Такой комментарий ДОМ 1С не читает и вы получите эксепшн. Как бороться? СтрЗаменить - наше все.
  3. Закрывающийся тег </b> без открывающегося. Просто удалял все <b> и </b>.
  4. Тег <br> - менял на <br />, но можно просто удалить.
  5. "&" в текстовых полях - надо менять на "&amp;", иначе 1С такой XML не прочитает.
  6. Ну и не надо читать весь HTML - начните с <body> и им же заканчивайте (для этого сайта лучше начать и закончить тегом <main> - он там как раз есть).
  7. Вишенка на торте - скрытый параметр, подставляемый в урл JSON-а, получаемого PHP-скриптом. Найдите его сами - в качестве домашнего задания. Подскажу - можно воспользоваться консолью хрома и запустить сбор данных о производительности - там будут все запросы, а в них, в свою очередь, будут все параметры.
  8. Удачи!

парсинг сайтов XPath XML JSON

См. также

Сайты и интернет-магазины Платформа 1С v8.3 1С:Розница 2 Розничная и сетевая торговля (FMCG) Россия Платные (руб)

Готовое интеграционное решение для оплаты покупок Долями в 1C:Розница 2.3. Реализовано в виде расширения. Интеграция сервиса dolyame.ru для приема платежей в рассрочку. Поддерживает работу от разных юридических лиц. Работа: в составе РИБ, отдельно от РИБ, тонкий, толстый клиент, web-клиент (через интернет-браузер).

20400 руб.

19.12.2023    5097    37    11    

35

Сайты и интернет-магазины WEB-интеграция Системный администратор Программист Пользователь Платформа 1С v8.3 Конфигурации 1cv8 1С:Управление торговлей 11 Автомобили, автосервисы Россия Управленческий учет Платные (руб)

Интеграционный модуль обмена между конфигурацией Альфа Авто 5 и Альфа Авто 6 и порталом AUTOCRM. Данный модуль универсален. Позволяет работать с несколькими обменами AUTOCRM разных брендов в одной информационной базе в ручном и автоматическом режиме.

36000 руб.

03.08.2020    17780    19    22    

16

Сайты и интернет-магазины Интеграция WEB-интеграция Платформа 1С v8.3 Конфигурации 1cv8 Управленческий учет Платные (руб)

Интеграция 1С и Битрикс 24. Разработка имеет двухстороннюю синхронизацию 1С и Bitrix24 задачами. Решение позволяет создавать пользователя в 1С из Битрикс24 и наоборот. Данная разработка технически подходит под все основные конфигурации линейки продуктов 1С:Предприятие 8.3 (платформа начиная с 8.3.23). При приобретении предоставляется 1 месяц бесплатных обновлений разработки. Доступна демо-версия продукта с подключением Вашего Битрикс24

5040 руб.

04.05.2021    19844    13    17    

16

СБП. Оплата по QR-коду Сайты и интернет-магазины Программист Платформа 1С v8.3 Конфигурации 1cv8 Автомобили, автосервисы Россия Бухгалтерский учет Управленческий учет Платные (руб)

В настоящее время система СБП очень часто стала использоваться в повседневной жизни. Одна из систем интеграции СБП через СБЕР. Данная конфигурация является инструментом интеграции СБП в Альфа Авто. Данная система не просто формирует статический QR, а динамический, а значит, в системе будет привязка и на покупателя, и на документ.

7200 руб.

25.10.2022    6306    33    4    

11

Сайты и интернет-магазины Программист Бизнес-аналитик Пользователь Платформа 1С v8.3 Конфигурации 1cv8 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 Управленческий учет Платные (руб)

Программа позволяет одним кликом настроить сбор цен ваших конкурентов или дилеров в интернете. Автоматически собирает данные результата поиска Яндекса (вам не придется вручную добавлять каждый сайт, за которым нужно следить). Обновление цен происходит по заданному вами расписанию автоматически. Можете легко отслеживать позиции вашего сайта в Яндексе по ключевым словам и фразам. Этот инструмент даст вам лучшее понимание того, как ваша SEO-стратегия влияет на видимость вашего сайта в поисковой выдаче, и поможет вам улучшить контент и структуру сайта для повышения его позиций. Функция доступна во всех тарифах.

19950 руб.

23.09.2019    31021    7    12    

30
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. bonv 1560 01.04.19 15:53 Сейчас в теме
(0)
1. Редирект. По указанной в Урл странице находится страница с перманентным редиректом. Взять из нее адрес не составит никакого труда. а определить ее можно по 301-й ошибке в ответе веб-сервера.

Используйте
https://infostart.ru/public/709325/
и не будет проблем с редиректами
starik-2005; +1 2 Ответить
2. webester 26 02.04.19 12:33 Сейчас в теме
(1)Мне нравится простой пример с ИТС https://its.1c.ru/db/metod8dev#content:5574:hdoc обрабатывает перенаправления указывает на типы ошибок
3. bonv 1560 02.04.19 12:43 Сейчас в теме
(2) все хорошо, пока не захочется парсить сайты, требующие предварительной авторизации
4. webester 26 02.04.19 12:52 Сейчас в теме
(3)Это на тему перенаправления. Использовать только ради него Коннектор, как из пушки по воробьям. Библиотека сама по себе отличная.
5. пользователь 02.04.19 17:04
Сообщение было скрыто модератором.
...
6. Поручик 4692 02.04.19 21:19 Сейчас в теме
7. starik-2005 3087 02.04.19 22:49 Сейчас в теме
Редирект на столько прост, что я о нем даже говорить не стал - скучно! Особенно когда редирект внутри одного домена

О = С.Ролучить(З);
Если О.КодОтвета МЕЖДУ 300 и 399 Тогда
  З = Новый HTTPЗапрос(ПолучитьУрл(О));
  О = С.Получить(З);
КонецЕсли;
8. s_vidyakin 68 03.04.19 02:28 Сейчас в теме
лучше поднапрячься и изучить как это делается в цивилизованном мире - nodejs + axios + cheerio https://nuancesprog.ru/p/3102/ делов на полчаса ))
XPath это непонятная хрень, иногда работает иногда нет, на определенных тегах/классах/фазах Луны
testnv0; starik-2005; +2 1 Ответить
9. starik-2005 3087 03.04.19 07:20 Сейчас в теме
(8) ну это как с регулярками - у меня работает, а у пользователей компьютера не всегда, хотя у нас даже аналитики уже регулярки освоили и дату в локальном формате могут заменять на xml- дату, и всегда работает)))
for_sale; +1 Ответить
13. s_vidyakin 68 03.04.19 11:14 Сейчас в теме
(9) пробовал получать вложенные теги в определенном теге, указываю типа ".class1 > .class2" - НОЛЬ элементов. В консоли браузера все выбирается. Пришлось выбирать глобальным поиском по class2, но они там и в других местах были, логика усложнилась проверками. Больше с XPath не связываюсь
Возможно было бы более интересно если бы написали библиотеку на OScript типа cheerio и сделали обзор )
14. starik-2005 3087 03.04.19 11:22 Сейчас в теме
(13)
пробовал получать вложенные теги в определенном теге, указываю типа ".class1 > .class2" - НОЛЬ элементов
Если речь об 1С, то я даже уточнил в статье, что XPath в ней ограничен. Хотя //div[@class='c1']/div[class='c2']/text() - вполне рабочая конструкция даже для 1С.
10. starik-2005 3087 03.04.19 08:55 Сейчас в теме
(8) кстати, вывод автора неутешительный: "мы можем извлекать данные только из статических сайтов". Я же привел пример извлечения из динамического сайта как раз - основная хитрость тут - это разобраться с источниками данных.

Также если посмотреть на статью внимательно, то понятно становится, что ничего нового - тот же запрос к HTML-ДОМ'у, преобразованному в виртуальный ДОМ с помощью компонента node.
11. s_vidyakin 68 03.04.19 11:07 Сейчас в теме
(10) там есть вторая часть, для динамических сайтов - https://nuancesprog.ru/p/3125/.
Конечно тот же DOM, но удобнее, у XPath язык отличается от стандартных CSS селекторов
Да и скорость еще под вопросом у XPath, думаю он ляжет на больших объемах
testnv0; starik-2005; +2 Ответить
12. starik-2005 3087 03.04.19 11:11 Сейчас в теме
(11) так я и не спорю, что 1С для парсинга сайтов подходит весьма условно. Во второй статье очень хороший и интересный подход через кликер для динамики - реально вещь! )))
15. nbeliaev 835 04.04.19 10:46 Сейчас в теме
Материал полезен для новичков в этой теме, но зачем же так называть переменные?
vladimirmatancev; +1 Ответить
16. starik-2005 3087 04.04.19 13:32 Сейчас в теме
(15)
но зачем же так называть переменные?
Переменные названы просто отлично. Так, как в примерах для других языков программирования.
ЗЫ: Я, кстати, тоже с Вологды.
Прикрепленные файлы:
17. TODD22 19 04.04.19 13:33 Сейчас в теме
(16)Вы все переменные называете как "счётчик цикла" ?
vladimirmatancev; +1 Ответить
18. starik-2005 3087 04.04.19 13:35 Сейчас в теме
(17)
Вы все переменные называете как "счётчик цикла" ?
Мы все переменные называем ровно так, чтобы было понятно, что они есть.
19. TODD22 19 04.04.19 14:11 Сейчас в теме
(18)
Да я заметил "О", "С", "З"... очень содержательные имена.
vladimirmatancev; Kopitsa.k; alest; +3 Ответить
20. starik-2005 3087 04.04.19 14:18 Сейчас в теме
(19) для понимания достаточно?
21. premierex 204 05.04.19 16:54 Сейчас в теме
(20) Вот имя переменной ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений вполне себе содержательное. Для понимания достаточно, даже если с места её объявления прокрутить несколько сотен строк, смысл всё равно будет понятен.
А вот смысловую нагрузку "О", "С", "З" можно понять, если текст, где происходит объявление этой переменной и где время её жизни заканчивается, находятся на одной экранной страннице. Не экономьте время на читабельности кода! Через некоторое время самому сложно будет этот код править.
vladimirmatancev; Kopitsa.k; acanta; +3 Ответить
22. starik-2005 3087 05.04.19 21:20 Сейчас в теме
(21) кто не умеет - тому пропроцессор компоновки как ни назови - все бессмысленно. А кто умеет - тому хоть горшком (наролная мудрость, кстати, а вряд ли мы умнее народа по-одному)
24. nbeliaev 835 06.04.19 11:28 Сейчас в теме
(16) мы пересекались в Вологде года 4 назад. Магазин автозапчастей. Я тогда работал в своем первом франче, а Вы были как приглашенный московский спец ))
25. starik-2005 3087 06.04.19 17:19 Сейчас в теме
(24) я кстати к ним на днях заеду - чисто поглядеть. А по поводу приглашенного - это мои клиенты с 2004-го года.
26. starik-2005 3087 25.04.19 15:21 Сейчас в теме
(24) кстати, переработал и дополнил их программно-аппаратную часть - вот что получилось: https://infostart.ru/public/1051601/
27. nbeliaev 835 26.04.19 08:47 Сейчас в теме
(26) Да, я прочитал этот материал ) сразу понял про кого речь ))
23. premierex 204 06.04.19 10:27 Сейчас в теме
(0) Информация по теме: в версии платформы 8.3.13 у объекта ДокументHTML появилась функция НайтиПоФильтру(Фильтр). С её помощью можно получить требуемые узлы, выполнив следующий код:
	ЧтениеHTML = Новый ЧтениеHTML;
	ЧтениеHTML.УстановитьСтроку(ТекстHTML);
	Построитель = Новый ПостроительDOM;
	ДокументHTML = Построитель.Прочитать(ЧтениеHTML);
	ЧтениеHTML.Закрыть();
	ДокументHTML.НормализоватьДокумент();
	Фильтр = "{
			|	""type"": ""intersection"", 
			|	""value"": 
			|	[
			|		{ 
			|			""type"": ""elementname"", 
			|			""value"": 
			|			{ 
			|				""value"": ""div"",
			|				""operation"": ""equals""
			|			}, 
			|		}
			|		,
			|		{ 
			|			""type"": ""hasattribute"", 
			|			""value"": 
			|			{ 
			|				""value"": ""class"",
			|				""operation"": ""nameequals""
			|			}	 
			|		}
			|		,
			|		{ 
			|			""type"": ""hasattribute"", 
			|			""value"": 
			|			{ 
			|				""value"": ""item-info"",
			|				""operation"": ""valueequals""
			|			}	 
			|		}
			|	]
			|}";
	МассивУзлов = ДокументHTML.НайтиПоФильтру(Фильтр);
Показать


Ну а затем уже анализировать дочерние элементы. Можно с помощью XPath, а можно и без.
vladimirmatancev; AlX0id; coollerinc; starik-2005; +4 Ответить
28. ture 611 17.05.19 15:15 Сейчас в теме
Jsoup, и не забивай голову ерундой
29. starik-2005 3087 17.05.19 15:36 Сейчас в теме
(28)
Jsoup
А что там у него с динамическим контентом?
30. ture 611 17.05.19 15:42 Сейчас в теме
31. starik-2005 3087 17.05.19 17:30 Сейчас в теме
32. amd1986 25.07.19 11:38 Сейчас в теме
Брр. Кто мешает использовать 1C 8.3.14 и javascript?
33. starik-2005 3087 25.07.19 12:29 Сейчас в теме
(32)
Кто мешает использовать 1C 8.3.14 и javascript?
А как JS заставить работать в регламентном задании? Мне вот реально надо (бывал как-то в Кадникове ВО в детском доме - давно это было).
34. amd1986 25.07.19 13:00 Сейчас в теме
(33) В фоновом наверно не получится(не проверял). Страница должна прогрузиться на форме, чтобы с ней можно было работать через ноды.
Оставьте свое сообщение