4 место [Обзор]ZennoPoster + xPath на примере Яндекс.Маркета

Sz5

Client
Регистрация
10.12.2012
Сообщения
157
Благодарностей
186
Баллы
43
Большой привет всему Community Zennolab:az:

Сегодня проведем небольшой разбор языка запросов xPath и его применении в ZennoPoster.
Также напишем несколько заготовок-болванок, чтобы наши знания закрепились на практике)

Дисклеймер
В статье используется мат :av:

XPath — это язык запросов к элементам html документа. Чтобы получить интересующие данные, необходимо всего лишь создать запрос, описывающий эти данные. Остальную работу за вас выполнит интерпретатор языка xPath. @AlexeyKuzmin

Инструментарий:
При построении xPath запросов необходимо:
  1. Базовые знания HTML тегов
  2. Базовая логика (дедукция и индукция) :eek:
  3. Знание синтаксиса xPath часто используемые:
    1. ancestor (предок, батя)
    2. descendant (потомок, пезд*к)
    3. starts-with (начинается с)
    4. contains(содержит ключевое слово)
  4. Понять основные принципы построения xPath запросов
Взял для примера популярный сайт Яндекс.Маркет, практическая польза, сайт не самый простой есть, где потренироваться + для интернет магазинов оживить сайт готовыми комментариями от живых людей или наполнить и актуализировать информацию о товаре, никогда не помешает :bq:

Работать будем с разделом мобильных телефонов.
Справедливости ради, хочу заметить, что работать будет с любым разделом, Вам необходимо лишь будет изменить id в url на свои.

1.png


Начнем с Бати.

2.jpg

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

*
Примечание На самом деле, мы можем написать запрос xPath и минуя батю получить результат и это даже будет работать, но это не про нас. Мы любим посложнее.:cd:

Закрепим на практике примечание)

Переходим на страничку Яндекс.Маркета -> Мобильные телефоны

Активируем xPather в появившемся окне набираем наш "первый" xPath запрос -
Код:
//div[@class='snippet-card__view']
3.png

Мы нашли div у которого название класса соответствует snippet-card__view.

Разберем структуру xPath запроса:

  1. // - Две косые черты, просто запоминанием, что все начинается с них
  2. div – мы говорим, что будем искать в html коде все div (это может быть любой html тег)
  3. [Тут будет запрос] – в этих скобках, будет проходить вся работа.
  4. @class = собачка вначале обязательно, искать только те div у которых есть class
  5. 'snippet-card__view' находим только тот class, у которого имя snippet-card__view, не забываем про верхние апострафы ' '
По такому же принципу можно найти snippet-card__info, snippet-card__content

Усложним задачу.

Получим все отзывы. Посмотрим, как лучше составить xPath запрос

Попробуем обратиться к элементу «Отзывов» напрямую, минуя Батю.

Открываем xPather и набираем в поле по аналогии.
Код:
//span[@class='small']
4.png


Хапнули лишнее, ну ничего, попробуем изменить запрос, задействуем ключевое слово descendant.

5.jpg

А вот и Батя:D

6.png



Разберем построение xPath запроса при использовании descendant

Код:
//[запрос к бате]/(косая черта)descendant(ключевое слово)::(два двоеточия)[запрос_к_пезд*ку]
Готовый запрос, исходя из конструкции выше.
Код:
//div[@class='snippet-card__content']/descendant::span[@class='small']
Результат не заставил себя долго ждать, Мы получили все отзывы без лишних результатов.

7.png


Мои поздравления, если вы до сих пор читаете этот бред:do:

Дальше будет интереснее (facepalm)

  • contains(@class, 'имя_класса') – возвращает true, если первая строка содержит вторую, иначе возвращает false.
Применяю когда имена классов являются динамическими или сгенерированными случайным образом.
Примеры этой функции будут в изобилии доступны исходном коде приложенного проекта.
  • starts-with(@class, 'имя_класса') - возвращает true если первая строка начинается со второй, иначе возвращает false.
Данная функция полезна, если, мы знаем, что какой-то из html элементов начинается с определенной ключевой фразы.

Вернемся в Yandex.Market

Продолжим погружение:de:

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

8.png


Построим следующий xPath запрос:
Код:
//div[starts-with(@data-id, 'model-')]
Результат


