Получение данных из внешних источников

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

Первое, что вы делаете перед поиском данных на сайте, это смотрите файл robots.txt: baidu_robotstxt <- "http://www.baidu.com/robots.txt". Внутри такого файла перечислены разделы сайта, и у некоторых будет стоять атрибут Allow, который разрешает парсинг поисковым роботам. Если вы видите ситуацию вроде Disallow: /data/ и Allow: /data/texts/, то каталог data запрещен для парсинга, но texts посмотреть можно. Также, в заголовке кода самой страницы можно найти строчку content="noindex, nofollow", это также явный запрет на копирование информации. Существует скрипт, который смотрит на robots.txt и выводит список запрещенных к парсингу директорий сайта непосредственно в косноль RStudio.

В рамках этой статьи мы будем работать с источниками открытых данных, например, репозиториями данных для машинного обучения. Самая простая задача это скачать готовый файл .csv из интернета (HTTP) или с FTP вашей компании. Обычно источник данных предоставляется в формате .csv (comma separated values), но данные все равно могут быть разделены точкой с запятой, поэтому лучше использовать не read.csv, а более общую функцию read.table. Для первого примера возьмем dataset с archive.ics.uci.edu.

dafileMaster <- "http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-white.csv"
df <- read.table(file = dafileMaster , sep=";", header=TRUE)
View(head(df))

Сработало, таблица отобразилась в RStudio. Отмечу, что header позволяет указать, являются ли значения в первой колонке заголовками. Формат .csv для работы популярен, но в корпоративной среде легко наткнуться и на файл excel. И уж тем более может понадобиться записывать данные непосредственно в файл .xlsx, обновляя расчет всех формул, и считывать результаты обновленных расчетов в другие таблицы. С помощью XLConnect вы сможете работать с файлами Excel как в древнем и медленном формате Excel ’97 (*.xls и привет интерфейсам бухгалтеров), так и с современным OOXML (Excel 2007+, *.xlsx), и записывать значения в файл. Пример считывания данных:

install.packages('XLConnect')
library(XLConnect)
wb = loadWorkbook("C:/Users/Downloads/testfile.xlsx",create=T)
df = readWorksheet(wb, sheet = "data")
newExport = readWorksheet (wb,"Data",1, 4, 1000, header = TRUE)
summary(newExport)

А теперь попробуем записать данные в файл простой командой writeWorksheetToFile("C:/Users/Downloads/testfile.xlsx",data=iris,sheet="iris2"), и в общем то это все, тестовые данные iris2 появились в файле.

Если возникают проблемы с зависимостями Java, то вместо XLconnect можно использовать openxlsx.

