Найти в Дзене
Властелин машин

Программируем специфику парсера на Python

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

Например, для рассматриваемых сайтов с объявлениями о продаже товаров либо итогами прошедших соревнований (см. подробнее здесь) одним из этапов является формирование нового url после прохождения по записям предыдущего. При этом, если для такого сайта, как Avito достаточно задать программе один раз адрес, а затем менять параметр 'p', регулирующий перебор страниц, то, например, для спортивных мероприятий это может представлять больше трудностей (на скриншоте ниже пример запроса для получения списка продаваемой недвижимости на Avito):

-2

Например, для парсинга деталей проведенных организацией по смешанным единоборствам UFC поединков, нам потребуется сначала перейти на страницу со ссылками на события, затем на конкретный турнир/событие и только после на поединок. На скриншотах ниже мы перемещаемся с первого турнира на третьей странице (параметр 'page') к конкретному списку схваток:

-4

В этом случае, чтобы не нарушать неизменную часть парсера, можно немного абстрагироваться и представить страницу для программы как объект, получаемый из текущей страницы UFC, ссылок на все заданные на ней турниры и значения текущего счетчика. Специфическая часть парсера для UFC поединков сама позаботиться о том, чтобы передавать по очереди ссылки на все турниры на заданной странице, затем по достижении последней перейти на следующую и передать ссылку на первый турнир.

Ниже привожу программную реализацию части класса UFCFightsParser - конструктора и метод get_next_url, получающий следующий адрес.

Некоторые используемые в UFCFightsParser методы реализованы в базовом классе ItemsParser (например, get_items_list_from2tags, подробнее здесь).

class UFCFightsParser(ItemsParser):


def __init__(self,cur_url, events_url, delay, page_param, tag_container_events,tag_event, tag_container_el, tag_el, rec_ign_bef_stop_max=REC_IGN_BEF_STOP_MAX, pages_load_stop_num=PAGES_LOAD_STOP_NUM):

# страница со ссылками на события/турниры
self.events_url = events_url
# строчные описания контейнера и внутренних тегов с ссылками
# на все события на странице events_url
self.tag_container_events = tag_container_events
self.tag_event = tag_event
# параметр, отвечающий в events_url
# за номер страницы, для UFC - 'page'
self.page_param = page_param
# parse_url - метод ItemsParser для разбиения url
# на части: адрес сайта, относительный путь
# по папкам, параметры и их значения
self.url_base, self.url_add, self.params = self.parse_url(events_url)
# задержка при скачивании каждой страницы с сайта
self.delay=delay
# конструктор базового класса ItemsParser
super().__init__(cur_url, tag_container_el,tag_el, rec_ign_bef_stop_max, pages_load_stop_num)
events_hrefs_tags, _ = self.get_items_list_from2tags(self.events_url,self.tag_container_events,self.tag_event, self.delay)
# ссылки на все события на странице events_url
self.events_hrefs = [item.attrs['href'] for item in events_hrefs_tags]
self.events_num = len(self.events_hrefs)
# self.event_ind - индекс текущего события,
# которое ищем путем поиска
# его url в списке. Последний символ отсекаем, так как
# в настройках cur_url адрес задается с ? на конце
# для простоты отделения частей адреса
# от параметров в методе self.parse_url
self.event_ind = self.events_hrefs.index(self.cur_url[:-1])
# дата, место события, число зрителей
self.event_date, self.event_place, self.event_attendence = self.get_event_inf(cur_url,'i,class,b-list__box-item-title')



# возвращает ссылку на новый турнир
# для функции обхода start_items_parser
def get_next_url(self):
# если так, то надо переходить на новую страницу UFC
if self.event_ind == self.events_num-1:
# self.params - словарь, хранящий параметры
# и их значения в url
self.params[self.page_param] = \
str(int(self.params[self.page_param])+1)
# соединяем части для образования
# ссылки на новую страницу UFC
self.events_url = self.make_url_from_parts(self.url_base, self.url_add, self.params)
# обновляем поля, описанные в конструкторе
events_hrefs_tags, _ = self.get_items_list_from2tags(self.events_url,self.tag_container_events,self.tag_event, self.delay)
if events_hrefs_tags:
self.events_hrefs = [item.attrs[
'href'] for item in events_hrefs_tags]
self.event_ind = 0
self.events_num = len(self.events_hrefs)
self.event_date, self.event_place, self.event_attendence = \
self.get_event_inf(self.events_hrefs[self.event_ind]+
"?",'i,class,b-list__box-item-title')

return self.events_hrefs[self.event_ind]+
"?"
# если турниры в рамках данной страницы
# еще есть, то просто увеличиваем счетчик ссылок
else:
self.event_ind = self.event_ind+1
return self.events_hrefs[self.event_ind]+
'?'