CoderCastrov logo
CoderCastrov
Питон

Как создать парсер для извлечения спортивных данных с использованием Python

Как создать парсер для извлечения спортивных данных с использованием Python
просмотров
6 мин чтение
#Питон

Эта статья призвана обучить, как создать простой парсер для извлечения статистики отдельных игр NBA с веб-сайта theScore. В этом примере вы научитесь извлекать статистику отдельных игроков, включая имя игрока, количество минут на площадке и количество набранных очков за четыре игры NBA на две разные даты, а затем сохранять эти данные в файл CSV.


Предварительная работа

Прежде чем мы сможем извлечь данные, нам необходимо определить URL-адреса, содержащие данные, которые мы хотим извлечь. Просто, перемещаясь по сайту, мы можем найти пример статистики игр в URL-адресе:

https://www.thescore.com/nba/events/148019/stats

Переход по указанному выше URL-адресу приведет вас на страницу theScore, на которой отображаются таблицы статистики для данной игры, как показано ниже:

изображение через: theScore.com

Используя указанный выше URL-адрес, мы просто можем заменить идентификатор на идентификатор любой игры и получить все статистические данные для этой конкретной игры. Это наша конечная цель, но сначала нам нужно найти способ определить идентификаторы для интересующих нас игр.

Чтобы определить идентификаторы игр, мы можем работать в обратном порядке, описывая путь к этой странице. Делая это, мы можем определить наш исходный URL-адрес как:

https://www.thescore.com/nba/events/date/2019-04-28

Теперь мы можем использовать указанный выше URL-адрес в качестве отправной точки, получить идентификаторы игр на эту дату, перейти на страницу статистики этих игр и извлечь нужную нам статистику.

Мы готовы перейти к проекту.


План проекта

Сам проект очень простой и потребует выполнения всего четырех шагов:


Установка Scrapy

Начнем с установки Scrapy. Используя conda, выполните команду:

conda install scrapy

Если вы используете терминал, вы можете вместо этого выполнить:

pip install scrapy

Создание проекта Scrapy

Теперь, когда Scrapy установлен, мы можем легко создать новый проект, который мы назовем "stat_scraper", и перейти в проект с помощью команды:

scrapy startproject stat_scraper

Мы увидим, что в нашей рабочей директории создан каталог под названием "stat_scraper", который будет выглядеть примерно так:

Дерево каталогов проекта Scrapy

Затем мы можем перейти в каталог "stat_scraper" и создать наш парсер.

cd stat_scraper

Создание паука

Теперь самая интересная часть. Паук будет делать всю работу за нас. Нам просто нужно дать ему соответствующие инструкции. Для примера мы назовем нашего паука "stat_spider". Чтобы инициализировать его, выполните следующую команду:

scrapy genspider stat_spider thescore.com/nba/events

URL в конце команды указывает начальную точку URL, которые мы хотим использовать для нашего паука.

Используя вашу любимую IDE или текстовый редактор, откройте файл stat_spider.py, который находится в директории spiders. Он должен выглядеть так:

import scrapyclass StatSpiderSpider(scrapy.Spider):
    name = 'stat_spider'
    start_urls = ['[http://thescore.com/nba/events/'](http://thescore.com/nba/events/')]def parse(self, response):
    pass

Мы можем удалить 'allowed_domains', так как это необязательно.

Доступ к странице с датами

Метод 'parse' будет запускаться при запуске нашего паука. Вспомним шаги и сначала определим начальные URL-адреса, с которых наш паук начнет сканирование.

В этом примере давайте используем 28 и 29 апреля 2019 года в качестве наших дат. Мы можем определить URL-адрес для доступа к играм в эти даты, используя следующий код для нашего метода 'parse':

def parse(self, response):
    dates = ['2019-04-28', '2019-04-29']
    for date in dates:
        yield response.follow(self.start_urls[0] + 'date/' + date, 
                              self.access_stats_page)

Помните, что start_urls - это список, содержащий только значение http://thescore.com/nba/events/. Затем мы объединяем его с 'date/', а затем с каждой датой, хранящейся в созданном нами списке dates, что приводит к нашему начальному URL-адресу: http://thescore.com/nba/events/date/2019–04–28, а затем переходим к 2019–04–29 для нашего второго URL-адреса.

Второй аргумент, переданный в response.follow(), является обратным вызовом для другого метода, который мы позже создадим.

Идентификация идентификаторов игр

Теперь мы переходим к этапу определения идентификатора каждой игры. Перейдя по одному из указанных выше URL-адресов (_http://thescore.com/nba/events/date/2019–04–28), _мы будем анализировать структуру HTML, щелкнув правой кнопкой мыши на одной из игр и выбрав «Инспектировать».

Структура имеет одинаковый класс для каждой игры с a-тегом, находящимся выше (его родительским элементом) и содержащим относительный путь к каждой игре в эту дату.

Сказанное выше означает, что мы можем легко получить доступ к статистике каждой игры следующим образом:

Получение доступа к странице статистики каждой игры

Давайте сделаем это в методе с названием «access_stats_page»:

def access_stats_page(self, response):
    init_url = '[http://thescore.com'](http://thescore.com')
    pth = '//div[[@class](http://twitter.com/class)="EventCard__eventCardContainer--3hTGN"]/../@href'
        
    for href in response.xpath(pth).extract(): 
        yield response.follow(init_url + href + '/stats', 
                              self.scraper)

Мы используем переменную с названием «pth» для хранения xpath, который обращается к div с классом EventCard__eventCardContainer--3hTGN, затем обращается на один уровень выше с помощью '/../' и получает href.

Мы перебираем каждый href и используем его для создания нового URL, объединяя «http://thescore.com» с href (например, «/nba/events/148019») и добавляя в конец «/stats/». Затем мы следуем по этому URL, используя обратный вызов на другой метод, который будет парсить данные.

Парсинг данных

Наконец, мы можем выполнить парсинг данных, которые нам нужны. Нам нужно определить CSS-селекторы для каждого из интересующих нас элементов данных. Аналогично определению идентификаторов игр, мы щелкаем правой кнопкой мыши на одной из строк с игроками и выбираем пункт "Инспектировать элемент":

Здесь мы видим, что каждая строка с именем игрока и его статистикой имеет общий класс BoxScore__statLine--3Daky. Имена игроков имеют класс BoxScore__rosterCell--1mCYH, а каждая отдельная статистика (MIN, PTS, REB и т. д.) перечислена ниже с тем же классом BoxScore__statCell--1mqbI.

Мы будем использовать эти селекторы для доступа к значениям в нашем методе "парсера":

def scraper(self, response):
    player_rows = response.css(".BoxScore__statLine--3Daky")
    name_sel = ".BoxScore__rosterCell--1mCYH::text"
    min_sel = ".BoxScore__statCell--1mqbI:nth-child(2)::text"
    pts_sel = ".BoxScore__statCell--1mqbI:nth-child(3)::text"
    
    for player in player_rows:
        name = player.css(name_sel).extract()
        minutes = player.css(min_sel).extract()
        points = player.css(pts_sel).extract()
        yield {
            'name': name,
            'minutes': minutes,
            'points': points
        }

Сначала мы используем вышеуказанные классы в качестве наших CSS-селекторов - используйте nth-child для определения каждого дополнительного столбца данных, к которому нужно получить доступ, например, для подборов это будет 4, для блоков - 7 и т. д.

Затем перебираем каждого игрока, выбранного переменной player_rows, и извлекаем имя, минуты и очки, используя созданные нами переменные name_sel, min_sel и pts_sel.

Завершаем парсер, возвращая сгенерированный из этих данных вывод.

Запуск паука

Теперь, когда мы закончили создание паука, мы можем запустить его из командной строки:

scrapy crawl stat_spider

Вывод в командной строке должен вернуть много таких строк:

Scrapy spider output

Если так, то поздравляю, паук работает отлично. Осталось только сохранить все данные в CSV файл.

Сохранение данных в CSV файл

Для этого мы снова запустим паука, но с немного другой командой:

scrapy crawl stat_spider -o nba_stats.csv

Флаг -o позволяет указать вывод паука, который мы присваиваем CSV файлу с именем nba_stats. Вы также можете сохранить его в файл .json или .xml.

При открытии файла вы увидите:

First 12 Records

Всего будет 121 строка — 15 игроков в каждой команде, 2 команды в каждой игре, 4 игры в сумме = 120 + 1 строка для названий столбцов.

Вы можете повторить это на любом количестве сайтов, чтобы извлечь любые данные, которые вам нужны. Обязательно следуйте правилам robots.txt, если сайт имеет такой файл. В противном случае, что делать с данными, зависит только от вас.


Финальный паук выглядит так:

import scrapyclass StatSpiderSpider(scrapy.Spider):
    name = 'stat_spider'
    start_urls = ['[http://thescore.com/nba/events/'](http://thescore.com/nba/events/')]    def parse(self, response):
        dates = ['2019-04-28', '2019-04-29']
        for date in dates:
            yield response.follow(self.start_urls[0]+'date/'+date, 
                                  self.access_stats_page)    def access_stats_page(self, response):
        init_url = '[http://thescore.com'](http://thescore.com')
        pth = '//div[[@class](http://twitter.com/class)="EventCard__eventCardContainer--3hTGN"]/../@href'
        
        for href in response.xpath(pth).extract():
            yield response.follow(init_url + href + '/stats', 
                                  self.scraper)    def scraper(self, response):
        player_rows = response.css(".BoxScore__statLine--3Daky")
        name_sel = ".BoxScore__rosterCell--1mCYH::text"
        min_sel = ".BoxScore__statCell--1mqbI:nth-child(2)::text"
        pts_sel = ".BoxScore__statCell--1mqbI:nth-child(3)::text"
        
        for player in player_rows:
            name = player.css(name_sel).extract()
            minutes = player.css(min_sel).extract()
            points = player.css(pts_sel).extract()
            yield {
                'name': name,
                'minutes': minutes,
                'points': points
            }

Не стесняйтесь связаться со мной на LinkedIn для уточнений или обсуждения.