9.png


Примечание
Весь свет на теге div не сошелся, мы можем использовать и другие теги, чтобы построить xPath запрос, старайтесь идти по пути наименьшего сопротивления и задействовать готовые функции, которые доступны в языке xPath, иначе со временем Ваш запрос будет похож на вот такой кусок
Код:
/html/body/div[1]/div[4]/div[2]/div[1]/div[2]/div[1]/div[1]/div[3]/div/div[1]/div/h3/a/span
10.jpg




Теперь начнем применять наши знания по назначению, в рамках ZennoPoster-а:do:

Поставим задачу «Спарсить следующую информацию со странички Яндекс.Маркета» :
  1. Получим название товара
  2. Получим стоимость товара от и до
  3. Получим рейтинг товара
  4. Получим количество отзывов
  5. Получим ID товара, чтобы потом можно было парсить внутренние странички.
  6. Получим комментария к товару
Результат сформируем в строки и выведем их в логи.

Основная наша работа будет проходить на языке программирования C#.
В ZennoPoster
доступны специально написанные классы HtmlElement и HtmlElementCollection

11.png


Описании работы и заготовки-болванки в прикрепленный проекте.

12.png


На момент 15.05.2017 года. xPath запросы актуальны и выполняют своё назначение.

Спасибо за ваше внимание, лучшая благодарность для автора будет ваш голос:ay:
 
Тема статьи
Другое
Номер конкурса статей
Седьмой конкурс статей

Вложения

Для запуска проектов требуется программа ZennoPoster или ZennoDroid.
Это основное приложение, предназначенное для выполнения автоматизированных шаблонов действий (ботов).
Подробнее...

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

Последнее редактирование:

amyboose

Client
Регистрация
21.04.2016
Сообщения
2 312
Благодарностей
1 191
Баллы
113
Батя в здании 8-)
Расписано круто, автору респект
 
  • Спасибо
Реакции: Sz5

Sz5

Client
Регистрация
10.12.2012
Сообщения
157
Благодарностей
186
Баллы
43

Juniorcpa

Client
Регистрация
27.05.2014
Сообщения
2 031
Благодарностей
1 286
Баллы
113
Голосовать буду за Вас :-)
 
  • Спасибо
Реакции: leha52rus и Sz5

Tritatushki

Client
Регистрация
08.04.2010
Сообщения
70
Благодарностей
19
Баллы
8
Прекрасное оформление статьи, ставлю нраица :-)
 
  • Спасибо
Реакции: Sz5

Astraport

Client
Регистрация
01.05.2015
Сообщения
4 949
Благодарностей
4 372
Баллы
113
Я уж думал что в Зенно появилась удобная тулза для работы с Xpath.
/html/body/div[1]/div[4]/div[2]/div[1]/div[2]/div[1]/div[1]/div[3]/div/div[1]/div/h3/a/span
Вот так примерно выглядит работа с FB.
 
  • Спасибо
Реакции: iBotovod и Sz5

samsonnn

Client
Регистрация
02.06.2015
Сообщения
1 667
Благодарностей
1 341
Баллы
113

Geograph

Client
Регистрация
16.02.2014
Сообщения
209
Благодарностей
113
Баллы
43
Хорошая статья
XPath — это язык запросов к элементам html документа
На самом деле XPath - это язык запросов к элементам XML-документа о чем сообщает первая буква X. И это вносит свои коррективы - если HTML невалидный, что чаще всего и бывает (не закрытый тег, нет парного тега, не экранирован спецсимвол и т.п.), то чистый XPath вылетит с ошибкой - поэтому, сначала правится HTML и приводится к нормальному XML-виду (так работает большинство компонентов, например HtmlAgilityPack).

Стандартная панель разработчика в движке Chromium (по F12) тоже умеет искать по XPath и по CssQuery. В чём преимущества компонента XPather?
 
Последнее редактирование модератором:

seomiks

Client
Регистрация
13.09.2014
Сообщения
369
Благодарностей
124
Баллы
43
Возможно я скудоумный, мне регулярками проще.
 

Geograph

