Пишем парсеры в Zennoposter на C#. Инструкция для новичков от новичка часть 2

volody00

Client
Регистрация
06.09.2016
Сообщения
917
Благодарностей
951
Баллы
93
В прошлой статье я показал, как парсить на веб. В этой статье разберем парсинг на запросах. Писать будем парсер нашего любимого форума zennolab. Несколько предупреждений:

  • Статья для новичков
  • В статье могут быть неточности в терминах ввиду неопытности автора
  • Шаблон писался на последней версии (5.39.0.0)
  • Не рассматривается вариант, когда какие-либо данные подгружаются скриптом
  • В этой статье на логике я останавливаться не буду. Просто покажу, как парсить на запросах простенькие сайты. Если вы пока не понимаете, как писать парсеры, то можете ознакомиться с моей предыдущей статьей.
Подготовка

Собирать будем ссылки на профили пользователей. Парсить мы будем активных пользователей, т.е. тех, кто оставляет сообщения. Нужные нам ссылки на профили находятся здесь (1):
46138

Зальем в txt файл ссылки на разделы, которые хотим парсить. Для примера взял «новости», «партнерская программа», «ZennoStore»
46140

Создаем кубик c# и привязываемся к нашему txt файлу:
46142 46143

В кубике c# мы создали список (далее к нему будем обращаться по имени spis):

C#:
IZennoList spis = project.Lists["разделы"]; //создаем список с именем spis
Создадим цикл и возьмем первый раздел:
46144

Теперь спарсим html-код страницы. Делается это следующим образом:
46145

Так, теперь у нас в переменной response хранится код вот этой страницы - https://zennolab.com/discussion/forums/novosti.35/

Сейчас нам надо составить xpath путь до нужных нам ссылок. Идем в гугл хром, открываем страницу, устанавливаем плагин Xpath Helper Wizard (если его нет, устанавливаем). Посмотрим, как можно выцепить нужные нам ссылки. Жмем по ним правой кнопкой мыши, смотрим в код:
46147

За сами ссылки не зацепиться, будем брать элементы повыше. Видим, что у него есть «родитель» <div class="structItem-title">
46148

Видим, что данный вариант нам подходит. На всякий случай разберем наш xpath путь (//div[@class='structItem-title']/a). Я сказал, что мне надо найти тег div с классом равным 'structItem-title', а затем найти его «ребенка» тег a.

Ок, xpath путь мы составили. Чтобы нам спарсить эти элементы нам понадобится ещё два списка:

  • Один временный - List<string> vrem = new List<string>();
  • Другой основной - IZennoList ssylk = project.Lists["ссылки"]

46149

Запустим шаблон. Зайдем в наш файл ссылки.txt и убедимся, что всё норм.
46150

Всё норм, но перед /discussion/ не хватает https://zennolab.com/. Мы это потом поправим.

Парсим остальные страницы

Сейчас мы с вами получили парсинг только трех страниц, указанных в txt файле. Нам надо собрать остальные. Как мы это сделаем? Мы будем искать кнопку Next до тех пор, пока она не исчезнет.
46151
46152
Т.е.логика у нас такая:
  • находим кнопку Next
  • если она есть, то берем у неё href
  • загружаем html-код стр
  • парсим что нам нужно
  • находим кнопку Next, если она есть, то берем у неё href
  • загружаем html-код стр
  • и т.д.
Если нет, то цикл завершает работу. Давайте реализовывать
46153

Что хочу отметить:
  • обратите внимание на 13 строку. Тут мы уже складываем в переменную, отбирая только один элемент (с помощью ElementAt(0))
  • Если в результате выполнения ZennoPoster.Parser.ParseBByXpath ничего не найдется, то будет ошибка. Именно поэтому мы обернули данный код в try-catch. Т.е. если ничего не будет найдено, то будет ошибка и шаблон перейдет к выполнению того, что написано внутри catch {}. Там я говорю, что надо выйти из цикла (значит, мы больше не нашли кнопок Next)
  • Если xpath пути привязываете к названиям (например, //a[contains(string(), 'ZennoDroid')]), то следите за тем, чтобы на сайте не было нескольких языковых версий (обжегся на этом, пока писал данный код)
Многопоток

Я в многопотоке не силен, но решил включить в статью, чтобы вы могли с чего-то начать. В общем, у нас есть такая конструкция:



Если вы что-то напишите внутри него, то остальные потоки будут ждать. SyncObjects может быть 3 видов:

SyncObjects.ListSyncer - для списков
SyncObjects.TableSyncer - для таблиц
SyncObjects.InputSyncer - для буфера обмена

Вот нарисовал для себя как это работает, может кому-то будет полезно. Предположим, что у нас есть 3 спортсмена, которые стартуют одновременно:
46154


Затем кто-то из них (тот, кто первый =) ) заходит в поле lock. Остальные при этом туда зайти не могут и ждут, пока тот выбежет оттуда.
46155

Когда чел выходит из поля lock(){}, туда запускают ещё одного (только одного (!))
46157

У нас в коде может быть несколько lock(). При этом сколько бы их ни было, в поле может находиться только один чел.
46158

О последнем факте и о том, что бывает не только SyncObjects.ListSyncer, я узнал совсем недавно вот отсюда (рекомендую ознакомиться).
С детским садом закончили, перейдем к практике. Как мы с вами поступим:

  • На 8-й строке мы с вами получаем строку (razdel = spis), в которой содержится категория. Нам нельзя, чтобы несколько потоков её взяли. Поэтому мы её залочим и после взятия удалим
    Соответственно, следующий поток уже не сможет взять тот же урл (мы его удалили), он возьмет уже следующий и будет работать с ним.
    Следующий лок нам нужен тогда, когда мы записываем в наш основной txt файл, чтобы не получилось так, что несколько потоков одновременно засовывают туда информацию. Это 12 строка и 23 строка


Давайте реализуем это:
46159

Ап. Начал тестить шаблон и нашел косяки. Какие нюансы:

    • Во-первых, я забыл поменять шестую строку. Поскольку мы с вами теперь удаляем строки из файла, то spis.Count у нас постоянно уменьшается. Её надо за циклом записать в переменную и в цикле уже подставлять именно переменную
    • Во-вторых, при парсинге выяснился такой нюанс: например, на 5 странице мой Xpath путь работает, а на шестой уже нет. Из-за этого собирались не все данные
    • При запуске многопотока выходит ошибка, что индекс за пределами диапазона
Давайте исправлять. Сначала по первому пункту:

46161
Теперь по второму пункту. Переделаем xpath путь. И добавим проверку. В общем, если зайти на стр то можно увидеть общее количество страниц:
46162
Если наш шаблон вывалился в catch, то делаем проверку: равна ли последняя цифра в урл этой цифре. Если нет, то выведем себе сообщение, что наш xpath путь фуфло. Т.е. мы будем сравнивать вот эти две цифры:
46163

Получаем цифру (где постраничная пагинация)
46164
Сравниваем:
46165
И теперь по третьему пункту:
46166
При работе на запросах можно поставить галочку «без браузера».
46167

Парсим пользователей

Тут принцип тот же. Создаем новый кубик и действуем по следующей схеме:
  • Создаем цикл
  • Берем урл
  • Удаляем его
  • Делаем запрос
  • Вытаскиваем профили пользователей
  • Перебираем их в цикле
  • Складываем в список

Реализацию можно посмотреть в шаблоне. Спасибо за внимание.
 
Тема статьи
Парсинг
Номер конкурса статей
Двенадцатый конкурс статей

Вложения

  • 20,5 КБ Просмотры: 330

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

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

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

melutsk

Client
Регистрация
03.08.2016
Сообщения
1 347
Благодарностей
1 257
Баллы
113
Так по извращенному я не парсил еще))
 

