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

  • Автор темы Автор темы volody00
  • Дата начала Дата начала

volody00

Client
Регистрация
06.09.2016
Сообщения
991
Реакции
1 111
Баллы
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

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

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

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

Вложения

  • pars.rar
    pars.rar
    20,5 KB · Просмотры: 419
Последнее редактирование модератором:
Для тех кто только начинает осваивать c# хороший пример, спасибо
 
А я буду изучать, потому что хочу сам парсер себе написать. А можно видео запилить?
 
А я буду изучать, потому что хочу сам парсер себе написать. А можно видео запилить?
видео модераторы не пропустили, из-за того что автор (т.е. я) периодически тупил и из-за этого затянул его на 30 мин. Переписывать не хочу. По шагам запускайте и смотрите, что происходит. если что спрашивайте
 
видео модераторы не пропустили, из-за того что автор (т.е. я) периодически тупил и из-за этого затянул его на 30 мин. Переписывать не хочу. По шагам запускайте и смотрите, что происходит. если что спрашивайте
самокритично :)
этот 12-й конкурс прям впечатляет. статья за статьей и все такие интересные :)
 
Пример с человечками хороший, реально на пальцах объяснил
 
Спасибо. Очень доходчиво. Даже коту, вроде меня, понятно. А для Хпата есть подобный плагин для Мозиллы (не люблю я Хрома, он говнюк)
 
а как к примеру спарсить толдько нужный блок? к примеру как на картинке..
30106006.jpg

название команд зачеркнул потому как они не важны и меняются.. в принципе как и другие координаты.. xpath будет везде одинаковый.. только у выделленного будет немного отличаться. так вот как можно спарсить только этот блок ?)
 
получить коллекцию по xpath и вытащить элемент по номеру в коллекции или прямо в xpath указать номер нужного элемента [3] например
 
ну это если они местами и количеством не меняются не меняются...
ну так из скрина не видно уникальных элементов за что можно зацепиться, поэтому и привел как пример номер.
 
ну так из скрина не видно уникальных элементов за что можно зацепиться, поэтому и привел как пример номер.
да да.. это я по ходу тупанул.. в коллекцию я как понимаю сразу весь блок вписать же можно.... т.е от начала до конца .. а так как стил будет в некоторых оличаться он их и выпарсит верно? т.е нужные..
не поможешь как составить?
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> ...
только тогда будет следующая проблема)) как почистить не нужное... тогда все в список а оттуда регулярками по идее..
 
Последнее редактирование:
лучше всю страницу скопируй , а то по одному элементу сложно ось выбрать

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

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

53423


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

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


53424
 

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