Client
Регистрация
16.02.2014
Сообщения
209
Благодарностей
113
Баллы
43
Возможно я скудоумный, мне регулярками проще.
Малейшее изменение страницы (добавление пробела, изменение названия класса) и регулярка отвалится, XPath более надежён в этом смысле.

P.S. Почему не надо парсить HTML регулярками: http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags?tab=votes#tab-top :-)
 

sergej_g

Client
Регистрация
29.11.2011
Сообщения
151
Благодарностей
124
Баллы
43
Спасибо за статью и старания. Мат лучше убрать...Мешает восприятию.
 
  • Спасибо
Реакции: Sz5

stanar

Client
Регистрация
19.12.2015
Сообщения
314
Благодарностей
157
Баллы
43
Однозначно статья имеет место быть. Плюсую и мой голос.
 
  • Спасибо
Реакции: Sz5

Lord_Alfred

Client
Регистрация
09.10.2015
Сообщения
3 916
Благодарностей
3 859
Баллы
113
  • starts-with(starts-with(@class, 'имя_класса')) - возвращает true если первая строка начинается со второй, иначе возвращает false.
Тут скорее всего опечатка, два раза starts-with


  • // - Две косые черты, просто запоминанием, что все начинается с них
Думаю, стоит ещё рассказать о том, что если мы ищем по xpath внутри другого HtmlElement элемента, то нужно использовать:
Код:
*//
Пример (найти первую форму и в ней кнопку сабмит):
Код:
HtmlElement he_form = instance.ActiveTab.FindElementByXPath(".//form", 0);
HtmlElement he_submit = he_form.FindChildByXPath("*//input[@type='submit']", 0);

Одна картинка после длиннющего плохого xpath - битая.
ЗЫ: картинки нужно залить на сервера зеннолаба, кста.


PS: статья очень понравилась, буду голосовать за неё) Стиль оформления и текст улыбнули) Понимаю, что некоторые вещи могут быть неприемлимы, но тут они крайне уместны. Особенно с поиском через Батю - прям гениальное сравнение!
 
  • Спасибо
Реакции: AndrewSuul, Ground и Sz5

Lord_Alfred

Client
Регистрация
09.10.2015
Сообщения
3 916
Благодарностей
3 859
Баллы
113
Стандартная панель разработчика в движке Chromium (по F12) тоже умеет искать по XPath и по CssQuery. В чём преимущества компонента XPather?
А ещё в Chrome можно искать через консоль JS используя:
Код:
$x("тут_xpath")
Тоже очень удобно, я юзаю именно так :-)
 
  • Спасибо
Реакции: kfil и Sz5

stanar

Client
Регистрация
19.12.2015
Сообщения
314
Благодарностей
157
Баллы
43
Я бы добавил поиск по Xpath без браузера при работе на post get запросах, в разы ускоряет. Но и так отлично, когда разбирался с ним - потратил много времени на поиск информации. На форуме 5 полезных постов.
 
  • Спасибо
Реакции: Sz5

lzlmrf

Client
Регистрация
14.08.2015
Сообщения
487
Благодарностей
148
Баллы
43
Screenshot_4.png
статья заебз 8-) Понятно что искать по постоянным элементам , и которые не меняются - это вы для ознакомления привели так сказать с темой. И что вы и Xpath можете много больше.. а можно попросить разобрать другой пример? дапустим https://plus.google.com/collection/cU7oBB?hl=ru получить описание к картинкам и сами картинки ?
 
  • Спасибо
Реакции: Sz5

Lord_Alfred

Client
Регистрация
09.10.2015
Сообщения
3 916
Благодарностей
3 859
Баллы
113
а можно попросить разобрать другой пример? дапустим https://plus.google.com/collection/cU7oBB?hl=ru получить описание к картинкам и сами картинки ?
Не знаю как в гуглоплюсе генерируются пути к картинкам, но скорее всего лучше отталкиваться от такой логики: ищем img, где в содержимом src (через contains) есть "googleusercontent.com/proxy/".
А описания соответственно через "соседние" к этому элементы получать.

Да, бывает что встречаются сайты, где классы у тегов генерируются автоматически или меняются при малейшем изменении верстки (например, из-за использования минификаторов css), но почти всегда можно оттолкнуться от каких-то других элементов. Например, даже использовать "text()" для получения содержимого (то что внутри тега) и сравнивать этот текст.

