Собираем веб-данные с помощью R

Для того чтобы принять участие в сборе данных, вам понадобится иметь в арсенале серьезное оружие. Cначала проверьте, установлено ли у вас R (программное обеспечение для статистических вычислений) и RStudio (среда разработки программного обеспечения для языка программирования R).

Исследуем базы данных команды NBA с сайта Basketball Reference.

Посмотрим на сезон 2013-2014. По правде говоря, выглядит просто как куча таблиц. Но для извлечения данных это прекрасная новость. Давайте попробуем вытащить данные из таблицы с командной статистикой. Для этого возьмем виджет Selector, спустимся на конец таблицы и выделим ее так, чтобы по краям появилось оранжевое обрамление. Должно появиться окно с надписью #team.

Это CSS Selector для таблицы. Теперь мы уже знаем, что нужно, чтобы перенести эти данные в R. Перемещаемся назад в RStudio, создаем новый скрипт-файл R, — настало время извлечь данные.

В скрипт нужно загрузить пакеты, которые принесут нам победу в этом бою.

Следующий пункт: в R появилась новая модная примочка, которую используют все крутые ребята, она называется piping (конвейер данных) и используется с символами %>% или %>>%. Можете воспринимать их как слово THEN (следовательно). Мне же больше нравится %>>% из потрясающего пакета pipeR. Наконец, если увидите #, знайте, что это комментарий, и предшествующая строка кода программой не выполняется.

Загружаем пакеты


c('rvest','dplyr','pipeR', 'knitr') -> packages # knitr вам не нужен, я использую его, чтобы создать этот пост
# (требуется для разметки MarkDown, которую использует в исходном посте автор - прим. пер.)
# для установки dplyr или pipeR используется функция install.packages: install.packages('dplyr') и install.packages('pipeR')
# Если у вас нет rvest, установите devtools: install.packages('devtools')
# Загрузите devtools: library(devtools) и после этого установите rvest с помощью install_github('hadley/rvest')
lapply(packages, library, character.only = T) #петля, которая загружает пакеты из списка packages

Начнем скрейпинг

Шаг первый. Давайте поместим таблицу в R. Так как мы хотим сопоставить её с другими таблицами, необходимо отключить заголовки и посмотреть, сможем ли мы найти строку с именами столбцов. На сайте Basketball-References все таблицы со статистикой начинаются с колонки под названием Rk (что значит rank).

Как мы видим, заголовки находятся в первой строке [в некоторых случаях во второй]. Давайте извлечем данные из этой строки и переместим их в вектор, содержащий строковые элементы. Затем мы используем этот вектор, чтобы задать названия в нашем дата-фрейме (вектор в R — это один из типов данных). Его специфика в том, что все его элементы должны быть одного формата (например, только строковые значения, или только числовые, или только логические).
Заголовки в R следует писать с маленькой буквы. Помимо этого, R просто терпеть не может такие вещи, как пробелы и символы %, / в заголовках, поэтому замените их все точкой. Для того чтобы подготовиться к написанию функции, нужно найти строку с заголовками и взять все данные, что находятся ниже.


'http://www.basketball-reference.com/leagues/NBA_2014.html' -> url
'#team' -> css_page
url %>>%
html %>>%
html_nodes(css_page) %>>%
html_table(header = F) %>>%
data.frame() %>>%
tbl_df() -> total_table
total_table %>>%
filter(X.1 == 'Rk') %>>% as.character -> names
'Rk' %>>% grep(x = total_table$X.1) -> row_of_header #ищем ранг
names %>>% tolower -> names(total_table)
names(total_table) %>>% (gsub('\\%|/','\\.',.)) -> names(total_table)
(row_of_header + 1) %>>% (total_table[.:nrow(total_table),]) -> total_table #пропускаем этот ряд и со следующего идем до конца
total_table %>>% head

Данные уже в R

Теперь у нас есть дата-фрейм в рабочем пространстве, который носит название total_table. Также мы уже нашли и удалили заголовки, но осталось провести еще пару зачисток, прежде чем мы получим нужный набор данных. После некоторых групп стоит звездочка, это значит, что команда вышла в плей-офф. Давайте добавим колонку с логическими значениями для команд, которые вышли в плей-офф и удалим звездочки. Теперь давайте удалим столбец с номерами (rank) и избавимся от строки со средними показателями, так как мы можем сами привести общую статистику при необходимости.

Посмотрите, теперь у нас есть чистый блок данных. Сохраним файл в csv:


total_table %>>% write.csv('Desktop/2014_team_data.csv', row.names = F)

