Узнайте о возможностях парсинга в R и напишите простую функцию для получения музыкальных чартов США за любую дату в прошлом.

Нам посчастливилось жить в эпоху, когда мы можем получить практически любой фактоид, какой захотим. Если мы хотим найти альбомы Top Billboard 200 1980 года, нам просто нужно зайти на официальный сайт Billboard 200, ввести дату, и вверх по списку появится красивый дисплей с обложками альбомов и всем прочим.

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

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

В этой статье мы собираемся рассмотреть элементарный парсинг веб-страниц в R с использованием пакетов rvest и xml2. Эти пакеты очень просты в использовании. К концу статьи мы создадим функцию с именем get_charts(), которая будет принимать дату, версию диаграммы и вектор ранжированных позиций в качестве аргументов и мгновенно возвращать записи диаграммы в этих позициях на эту дату. Я надеюсь, что это побудит вас попробовать его на бесчисленном множестве других источников веб-данных.

Для этого урока вам необходимо установить пакеты dplyr, xml2 и rvest. Вам также необходимо использовать браузер Google Chrome.

Приступаем к работе - собираем список Billboard Hot 100 на этой неделе

Мы начнем с того, что выясним, как очистить Billboard Hot 100 на этой неделе, чтобы получить ранжированный список исполнителей и названий. Если вы взглянете на страницу Hot 100 по адресу https://www.billboard.com/charts/hot-100, то сможете увидеть ее общую структуру. В нем есть различные баннеры и реклама, и здесь происходит довольно много всего, но вы сразу видите, что на странице есть список Hot 100, поэтому мы знаем, что информация, которую мы хотим, находится на этой странице, и нам нужно будет перемещаться по базовым код, чтобы найти это.

Пакеты rvest и xml2 в R разработаны, чтобы упростить извлечение и анализ глубоко вложенного кода HTML и XML, который сегодня стоит за большинством веб-сайтов. HTML и XML отличаются - здесь я не буду вдаваться в подробности, но обычно вам понадобится rvest, чтобы раскопаться и найти конкретные узлы HTML, которые вам нужны, и xml2, чтобы извлечь атрибуты XML, содержащие определенные данные. вы хотите.

После загрузки наших пакетов первое, что нам нужно сделать, это прочитать html с веб-страницы, чтобы у нас была отправная точка для поиска нужных узлов и атрибутов.

# required libraries
library(rvest)
library(xml2)
library(dplyr)
# get url from input
input <- "https://www.billboard.com/charts/hot-100"
# read html code from url
chart_page <- xml2::read_html(input)

Теперь у нас есть объект списка chart_page, который содержит два элемента: один для заголовка веб-страницы, а другой для тела веб-страницы.

Теперь нам нужно проверить веб-сайт с помощью Chrome. Щелкните веб-сайт правой кнопкой мыши и выберите «Проверить». Это вызовет панель, показывающую вам весь вложенный код HTML и XML. Когда вы наведете указатель мыши на этот код, вы увидите, что часть страницы, на которую он ссылается, выделена. Например, здесь вы можете увидеть, что интересующий нас раздел выделяется, когда я наводю указатель мыши на выделенный <div class = "container chart-container ...">, что имеет смысл.

Если вы продолжите расширять этот раздел и будете следовать этому методу, вы в конечном итоге перейдете к конкретному коду HTML и XML, который заполняет список диаграмм. Если вы посмотрите достаточно внимательно, то увидите, что все элементы диаграммы имеют формат <div class='chart-list-item' .... Мы можем использовать функцию html_nodes() rvest, чтобы погрузиться в тело страницы, и функцию xml2 xml_find_all(), чтобы захватить все <div> узлы, которые имеют chart-list-item в качестве своего класса.

# browse nodes in body of article
chart <- chart_page %>% 
    rvest::html_nodes('body') %>% 
    xml2::xml_find_all("//div[contains(@class, 'chart-list-item  ')]")