Но бывают ситуации, что и такие способы не спасают. Поэтому для каждого подхода должны быть свои инструменты и важно понимать и знать где и как их применять.
Лепить везде регулярки - бред. Лепить везде xpath - бред. Комбинировать эти способы - гораздо лучше.
 
Последнее редактирование:
  • Спасибо
Реакции: Sz5

Geograph

Client
Регистрация
16.02.2014
Сообщения
209
Благодарностей
113
Баллы
43
  • Спасибо
Реакции: Gunjubasik и lzlmrf

lzlmrf

Client
Регистрация
14.08.2015
Сообщения
487
Благодарностей
148
Баллы
43
ищем img, где в содержимом src (через contains) есть "googleusercontent.com/proxy/".
не пойдет - это только на загруженых с сайтов(с сылками).
А описания соответственно через "соседние" к этому элементы получать.
звучит красиво - но можно пример ? и желательно от ТС .
 

Sz5

Client
Регистрация
10.12.2012
Сообщения
157
Благодарностей
186
Баллы
43
@Geograph спасибо за отзыв, специально убрал упоминания про xml, дабы не смущать не подготовленного зрителя)
xPather использую исключительно исходя из эстетических моментов :bm:
@seomiks оно действительно проще, но кода всегда получается больше) попозже попробую провести несколько сравнений количества кода при использовании xPath и простых регулярок)
@lzlmrf Почему бы и нет. Держите готовый проект, увы без красочного описания, снабдил код небольшими комментариями.
@stanar да есть даже готовая библиотека HtmlAgilityPack или AngleSharp в последнем вроде есть возможность использовать css селекторы
@Lord_Alfred не смог найти, где картинка битая (
P.S. Залив картинок это можно только посредством прикрепления атачей?

Лепить везде регулярки - бред. Лепить везде xpath - бред. Комбинировать эти способы - гораздо лучше.
Самый верный подход :ay:
 

Вложения

Последнее редактирование:

amyboose

Client
Регистрация
21.04.2016
Сообщения
2 312
Благодарностей
1 191
Баллы
113
Есть предположения из неподтвержденных источников, что в htmlagilitypack идет утечка памяти, но обоснований этому у меня нет, как и опровержений.
 

Sz5

Client
Регистрация
10.12.2012
Сообщения
157
Благодарностей
186
Баллы
43
Есть предположения из неподтвержденных источников, что в htmlagilitypack идет утечка памяти, но обоснований этому у меня нет, как и опровержений.
Библиотека давно лишилась поддержки автора( использую ее для парсинга нескольких ресурсов, утечек не замечал, все работает стабильно, правда гоняю все это в один поток.
 

Sanekk

Client
Регистрация
24.06.2016
Сообщения
993
Благодарностей
389
Баллы
63
тоже очень понравилась статья,закреплю в закладках, буду обращаться по случаю как к мини мануалу)
ТС где вы были на прошлом конкурсе статей) зимой как раз плотно изучал xpath в том числе и по нескольким постам на форуме!
мой голос за статью полюбому)
 
  • Спасибо
Реакции: Sz5

seodima

Client
Регистрация
21.09.2014
Сообщения
199
Благодарностей
60
Баллы
28
Спасибо за статью. Я занимаюсь по большей части парсингом. Поэтому мне особо актуально. Я вообще регулярками обходился. Но за альтернативное решение - огромное спасибо. Голосовать буду, скорее всего, за Вас. Удачи.
 
  • Спасибо
Реакции: Sz5

lzlmrf

Client
Регистрация
14.08.2015
Сообщения
487
Благодарностей
148
Баллы
43
Активируем xPather в появившемся окне набираем наш "первый" xPath запрос -
вы прям сразу набираете запрос..а почему именно его? как пришли к такому решению? Плагин только показывает то что набрали - поиска или выделения блока нет (или я не нашел)
 

Sz5