Переведём наш код в функцию

Теперь, когда мы уже научились вытягивать данные с веб-страницы в таблицу, давайте покажем R, как нужно создавать функцию, которая применима к разным случаям. Если мы доведем всё до конца, то сможем делать очень крутые вещи. Например, пройтись по всем сезонам, начиная с 1951 года, или вытянуть данные из различных таблиц с веб-страницы. Мы станем мастерами в работе со всеми данными команды NBA благодаря всего нескольким аргументам, подключению к интернету и R.

Ключ ко всему – это понимание структуры URL. К счастью, наши друзья из Sports Reference упростили нам задачу. Структура URL состоит из трех вещей: основа, лига и год окончания сезона. Мы легко можем научить нашу функцию вставлять три параметра, чтобы сформировать URL.

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

Теперь нам нужно добавить в блок данных сезон и имя таблицы: команда, противник и прочие данные по каждому сезону. Если посмотреть информацию за все года, окажется, что там есть много вещей, которые можно использовать. Функция должна позволять найти данные из любой таблицы. Наконец, нам нужна возможность делать временные отметки, если мы хотим отслеживать изменения в данных за текущий сезон или получать чистые данные из Sports References автоматически.

Итак, вот она наша функция getBREFTeamStatTable


getBREFTeamStatTable <- function(season_end = 2015, table_name = 'team', date = T){ c('rvest','dplyr','pipeR') -> packages
lapply(packages, library, character.only = T)
'http://www.basketball-reference.com/leagues/' -> base
'NBA' -> league
'#' %>>% paste0(table_name) -> css_page
css_page %>>% paste0(" , ", css_page,' a') -> css_id
table_name %>>% tolower -> table_name
table_name %>>% paste('stats', sep = "_") -> table
base %>>% paste0(league,'_',season_end,".html") -> url
url %>>% ## get table
html %>>%
html_nodes(css_page) %>>%
html_table(header = F) %>>% data.frame() %>>% tbl_df() -> df

if(df$X.1[1] == 'Rk'){
df %>>%
filter(X.1 == "Rk") %>>% as.character -> names
'Rk' %>>% grep(x = df$X.1) -> row_of_header #находим ранг
(row_of_header + 1) %>>% (df[.:nrow(df),]) -> df #пропускаем этот ряд и идем до конца
names %>>% tolower-> names(df)} else{
df %>>%
filter(X.1 == "Rk") %>>% as.character -> names
'Rk' %>>% grep(x = df$X.1) -> row_of_header #находим ранг
(row_of_header + 1) %>>% (df[.:nrow(df),]) -> df #пропускаем этот ряд и идем до конца
names %>>% tolower-> names(df)
}
names(df) %>>% (gsub('\\%|/','\\.',.)) -> names(df)
NULL -> df$rk
c('team','arena') -> table_name_character
df[,!(df %>>% names) %in% table_name_character] %>>%
apply(2, function(x) gsub('\\,','',x) %>>% as.numeric(x)) ->
df[,!(df %>>% names) %in% table_name_character] #убираем запятые, переводим в числовой формат
df$team %>>% grepl(pattern = '\\*') -> df$playoff_team
df$team %>>% (gsub('\\*','',.)) -> df$team
df %>>% nrow() -1 -> rows
df[1:rows,] -> df
(season_end-1) %>>% paste0("-",season_end) -> season
##Собираем ID команд
url %>>% ## get table
html %>>%
html_nodes(css_id) %>>%
html_attrs() %>>% unlist %>>% as.character -> stems
stems[3:length(stems)] -> stems #пропускаем первые два ряда, потому что там заголовки
stems %>>% (gsub('\\/|.html|teams','',.)) %>>% #убираем ненужный текст
(gsub(season_end,'',.)) -> bref_team_id #убираем год, получаем id команды
data.frame(season,table_name = table, bref_team_id, df) -> df #объединяем в 1 df
if(date == T){
Sys.time() -> df$scrape_time #add scrape time if you want it
}
return(df)
}

Проверим


getBREFTeamStatTable() -> team2015
team2015 %>>% kable('html', table.attr='id="team2015"')

Исходный код и функция

Источник: Журналистика данных

Data Scientist # 1

Машинное обучение, большие данные, наука о данных, анализ данных, цифровой маркетинг, искусственный интеллект, нейронные сети, глубокое обучение, data science, data scientist, machine learning, artificial intelligence, big data, deep learning

Данные — новый актив!

Эффективно управлять можно только тем, что можно измерить.
Copyright © 2019 Data Scientist. Все права защищены.