Парсинг сайта fightmatrix с помощью Python и Scrapy

10 мая 2022 г.

Одна из часто встречающихся задач - парсинг каких-либо сайтов. Для этой цели удобно использовать фреймворк Scrapy. Парсить буду сайт fightmatrix.com.

Как подготовить проект и попробовать его запустить:

pip install scrapy
scrapy startproject fightmatrix
cd fightmatrix/
scrapy genspider fightmatrix_spider fightmatrix.com
scrapy crawl fightmatrix_spider

Здесь я ставлю scrapy, создаю новый проект, перехожу в папку с проектом и создаю паука. Паук создается в папке fightmatrix/spiders/fightmatrix_spider.py:

 1import scrapy
 2from fightmatrix.items import FightmatrixItem
 3
 4
 5class FightmatrixSpider(scrapy.Spider):
 6    name = 'fightmatrix_spider'
 7    allowed_domains = ['fightmatrix.com']
 8    start_urls = ['http://fightmatrix.com/']
 9
10    def parse(self, response):
11        pass

в папке fightmatrix/items.py создается наш конечный класс, используя который мы будем парсить сайт. Сразу пропишу некоторые поля:

1import scrapy
2
3
4class FightmatrixItem(scrapy.Item):
5    name = scrapy.Field()
6    division = scrapy.Field()
7    current_ranking = scrapy.Field()
8    ufc_record = scrapy.Field()

Обход сайта начнётся с функции parse, буду в ней перебирать все ссылки на различные дивизионы, fightmatrix/spiders/fightmatrix_spider.py:

 1import scrapy
 2from fightmatrix.items import FightmatrixItem
 3
 4
 5class FightmatrixSpider(scrapy.Spider):
 6    name = 'fightmatrix_spider'
 7    allowed_domains = ['fightmatrix.com']
 8    start_urls = ['https://www.fightmatrix.com/mma-ranks/']
 9
10    def parse(self, response):
11        for division_link in response.xpath('//td/a/@href').extract():
12            if (
13                'pound' not in division_link.lower() and 
14                'division' not in division_link.lower()
15            ):  
16                div_name = division_link.split('/')[-2]
17                yield response.follow(
18                    '{0}?PageNum=1'.format(division_link),
19                    callback=self.parse_division,
20                    meta={
21                        'page_num': 1,
22                        'div_name': div_name,
23                    },
24                )

Здесь я получаю ссылки на все дивизионы бойцов и передаю некоторые параметры для функции parse_division, которая будет парсить все доступные дивизионы, fightmatrix/spiders/fightmatrix_spider.py:

 1def parse_division(self, response):
 2    page_num = response.meta.get('page_num')
 3    div_name = response.meta.get('div_name')
 4    fighters_on_page = False
 5    for fighter_link in response.xpath(
 6        '//td/a[@class="sherLink"]/@href',
 7    ).extract():
 8        fighters_on_page = True
 9        yield response.follow(
10            fighter_link,
11            callback=self.parse_fighter,
12            meta={
13                'div_name': div_name,
14            },
15        )
16    # go to next page if are some fighters
17    if fighters_on_page:
18        div_url = response.url
19        div_url = div_url[:div_url.index('?PageNum')]
20        page_num += 1
21        yield response.follow(
22            '{0}?PageNum={1}'.format(div_url, page_num),
23            callback=self.parse_division,
24            meta={
25                'page_num': page_num,
26                'div_name': div_name,
27            },
28        )

Здесь я нахожу всех бойцов, вызываю функцию parse_fighter, она будет парсить страничку конкретного бойца, также я перебираю дальнейшие страницы дивизионов, задавая номер странички с помощью переменной page_num. Теперь осталось написать собственно функцию, которая парсит данные по бойцам, fightmatrix/spiders/fightmatrix_spider.py:

 1def parse_fighter(self, response):
 2    f_m_item = FightmatrixItem()
 3    name = response.xpath(
 4        '//div[@class="posttitle"]/h1/a/text()',
 5    ).extract_first()
 6    div_name = response.meta.get('div_name')
 7
 8
 9    current_ranking = ''
10    for ind, ranking in enumerate(response.xpath(
11        '//td[@class="tdRank"]/div[@class="leftCol"]/a/text()',
12    ).extract()):
13        if ind == 0:
14            current_ranking += ranking
15        else:
16            current_ranking += ', ' + ranking
17
18    for ranking in response.xpath(
19        '//td[@class="tdRank"]/div[@class="rightCol"]/a/text()',
20    ).extract():
21        current_ranking += ', ' + ranking
22
23    some_data = response.xpath(
24        '//tr/td[@class="tdRankAlt"]/div[@class="leftCol"]/strong/text()',
25    ).extract()
26    ufc_record = ''
27    for s_d in some_data:
28        if s_d.count('-') == 2:
29            ufc_record = s_d
30            break
31
32
33    f_m_item['division'] = div_name
34    f_m_item['name'] = name
35    f_m_item['current_ranking'] = current_ranking
36    f_m_item['ufc_record'] = ufc_record
37    yield f_m_item

Запустить паука можно например такой командой:

scrapy crawl fightmatrix_spider -o output.csv -t csv, данные будут сохранены как csv в корне проекта. Паук будет работать минут 5, находится порядка 4к бойцов во всех дивизионах.

Вот ссылка на гитхаб проекта.

Метки

Оставить комментарий

Нужен для связи
captcha

Комментарий отправлен

Спасибо! Он появится на странице после проверки.