Client
Регистрация
10.12.2012
Сообщения
157
Благодарностей
186
Баллы
43
вы прям сразу набираете запрос..а почему именно его? как пришли к такому решению? Плагин только показывает то что набрали - поиска или выделения блока нет (или я не нашел)
1. Перед началом работы пробегаюсь по всем блокам сайта инспектором(CTRL+SHIFT+I) , главная задача найти за что зацепиться, это может быть какое-то ключевое слово или какой-то определенный тег, которые есть только у этого элемента.
Наша задача отделить "зерна от плевел".
2. При принятии решения надо руководствоваться целями, если это парсинг, то основная цель это привести к коллекции (HtmlElementCollection) чтобы можно было в цикле все удобно перебрать, если это кнопка или поле на странице, то главный принцип это простота запроса, нет нужды с самого начала лепить огород из запросов (батя->пизд*к) если можно напрямую построить запрос привязавшись к какому-то ключевому слову или тегу.
3. Если xPath запрос написан правильно, то элемент подсветится в браузере, как на скрине


@lzlmrf Не спешите при построении запроса, а также будьте очень внимательны при составлении запроса,(если допускаете ошибку или забыли запятую, то запрос не будет работать), первое время сам пропускал ' апострофы, забывал писать @, ставить скобки, это нормальное явление, начинайте с простого и не пытаться сходу строить сложный запрос, который задействует весь набор запросов xPath. Велика вероятность, что вы допустите ошибку именно в синтаксисе, а не в логике запроса.
P.S. Главное это практика, со временем, xPath запрос у Вас будет собираться еще на этапе просмотра исходного кода.

 
  • Спасибо
Реакции: zennoX, lzlmrf и Sanekk

sydoow

Client
Регистрация
22.06.2011
Сообщения
285
Благодарностей
146
Баллы
43
Статья полезная, спасибо :-)
Вот бы мне раньше её до того как сам изучил))
 
  • Спасибо
Реакции: Sz5

sydoow

Client
Регистрация
22.06.2011
Сообщения
285
Благодарностей
146
Баллы
43
Кстате, вот ещё приведу запросы которые я ещё использую:

not - не содержит

примеры:
//a[not (@href)] - поиск всех элементов с тегом "а", у которых нет атрибута "href"
//label[not(@for='login')] - поиск всех элементов с тегом "label", у которых нет атрибута "for" со значением "login"

text() - InnerText
примеры
//span[text()='лалала'] - поиск всех элементов с тегом "span", у которых есть текст "лалала"
//a[contains(text(),"Удаленные")] - поиск всех элементов с тегом "a", которые содержат текст "Удаленные"

and - дополнительное условие
пример:
//*[@id='owner_photo_edit' and not(@style='display: none;')]
 

Tnyrtin

Client
Регистрация
12.05.2015
Сообщения
73
Благодарностей
8
Баллы
8
Спасибо за статью. Возникло пару вопросов по шаблону (http://joxi.ru/xAeBY5kt3zN8ry)
Главный вопрос как сохранить все элементы в переменую/список/таблицу?
Если на странице 100 найденных элементов - блоков(и каждый разбивается как у вас на "имя товара, описание, количество отзывов и т.д."), как сделать так чтобы эти элементы ложилось куда то(список, таблица, переменая).
 
  • Спасибо
Реакции: Sz5

Sz5

Client
Регистрация
10.12.2012
Сообщения
157
Благодарностей
186
Баллы
43
@Tnyrtin


  • Создаем один список и одну табличку, переименовываем их в info
  • Добавляем их в проект на свои места.

Код:
IZennoList infoList = project.Lists["info"];
IZennoTable infoTable = project.Tables["info"];
  • Формируем строку для таблички, разделяем ее табуляциями, чтобы каждый новый элемент лег в отдельный столбик + удаляем ключевики в цене "от" и "до"
Код:
string itemInfoTable = string.Format("https://market.yandex.ru/product/{0} \t {1} \t {2} \t {3} \t {4} \t {5}", idItem, nameItem.InnerText, rateItem.InnerText, reviewsItem.InnerText, priceFromItem.InnerText.Replace("от",""), priceToItem.InnerText.Replace("до",""));
  • Добавляем в табличку и в список
Код:
//Отправляем в список.
infoList.Add(itemInfo);
infoTable.AddRow(itemInfoTable);
Результат


Прикреплю готовый пример к этому посту.

Остальное, можно сделать по аналогии :do:
 

Вложения

Последнее редактирование:

Кто просматривает тему: (Всего: 1, Пользователи: 0, Гости: 1)