install.packages("openxlsx", dependencies=TRUE
readWorkbook('C:/Users/Downloads/testfile.xlsx')

Но порой нужно засучить рукава и работать с более «шумными» данными. Например, выкачать целую страницу сайта. Для выкачивания сайта из интернета достаточно объявить в переменную адрес сайта, указать папку для загрузки, и задать команду на скачивание. Таким же способом можно скачивать pdf, архивы. После выполнения работы скачанный сайт/файл можно удалить командой file.remove(paste(myfolder ,"filename.html ",sep="")). Ниже я приведу базовый пример, но есть библиотека downloader, которая позволяет более гибко работать с протоколом https. Во втором примере файл будет закачан в папку по умолчанию, у меня это C:\Users\user\Documents.

url <- "https://your-scorpion.ru/" 
myfolder  <- "E:/donwload/"
download.file(url,paste(myfolder,"filename.html",sep=""))
download_url <- "http://insight.dev.schoolwires.com/HelpAssets/C2Assets/C2Files/C2ImportSchoolSample.csv"
download.file(download_url, "./C2ImportSchoolSample.csv")

Это интересно, но что, если нам нужно получить лишь маленький кусочек информации с сайта? Вернемся к изначальной задаче выдирания ключевой информации со страницы. Для решения понадобятся библиотеки XML и rvest,

install.packages("rvest")
install.packages("XML")
install.packages("openxlsx") 
library(rvest)
library(openxlsx)
url <- read_html("https://your-scorpion.ru")
 
names <- url %>%
    html_nodes("h1") %>%
    html_text()
 
names1 <- url %>%
    html_nodes("article") %>%
    html_text()
 
newOne <- html_children(img)
lineOne <- html_attr(newOne, "href")
linetwo <- html_attr(newOne, "div")
 
myTableForBoss <- data.frame(title = names1, datam = names)
write.xlsx(myTableForBoss, file = 'C:/Users/checkInOnMonday.xlsx')

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

В зависимости от сложности структуры сайта код может усложниться и не удастся избежать NA (Not Available, отсутствие значения). Получим данные о рейтинге фильма со стороннего сайта:

library(rvest) 
movie2 <- read_html("https://www.kinopoisk.ru/film/840372/") 
rating <- movie2 %>%    html_nodes(xpath = '//*[@id="block_rating"]/div[1]/div[1]/a/span[1]') %>%   html_text() %>%   as.numeric()
print (rating)
revenue <- c("rating_ball")
companiesData <- data.frame(revenue, rating)
write.csv(companiesData, "E:/Downloads/data.csv", row.names=FALSE, na="")
 
//либо с NA в данных
rating <- movie2 %>%    html_nodes("a span") %>%   html_text() %>%   as.numeric()
print (rating)
library(data.table)
result <- data.table(rating)[, lapply(.SD, function(x) x[order(is.na(x))])]
result <- result[!result[, Reduce(`&`, lapply(.SD, is.na))]]
revenue <- c("rating_ball","ratingCount","Expectations")
companiesData <- data.frame(revenue, result)

В результате мы получили рейтинг фильма и даже удалили все NA. В реальном мире все не так радужно, сайты защищаются капчей от парсинга на уровне OAuth, если не указать сертификат. Для решения проблемы вам могут посоветовать антикапчи, но не предупредят, что вас забанят еще до того, как вы получите результат. Особенно, если вы будете отправлять паралельные запросы для выкачивания тяжелый данных, вроде картинок или видео. И это правильно. Для работы с OAuth существуют пакеты, вроде httr и RCurl.

OAuth включает в себя учетные данные клиента, временные данные для авториации и токен. Все проблемы с OAuth лечатся указанием корректного сертификата. После регистрации мы обычно получаем учетные данные. Временные учетные данные доказывают, что запрос приложения на токены доступа выполняется авторизованным клиентом (например, нашей собственной учетной записи Twitter). После получения временных учетных данных их можно обменять на учетные данные токена.

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

library(rvest)
box_office <- read_html("https://your-scorpion.ru/")
box_office %>% html_node("form") %>% html_form()
 
session <- html_session("https://your-scorpion.ru/printing-for-dummies/")
form <- html_form(session)[[1]]
form <- set_values(form, author = "Иван Иванов", comment = "Мой комментарий заключается в том, что эта идея достаточно здравая.")
submit_form(session,form)

Теперь давайте спарcим API, на выходе это обычный JSON. Если вы можете получить нужные данные по API , то это предпочтительный способ по сравнению с парсингом страниц. Иначе вам предстоит долгая и упорная работа с регулярными выражениями. Способы можно миксовать. Самое простое и очевидное по JSON:

install.packages("rjson")
library("rjson")
json_file <- "C:/Users/25.4.2019-Untitled.json"
jsonclass <- fromJSON(file = json_file)

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

install.packages("tidyverse")
library(tidyverse)
datalist <- c("Facebook.com", "Twitter.de", "Wikipedia.org", "Google.at", "Vc.ru",
"Dribbble.cn", "Instagram.fi", " Vk.cc", "Yandex.ru", "Your-scorpion.ru",
"Linkedin.com", "Wordpress.org", "Pinterest.kz")
 
pattern <- "(G|F)"
pattern2 <- "[a-zA-Z]*$"
str_subset(datalist, pattern)
sum(str_detect(datalist, pattern2))

Существует множество интересных возможностей по работе со строками через регулярные выражения, например, str_extract(datalist, pattern2) покажем все символы с определенными вхождениями, а если использовать регулярное выражение "[a-zA-Z]*$", то удастся вычленить из строки все домены. При этом есть важный нюанс, нужно использовать / .csv, так как просто .csv обозначает любое значение. Например, str_match(c('abcsv', 'a.csv'), '.csv') вернет вхождение bcsv, а оно нам не надо. str_match(c('abcsv', 'a.csv'), '/.csv') сработает так, как ожидается.

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

2 комментария

  1. Виталий Паникой

    Здравствуйте, а есть ли способы узнать, что в моих мобильных приложениях работают одни и те же пользователи? Допустим, Вася Пупкин работает в приложении 1 и он же в приложении 2?

    • Цветков Максим (Author)

      По идентификатору устройства, в любой системе аналитики идентификатор устройства будет одинаковым, поэтому вам нужен простой join по идентификатору.

«Взаимодействуя с данным сайтом, вы, как пользователь, автоматически даете согласие согласие на обработку персональных данных» Согласие

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.