Valandersi

Client
Регистрация
19.01.2015
Сообщения
1 919
Благодарностей
1 123
Баллы
113
Для тех кто только начинает осваивать c# хороший пример, спасибо
 

Supergrok

Client
Регистрация
05.03.2019
Сообщения
171
Благодарностей
156
Баллы
43
А я буду изучать, потому что хочу сам парсер себе написать. А можно видео запилить?
 

volody00

Client
Регистрация
06.09.2016
Сообщения
917
Благодарностей
951
Баллы
93
А я буду изучать, потому что хочу сам парсер себе написать. А можно видео запилить?
видео модераторы не пропустили, из-за того что автор (т.е. я) периодически тупил и из-за этого затянул его на 30 мин. Переписывать не хочу. По шагам запускайте и смотрите, что происходит. если что спрашивайте
 

Phoenix78

Client
Read only
Регистрация
06.11.2018
Сообщения
11 790
Благодарностей
5 718
Баллы
113
видео модераторы не пропустили, из-за того что автор (т.е. я) периодически тупил и из-за этого затянул его на 30 мин. Переписывать не хочу. По шагам запускайте и смотрите, что происходит. если что спрашивайте
самокритично :-)
этот 12-й конкурс прям впечатляет. статья за статьей и все такие интересные :-)
 

DenisK

Client
Регистрация
28.06.2016
Сообщения
591
Благодарностей
289
Баллы
63

Valandersi

Client
Регистрация
19.01.2015
Сообщения
1 919
Благодарностей
1 123
Баллы
113
Пример с человечками хороший, реально на пальцах объяснил
 

alexkoba25

Client
Регистрация
12.12.2019
Сообщения
30
Благодарностей
2
Баллы
8

Чешир

Client
Регистрация
27.06.2014
Сообщения
1 633
Благодарностей
962
Баллы
113
Спасибо. Очень доходчиво. Даже коту, вроде меня, понятно. А для Хпата есть подобный плагин для Мозиллы (не люблю я Хрома, он говнюк)
 

limarkximus

Client
Регистрация
01.08.2019
Сообщения
116
Благодарностей
73
Баллы
28
а как к примеру спарсить толдько нужный блок? к примеру как на картинке..

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

Phoenix78

