Парсинг сайта fightmatrix с помощью Python и Scrapy
Одна из часто встречающихся задач - парсинг каких-либо сайтов. Для этой цели удобно использовать фреймворк 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
:
1 import scrapy 2 from fightmatrix.items import FightmatrixItem 3 4 5 class 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
создается наш конечный класс, используя который мы будем парсить сайт. Сразу пропишу некоторые поля:
1 import scrapy 2 3 4 class 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
:
1 import scrapy 2 from fightmatrix.items import FightmatrixItem 3 4 5 class 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
:
1 def 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
:
1 def 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к бойцов во всех дивизионах.
Вот ссылка на гитхаб проекта.