Пакет rvest предназначен для извлечения данных из веб-страниц — веб-скрепинга (web scraping). rvest реализован как «обертка» над пакетами XML и httr и позволяет скачивать и обрабатывать html- и xml-файлы. Функции rvest можно объединять в последовательности команд при помощи оператора %>% из пакета magrittr.

Установка пакета

install.packages("rvest")

Подготовка к работе

В качестве примера рассмотрим, как с помощью rvest извлечь информацию о сериале «Светлячок» («Firefly») с сайта IMDB. Скачаем html-страницу фильма с помощью функции html():

library(rvest)
firefly <- html("http://www.imdb.com/title/tt0303461/")

Нам необходимо знать CSS-селектор или путь XPath к элементу HTML, содержащему нужную информацию. Чтобы их определить понадобится консоль разработчика (для Google Chrome и Яндекс.Браузер), а в случае использования Firefox — дополнения Firebug и Firepath.

Извлечение данных: простые случаи

Допустим, мы хотим определить рейтинг фильма. Его можно получить из текста, расположенного в «звезде» или в строке Ratings.

rating.png

Пути к этим элементам по CSS-селекторам будут следующими

#overview-top > div.star-box.giga-star > div.titlePageSprite.star-box-giga-star
#overview-top > div.star-box.giga-star > div.star-box-details > strong > span

Для определения элементов достаточно последней части пути. Для «звезды» это будет

div.titlePageSprite.star-box-giga-star

Вы можете проверить это, набрав $$("div.titlePageSprite.star-box-giga-star") в консоли разработчика.

Для второго варианта (строки Ratings) рейтинг c помощью rvest можно получить следующим образом:

firefly %>% html_node("strong > span") %>% html_text() %>% as.numeric()

Функция html_node() возвращает первый элемент, который соответствует заданному пути, html_text() извлекает текстовое содержимое из этого элемента, а as.numeric() преобразует его в цифру.

Знак > в пути можно убрать, записав:

firefly %>% html_node("strong span") %>% html_text() %>% as.numeric()

Вместо CSS-селекторов можно воспользоваться путем XPath:

firefly %>% html_node(xpath="//strong//span") %>% html_text() %>% as.numeric()

Для извлечения списка актеров воспользуемся функцией html_nodes(), которая возвращает все элементы, соответствующие данному пути, т.е. весь список актеров:

> firefly %>% html_nodes("#titleCast .itemprop span") %>% html_text()
[1] "Nathan Fillion"  "Gina Torres"     "Alan Tudyk"      "Morena Baccarin" "Adam Baldwin"    "Jewel Staite"    "Sean Maher"      "Summer Glau"     "Ron Glass"

Тогда как html_node() возвращает лишь первый элемент:

> firefly %>% html_node("#titleCast .itemprop span") %>% html_text()
[1] "Nathan Fillion"

Получим теперь последние сообщения, появившиеся на доске объявлений (message board). Они собраны в таблицу. Всего таблиц в документе четыре. В этом можно убедиться, выполнив

firefly %>% html_nodes("table")

Нас интересует 3-я таблица, и чтобы ее получить понадобится функция [[]]. А чтобы извлечь материал из таблицы HTML, применим к ней функцию html_table() из rvest:

> firefly %>% html_nodes("table") %>% .[[3]] %>% html_table()
                                  X1               X2
1                      Whirring guns    billandbonnie
2           Joss Whedon and his fans     aradiasnight
3               Episode List Problem            rv_jt
4               Mal, Simon or Jayne?     VivienCastro
5                  Order of watching         kolias18
6 Finally got around to watching it. SacrosanctPariah

Извлечение данных, расположенных на связанных страницах

Рассмотрим данные о биржевых инвестиционных фондах (Exchange Traded Funds, ETF), размещенные на сайте Лондонской фондовой биржи.

etf.png

Вначале получим первую страницу с данными о ETF.

library(rvest)
url <- "http://www.londonstockexchange.com/exchange/prices-and-markets/ETFs/ETFs.html"
content <- html(url)

Как видно, данные собраны в таблицу, которая на момент написания этого текста располагалась на 42 страницах. Возможно, теперь она стала еще длиннее. Нашей первой задачей является автоматическое получение числа страниц этой таблицы.

С помощью CSS-селекторов путь к строке, содержащей число страниц, записывается как:

#fullcontainer > div.column1_nomenu > div:nth-child(11) > p.floatsx

Для того, чтобы получить содержимое этой строки достаточно лишь последнего селектора

# строка с номером страницы
content %>% html_node("p.floatsx") %>% html_text()

Теперь из этой строки ("Page 1 of 42") нужно извлечь подстроку с номером страницы.

Функция regexpr()

> regexpr("of",pages)
[[1]]
[1] 9
attr(,"match.length")
[1] 2

возвращает номер позиции начала подстроки "of" в строке pages и длину этой подстроки.

Сохраним эти данные в переменных ppos и plen, соответственно:

result <- regexpr("of",pages)
ppos <- result[[1]]
plen <- attr(result,"match.length")

Извлечем из строки pages подстроку с общим числом страниц и преобразуем ее в число

pages <- substr(pages,ppos+plen,nchar(pages)) %>% as.numeric()

Теперь в переменной pages хранится число страниц.

Таблица фондов является первой из таблиц, хранящихся в элементе table. Ее содержимое извлекаем следующим образом:

table <- content %>% html_nodes("table") %>% .[[1]] %>% html_table()

Нам понадобится только первые 6 колонок

table <- table[1:6]

Кроме того, добавим время, в которое получены данные. Текущее время возвращает функция Sys.time():

table["Timestamp"] <- Sys.time()

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

Но сначала нужно создать таблицу данных, в которую мы будем складывать результаты:

etf_table <- data.frame()

Добавлять к ней строки, полученные из очередной страницы, будем с помощью функции rbind():

etf_table <- rbind(etf_table,table)

Теперь можно записать весь цикл операций

# для каждой страницы таблицы
for (p in 1:pages) {
  cur_url <- paste(url,"?&page=",p,sep="")
  # скачать html-страницу
  content <- html(cur_url)
  # извлечь из нее таблицу
  table <- content %>% html_nodes("table") %>% .[[1]] %>% html_table()
  # только первые 6 колонок
  table <- table[1:6]
  # добавить колонку со временем скачивания
  table["Timestamp"] <- Sys.time()
  # и присоединить полученные строки к таблице результатов
  etf_table <- rbind(etf_table,table)
}

Другие важные функции

  • Извлечь имя элемента HTML можно с помощью функции html_tag(), текст — с помощью html_text(), единичный атрибут — html_attr(), а список всех атрибутов — html_attrs().
  • Перемещаться по сайту так, как это делает браузер можно с помощью функций html_session(), jump_to(), follow_link(), back() и forward(). Извлекать из веб-страницы формы запросов, изменять значения их полей и отправлять можно с помощью html_form(), set_values() и submit_form() соответственно.
  • Функция guess_encoding() определяет кодировку текста. Исправить возможные проблемы с кодировкой можно функцией repair_encoding().

Более подробную информацию смотрите в документации к пакету.



Комментарии

comments powered by Disqus