HTML

API у веб-сайтов бывает очень редко. А часто оно бывает еще и платным. Поэтому нужно как-то обходится без него. В таких случаях пытаются извлечь информацию с сайта вручную.

Любой сайт написан на языке HTML. Давайте откроем сайт яндекса. Мы видим его следующим образом.

На самом деле мы можем посмотреть его HTML-код. Для этого нужно в браузере (в Safari не получится) нажать правой кнопкой мыши и выбрать “Просмотр кода страницы”.

Можно увидеть вот такой страшный и непонятный код.

Это и есть язык верстки сайтов HTML – Hyper Text Markup Language. Посмотрим как выглядит самый простой html-код.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Какая-то страница</title>
  </head>
  <body>
      <H1>Какой-то заголовок</H1>
      <p>Какой-то текст</p>
  </body>
</html>

Попробуйте создать текстовый файлик с разрешением .html и посмотреть как он выглядит в браузере.

Любой html-код из нескольких частей:

  1. <!DOCTYPE html> – это надпись идет в самом начале. В ней указывается, что этот файлик - html, а также указывается его версия.
  2. <html></html> – главный тег, внутри которого записывается весь html-код.
  3. <head></head> – первый основной тег, внутри которого записывается какая-то мета-информация, которая не будет видна на сайте. Например, кодировка или заголовок страницы.
  4. <body></body> – второй основной тег, в котором записывается вся информация, которую видит пользователь на странице.
Как вы могли заметить в html основная концепция заключается в тегах. Есть открывающий тег ( ) и закрывающий тег (

). Между этими тегами могут быть другие теги или что-то другое, например текст.

Рассмотрим некоторые теги:

  • H1-H6 – теги для заголовков
  • p – тег для абзацев
  • b – тег для жирного текста
  • em – тег для курсивного текста
  • ul и li – тег для создания списков

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

У тегов также бывают атрибуты. Они записываются в открывающем теге. Вот например тег с атрибутом id равным 1.

<div id="1"> </div>

Необязательно знать все теги и их атрибуты, главное понимать как они записываются.

XPath

XPath – это язык для работы с XML/HTML. Он предназначен, чтобы находить какие-то части внутри html файла. Давайте попробуем спарсить следующий html-документ. Предположим, что тут находятся оценки за какие-то контрольные мероприятия.

Чтобы работать с html-страницами нам понадобится пакет rvest. Давайте скачаем и установим его.

Первое, что нужно сдеалать это прочитать этот html-документ. Сделать это можно с помощью функции read_html. Можно передать ей переменную, в которой находится html или ссылку.

Полученный объект является листом. Можно посмотреть его иерархию.

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

## {xml_nodeset (4)}
## [1] <div class="HW" id="1">\n                <p>9</p>\n              </div>
## [2] <div class="HW" id="2">\n                <p>10</p>\n              </ ...
## [3] <div class="HW" id="3">\n                <p>6</p>\n              </div>
## [4] <div class="TEST" id="1">\n                <p>7</p>\n              < ...

С помощью такой команды мы нашли все div, которые находятся в body. Нам нужен первый div, а внутри него нужен p.

## {xml_nodeset (1)}
## [1] <p>9</p>

Вот таким образом мы добрались до оценке по первой домашки. Но это еще не все. Пока что у нас все тот-же html-документ. Нам нужно вытащить из него 9. То есть вытащить текст, который находится в контейнере p. Используем функцию html_text.

Вот так мы получили оценку за первую домашку. Она имеет строковый формат. С помощью функции as.numeric ее можно перевести в числовой и как-то использовать уже в R.

Пакет rvest поддерживает pipe, который вы можете удобно использовать.

## [1] "9"

Можно получить оценки за все работы сразу.

## [1] "9"  "10" "6"  "7"

Подход, когда вы прописываете путь к нужному блоку напрямую не очень хорош. Сайт может немного поменяться и весь ваш парсер сломается. Чтобы такого не просиходило нужно стараться использовать атрибуты контейнеров. Это бывает еще и удобно. Допустим мы хотим получить оценки только по домашкам. Видно, что они лежат в блоках div, у которых атрибут class равен HW.

  • //div – находит в html-документе все контейнеры div (на любом уровне иерархии)
  • //div[@class=1] – находит в html-документе все контейнеры div (на любом уровне иерархии) с атрибутом class равным 1
  • //*[@class=1] – находит в html-документе все блоки с атрибутом class равным 1

Найдем все конейнеры div, в которых class равен HW.

## {xml_nodeset (3)}
## [1] <div class="HW" id="1">\n                <p>9</p>\n              </div>
## [2] <div class="HW" id="2">\n                <p>10</p>\n              </ ...
## [3] <div class="HW" id="3">\n                <p>6</p>\n              </div>

Вы можете использовать html_nodes несколько раз.

## {xml_nodeset (3)}
## [1] <p>9</p>
## [2] <p>10</p>
## [3] <p>6</p>

Выведем оценки и найдем среднее.

## [1] 8.333333

Можно было также использовать функцию html_children, которая выводит все, блоки которые находятся внутри текущих блоков. Но с этой функцией нужно быть аккуратнее.

## [1] 8.333333

Вы можете использовать какие-то логические условия. Например выберем первую и вторую домашку.

## {xml_nodeset (2)}
## [1] <div class="HW" id="1">\n                <p>9</p>\n              </div>
## [2] <div class="HW" id="2">\n                <p>10</p>\n              </ ...

На этом сайте находится прекрасный туториал по языку XPath.

Парсим погоду

Попробуем парсить погоду в Москве с помощью Яндекс Погоды. Первое что нужно сделать это загрузить html страницу в R.

Далее нам нужно понять какую информацию мы хотим получить. Давайте зайдем на сайт и посмотрим (используйте любой браузер кроме Safari).

Допусти мы хоти знать температуру и время. Но как можно понять в каком блоке находится эта информация? Можно нажать правой кнопкой мыши на нужную информацию и нажать на “Просмотреть код”. Справа или снизу у вас появится окно, в котором будет html-код страницы. Вас перенаправит сразу на тот блок, где находится информация, которую вы запросили.

Можно быстро получить XPath до этого контейнера. Нажмите на него правой кнопкой мыши -> Copy -> Copy XPath. Вытащим нужную нам информацию.

## [1] "+3"

Такой код не очень устойчив. Можно заметить, что у блока span есть атрибут class равный temp__value. Попробуем найти нужный блок следующим кодом.

## {xml_nodeset (6)}
## [1] <span class="temp__value">+3</span>
## [2] <span class="temp__value">+3</span>
## [3] <span class="temp__value">+1</span>
## [4] <span class="temp__value">+2</span>
## [5] <span class="temp__value">+1</span>
## [6] <span class="temp__value">+3</span>

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

## {xml_nodeset (1)}
## [1] <span class="temp__value">+3</span>

Запомним наше значение.

## [1] 3

Найдем аналогично блок для даты.

Видно, что в самом тексте нет полезной информации, кроме времени. Но зато есть полезная информация в атрибуте datetime. С помощью функции html_attr.

## [1] "2019-12-11 01:33+0300"

С помощью пакетов stringr и lubridate можно обработать дату.

## Error in str_sub(., start = 1, end = 16): не могу найти функцию "str_sub"
## [1] "2019-12-11 01:33+0300"

Как же автоматизировать процесс вакачивания данных? Допустим мы хотим парсить погоду каждые 2 минуты. Как мы можем сделать это?

Таблицы

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