View(chart)

Это дает нам вложенный нумерованный список, который мы можем щелкнуть и просмотреть, например:

Теперь мы замечаем, что в фактическом XML-классе, содержащем интересующие нас данные, на самом деле есть пробел после chart-list-item, поэтому, если мы переписываем нашу предыдущую команду, чтобы иметь дополнительное пространство, это должно проанализировать именно те узлы, которые имеют данные, которые мы хотим . Затем мы можем использовать функцию xml_attr() xml2, чтобы вывести ранг, исполнителя и название в векторы.

# scrape data
  chart <- chart_page %>% 
    rvest::html_nodes('body') %>% 
    xml2::xml_find_all("//div[contains(@class, 'chart-list-item  ')]")
# get rank, artist and title as vector
rank <- chart %>% 
    xml2::xml_attr('data-rank')
  
  artist <- chart %>% 
    xml2::xml_attr('data-artist')
  
  title <- chart %>% 
    xml2::xml_attr('data-title')
# create dataframe, remove NAs and return result
  chart_df <- data.frame(rank, artist, title)
  chart_df <- chart_df %>% 
    dplyr::filter(!is.na(rank))
View(chart_df)

И вот у нас есть хороший список того, что мы хотим, и хорошо знать, что есть 100 строк, как и ожидалось:

Обобщение, чтобы вытащить любую диаграмму с любой даты

Так что это была большая исследовательская работа, и копаться в HTML и XML может раздражать. Есть плагины Chrome, такие как SelectorGadget, которые могут помочь с этим, но я считаю их непредсказуемыми и предпочитаю просто исследовать исходный код, как я сделал выше.

Однако теперь, когда мы знаем, где находятся данные, мы можем сделать это намного более мощным. Если вы поиграете с сайтом billboard.com, вы заметите, что можете перейти к определенной диаграмме на любую историческую дату, просто отредактировав URL-адрес. Так, например, если вы хотите увидеть Billboard 200 по состоянию на 22 марта 1983 г., просто перейдите на https://www.billboard.com/charts/billboard-200/1983-03-22.

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

get_chart <- function(date = Sys.Date(), positions = c(1:10), type = "hot-100") {
# get url from input and read html
  input <- paste0("https://www.billboard.com/charts/", type, "/", date) 
  chart_page <- xml2::read_html(input)
# scrape data
  chart <- chart_page %>% 
    rvest::html_nodes('body') %>% 
    xml2::xml_find_all("//div[contains(@class, 'chart-list-item  ')]")
rank <- chart %>% 
    xml2::xml_attr('data-rank')
  
  artist <- chart %>% 
    xml2::xml_attr('data-artist')
  
  title <- chart %>% 
    xml2::xml_attr('data-title')
# create dataframe, remove nas and return result
  chart_df <- data.frame(rank, artist, title)
  chart_df <- chart_df %>% 
    dplyr::filter(!is.na(rank), rank %in% positions)
chart_df
}

Хорошо, давайте проверим нашу функцию. Какие синглы входили в топ-20 22 марта 1983 года?

Какие альбомы входили в десятку лучших на 1 апреля 1970 года?

Что мне нравится в rvest и xml2, так это то, насколько они просты и мощны. Посмотрите, насколько компактным является содержание функции - для создания чего-то достаточно мощного не потребовалось много времени. Попробуйте с некоторыми другими источниками веб-данных и не стесняйтесь добавлять в репозиторий Github здесь, если вы создаете какие-либо другие классные функции очистки.

Изначально я был чистым математиком, затем стал психометриком и специалистом по анализу данных. Я с энтузиазмом отношусь к применению всех этих дисциплин к сложным человеческим вопросам. Я также помешан на кодировании и большой поклонник японских ролевых игр. Найдите меня в LinkedIn или Twitter.

Подробнее о rvest здесь и xml2 здесь.