использование rvest и purrr :: map_df для построения фрейма данных: работа с многоэлементными тегами - PullRequest
2 голосов
/ 03 мая 2019

(основываясь на моем собственном вопросе и его ответе @astrofunkswag здесь )

Я занимаюсь веб-страницей с rvest и превращаю собранные данные в фрейм данных с помощью purrr::map_df. Я столкнулся с проблемой, что map_df выбирает только первый элемент HTML-тегов с несколькими элементами. В идеале я хотел бы, чтобы все элементы тега были захвачены в результирующем фрейме данных, а теги с меньшим количеством элементов были переработаны.

Возьмите следующий код:

library(rvest)
library(tidyverse)

urls <- list("https://en.wikipedia.org/wiki/FC_Barcelona",
             "https://en.wikipedia.org/wiki/Rome")
h <- urls %>% map(read_html)

out <- h %>% map_df(~{
  a <- html_nodes(., "#firstHeading") %>% html_text()
  b <- html_nodes(., ".toctext") %>% html_text()

  a <- ifelse(length(a) == 0, NA, a)
  b <- ifelse(length(b) == 0, NA, b)

  df <- tibble(a, b)
})
out

, который выдает следующий вывод:

> out
# A tibble: 2 x 2
  a            b        
  <chr>        <chr>    
1 FC Barcelona History  
2 Rome         Etymology
> 

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

  a            b        
  <chr>        <chr>    
1 FC Barcelona History  
2 FC Barcelona  1899–1922: Beginnings  
3 FC Barcelona 1923–1957: Rivera, Republic and Civil War  
.
.
6 Rome         Etymology
7 Rome         History
8 Rome         Earliest history
.
.
> 

1 Ответ

2 голосов
/ 03 мая 2019

С ?ifelse

ifelse возвращает значение такой же формы, как у теста

Например, см.

ifelse(FALSE, 20, 1:5)
#[1] 1

Какlength(FALSE) равно 1, выбрано только первое значение 1:5, равное 1.

Аналогично, когда вы выполняете

ifelse(length(a) == 0, NA, a)

length(length(a) == 0) равно 1, и, следовательно, толькопервое значение a возвращается.

В этом случае мы можем использовать if вместо ifelse, поскольку у нас есть только один элемент для проверки, потому что

if(FALSE) 20 else 1:5 #returns
#[1] 1 2 3 4 5

Таким образом, он выдаст вам вывод, выполнив

library(tidyverse)
library(rvest)

h %>% map_df(~{
   a <- html_nodes(., "#firstHeading") %>% html_text()
   b <- html_nodes(., ".toctext") %>% html_text()
   a <- if (length(a) == 0) NA else a
   b <- if (length(b) == 0) NA else b
  tibble(a,b)
}) 


#    a            b                                        
#   <chr>        <chr>                                    
# 1 FC Barcelona History                                  
# 2 FC Barcelona 1899–1922: Beginnings                    
# 3 FC Barcelona 1923–1957: Rivera, Republic and Civil War
# 4 FC Barcelona 1957–1978: Club de Fútbol Barcelona      
# 5 FC Barcelona 1978–2000: Núñez and stabilization       
# 6 FC Barcelona The Dream Team era                       
# 7 FC Barcelona 2000–2008: Exit Núñez, enter Laporta     
# 8 FC Barcelona 2008–2012: Guardiola era                 
# 9 FC Barcelona 2014–present: Bartomeu era               
#10 FC Barcelona Support                                  
# … with 78 more rows
...