#r #web-scraping #tidyverse #rvest
#r #очистка веб-страниц #tidyverse #rvest
Вопрос:
Я пишу средство обработки данных, при использовании которого оно выглядит следующим образом rvest
:
library(tidyverse)
library(rvest)
library(magrittr)
library(dplyr)
library(tidyr)
library(data.table)
library(zoo)
targets_url <- paste0("https://247sports.com/college/ohio-state/Season/2021-Football/Targets/")
targets <- map_df(targets_url, ~.x %>% read_html %>%
html_nodes(".ri-page__star-and-score .score , .position , .meta , .ri-page__name-link") %>%
html_text() %>%
str_trim %>%
str_split(" ") %>%
matrix(ncol = 4, byrow = T) %>%
as.data.frame)
df_structure <- apply(targets,2,as.character)
df_targets <- as.data.frame(df_structure)
Вы заметите, что он создает фрейм данных с четырьмя переменными и 53 строками.
Но теперь перейдите к самому URL. Вы заметите, что 53 строки соответствуют определенным подкатегориям: Главная цель, высокий выбор и заинтересованность. Вот изображение, показывающее пример:
То, что я пытаюсь сделать, это создать пятый столбец, который содержит подкатегорию. Так, например, трем лицам, которые подпадают под «Top Target», будет назначен другой столбец, в котором они перечислены как «Top Target». Тогда в следующих 20 строках этот пятый столбец будет читаться как «Высокий выбор» и так далее. Причина, по которой я здесь, в том, что я понятия не имею, как это сделать. Что делает это еще сложнее, так это то, что не на каждой странице будут одинаковые номера, вот пример этого. Вы увидите, что, хотя на картинке выше указана только верхняя цель (3), на этой странице теперь есть верхняя цель (24). Это зависит от каждой страницы.
Можно ли изменить мой оригинальный сценарий, который бы:
A) Создает этот пятый столбец с подкатегорией, о которой я упоминал выше
Б) Знает, когда предполагается переключиться на следующую подкатегорию
C) Не зависит от общего количества людей в каждой подкатегории
ОТРЕДАКТИРОВАННЫЙ сценарий частично основан на ответе @Dave2e:
library(rvest)
library(dplyr)
library(stringr)
teams <- c("ohio-state","penn-state","michigan","michigan-state")
targets_url <- paste0("https://247sports.com/college/", teams, "/Season/2021-Football/Targets/")
# read the web page once! then extract the information requested
targets <- map_df(targets_url, ~.x %>% read_html %>%
html_nodes(".ri-page__star-and-score .score , .position , .meta , .ri-page__name-link") %>%
html_text() %>%
str_trim %>%
str_split(" ") %>%
matrix(ncol = 4, byrow = T) %>%
as.data.frame)
#find the headings and the players
list <- page %>% html_nodes("li.ri-page__list-item")
headers <- which(html_attr(list, "class") == "ri-page__list-item list-header")
#find the category
category <- list[headers] %>% html_node("b.name") %>% html_text()
#extract repeats from header
nrepeats<-as.integer(str_extract(category, "[0-9] "))
categories <- rep(category, nrepeats)[1:nrow(targets)]
#create combined dataframe
answer <- cbind(categories, targets)
Ответ №1:
Заголовки расположены в узле «li» с class =»ri-страница __список-список элементов-заголовок». Также удобно отметить, что заголовок содержит количество игроков под этим заголовком.
Этот скрипт находит узлы заголовка, извлекает количество игроков, а затем создает вектор повторяющихся заголовков для слияния с целевым фреймом данных.
library(rvest)
library(dplyr)
library(stringr)
targets_url <- paste0("https://247sports.com/college/ohio-state/Season/2021-Football/Targets/")
# read the web page once! then extract the information requested
page <- read_html(targets_url)
targets <- page %>%
html_nodes(".ri-page__star-and-score .score , .position , .meta , .ri-page__name-link") %>%
html_text() %>%
str_trim %>%
str_split(" ") %>%
matrix(ncol = 4, byrow = T) %>%
as.data.frame
#find the headings and the players
list <- page %>% html_nodes("li.ri-page__list-item")
headers <- which(html_attr(list, "class") == "ri-page__list-item list-header")
#find the category
category <- list[headers] %>% html_node("b.name") %>% html_text()
#extract repeats from header
nrepeats<-as.integer(str_extract(category, "[0-9] "))
categories <- rep(category, nrepeats)[1:nrow(targets)]
#create combined dataframe
answer <- cbind(categories, targets)
Обновление — поиск скрытых данных
Веб-страница динамически скрывает некоторую информацию, если список слишком длинный. Теперь cope может обрабатывать эту информацию. Приведенный ниже код находит скрытые данные JSON (содержащиеся в узле ‘script’) и анализирует эти данные. Он возвращает список игроков, но не всю одинаковую информацию.
#another option
#find the hidden JSON data
jsons <- page %>% html_nodes(xpath = '//*[@type ="application/ld json"]')
allplayers <- jsonlite::fromJSON( html_text(jsons[2]))
#Similar list, provide URL to each players webpage
answer2 <- cbind(rep(category, nrepeats), allplayers$athlete)
Комментарии:
1. Это выдающаяся работа. Большое вам спасибо. Еще один вопрос: если я переключу URL с помощью 247sports.com/college/penn-state/Season/2021-Football/Targets он выдает сообщение об ошибке. Есть понимание того, что там происходит?
2. Будучи выпускником PSU, я должен был начать здесь. Ошибка связана с тем, что имена в конце списка не отображаются при начальной загрузке страницы. Данные скрыты на странице. Решение состоит в том, чтобы использовать ether для сокращения
rep(category, nrepeats)
и / или доступа к скрытым данным. Смотрите обновления выше. Другой вариант — RSelenium.3. Большое спасибо за помощь. У меня есть еще один вопрос. Проверьте мой отредактированный ответ. Вы заметили, что я сопоставляю несколько команд сценарию. Хотя он выполняется без ошибок, вы заметите, что в нем перечислены только «категории» для первой команды в списке. Почему он это делает? Кроме того, любое предложение о том, как я могу добавить еще один столбец, в котором указано, с какой командой он связан (поэтому, если он находится по ссылке на страницу штата Огайо, там будет написано «Штат Огайо», если на странице штата Пенсильвания будет написано «Штат Пенсильвания» и т. Д.) Это нормально, если имена повторяются, могут быть имена, прикрепленные к нескольким страницам.
4. Что бы я сделал: это поместить все мое решение в функцию, принимающую имя команды в качестве аргумента. Попросите функцию создать URL-адрес и сгенерировать data.frame результатов, а перед возвратом фрейма данных из функции добавьте название команды в качестве дополнительного столбца.
lapply
Для вызова функции используйте или «purrr». Результатом должен быть список фреймов данных, которые вы можете использоватьbind_rows()
для создания окончательного большого фрейма данных.5. Также обратите внимание, что я только один раз читаю веб-страницу в переменную «страница» и анализирую ее. Ваш код использует «targets_url» и неопределенную переменную «страница»