Client
Read only
Регистрация
06.11.2018
Сообщения
11 790
Благодарностей
5 718
Баллы
113
получить коллекцию по xpath и вытащить элемент по номеру в коллекции или прямо в xpath указать номер нужного элемента [3] например
 

limarkximus

Client
Регистрация
01.08.2019
Сообщения
116
Благодарностей
73
Баллы
28

Phoenix78

Client
Read only
Регистрация
06.11.2018
Сообщения
11 790
Благодарностей
5 718
Баллы
113
ну это если они местами и количеством не меняются не меняются...
ну так из скрина не видно уникальных элементов за что можно зацепиться, поэтому и привел как пример номер.
 

limarkximus

Client
Регистрация
01.08.2019
Сообщения
116
Благодарностей
73
Баллы
28
ну так из скрина не видно уникальных элементов за что можно зацепиться, поэтому и привел как пример номер.
да да.. это я по ходу тупанул.. в коллекцию я как понимаю сразу весь блок вписать же можно.... т.е от начала до конца .. а так как стил будет в некоторых оличаться он их и выпарсит верно? т.е нужные..
не поможешь как составить?
HTML:
<tr>
    <td class="sport tte bas" title="<div><b>Баскетбол</b></div>"><b>B</b></td>
    <td class="location"><div class="starts"><div class="tte" title="<div class='date_hint'><table><tr><td colspan='2'>От ввода прогноза: &nbsp; <b>43 мин. </b></td></tr><tr><td colspan='2'>До начала события: &nbsp; <b>2 ч. 0 м. </b><br /><br /></td></tr><tr><td>Введено:</td><td>9 апреля 2020, 09:15 &nbsp;Мск [GMT+3]</td></tr><tr><td>Событие:</td><td>9 апреля 2020, 12:00 &nbsp;Мск [GMT+3]</td></tr><tr><td>Интервал:</td><td><b>2 ч. 44 мин.</b> от ввода до события</td></tr></table></div>"><span class="input_icon_green"><b><span class="green">43 мин. </span></b></span> &nbsp; &nbsp;<span class="starts_icon_gray"><b>2 ч. 0 м. </b></span></div></div>Тайвань. SBL</td>
</tr>
</table>
<table>
<tr>
    <td class="event head">Событие</td>
    <td class="outcome head">Прогноз</td>
    <td class="stake tte head" title="Ставка, в процентах от банкролла">Размер</td>
    <td class="odds tte head" title="Коэффициент">Коэф.</td>
    <td class="book tte head" title="Букмекерская контора или биржа ставок">Контора</td>
</tr>
<tr>
    <td class="event"><a href="/sub/133661/prvzkbt.UN.L/picks/30031536/"><div class="event_main">Pauian - Bank of Taiwan</div><div class="event_aux">Включая овертайм</div></a></td>
    <td class="outcome tte" title="Тотал">ТМ 180.5</td>
    <td class="stake">1.00%</td>
    <td class="odds"><b>1.55</b></td>
    <!-- <td class="book tte  img_book" title="<b>Bwin</b>" ><b class="darkred tte help" title="<b>Bwin</b>">Bwin</b></td> -->
    <td class="book"><b class="darkred tte help" title="<b>Bwin</b>">Bwin</b></td>
</tr>
</table>
уникальное значение (т.е и начало ) <span class="input_icon_green"> второq блок <tr> ...
только тогда будет следующая проблема)) как почистить не нужное... тогда все в список а оттуда регулярками по идее..
 
Последнее редактирование:

Phoenix78

Client
Read only
Регистрация
06.11.2018
Сообщения
11 790
Благодарностей
5 718
Баллы
113
лучше всю страницу скопируй , а то по одному элементу сложно ось выбрать

заготовка xpath : //span[contains(@class,'icon_green')]/ancestor::table
закинешь весь html, можно ось повыше взять
 
Последнее редактирование:

limarkximus

Client
Регистрация
01.08.2019
Сообщения
116
Благодарностей
73
Баллы
28
лучше всю страницу скопируй , а то по одному элементу сложно ось выбрать

заготовка xpath : //span[contains(@class,'icon_green')]/ancestor::table
закинешь весь html, можно ось повыше взять
не получается весь хтмл скинуть.. попробую сам.. спс)
 

Phoenix78

Client
Read only
Регистрация
06.11.2018
Сообщения
11 790
Благодарностей
5 718
Баллы
113
не получается весь хтмл скинуть.. попробую сам.. спс)
запихай в файл текстовый и кинь в личку. ну или пробуй сам, после ancestor надо указать верхнего родителя. тоесть сначала ищет //span[contains(@class,'icon_green')] , а потом от найденого ищет наверх до указанного элемента. а потом можно добавить уже нужные пути xpath.

53423


таким образом можно найти данные в соседней таблице цеплясь за данные из соседней верхней таблицы. например
//span[contains(@class,'icon_green')]/ancestor::body/table[2]//td[@class='odds']

body - это как пример, так как в моем примере ничего нет кроме как этого элемента.


53424
 

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