XML2-Package: как обрабатывать пустые узлы?

#r #xml2

#r #xml2

Вопрос:

Я пытаюсь извлечь некоторые данные с html-сайта. У меня есть 500 узлов, которые должны содержать дату, заголовок и сводку. Используя

 url <- "https://www.bild.de/suche.bild.html?type=articleamp;query=Migrationamp;resultsPerPage=1000"
html_raw <- xml2::read_html(url)
main_node <- xml_find_all(html_raw, "//section[@class='query']/ol") %>%
  xml_children()

xml_find_all(main_node, ".//time") #time
xml_find_all(main_node, ".//span[@class='headline']") #title
xml_find_all(main_node, ".//p[@class='entry-content']") #summary
 

он возвращает три вектора с датами, заголовками и сводками, которые можно связать вместе. По крайней мере, в теории. К сожалению, мой код находит 500 дат, 500 заголовков, но только 499 резюме. Причина этого в том, что один из узлов просто отсутствует.

Это оставляет меня с проблемой, что я не могу связать это с фреймом данных из-за разницы в длине. Сводки не будут соответствовать точным датам и названиям.

Простым решением было бы перебирать узлы и заменять пустой узел заполнителем, таким как «NA».

 dates <- c()
titles <- c()
summaries <- c()

for(i in 1:length(main_node)){
  date_temp <- xml_find_all(main_node[i], ".//time") %>%
    xml_text(trim = TRUE) %>%
    as.Date(format = "%d.%m.%Y")
  title_temp <- xml_find_all(main_node[i], ".//span[@class='headline']") %>%
    xml_text(trim = TRUE)
  summary_temp <- xml_find_all(main_node[i], ".//p[@class='entry-content']") %>%
    xml_text(trim = TRUE)

  if(length(summary_temp) == 0) summary_temp <- "NA"

  dates <- c(dates, date_temp)
  titles <- c(titles, title_temp)
  summaries <- c(summaries, summary_temp)
}
 

Но это делает простой трехстрочный код ненужным длинным. Итак, я думаю, мой вопрос: существует ли более сложный подход, чем цикл?

Комментарии:

1. Извините! Добавлено еще несколько деталей.

2. К сожалению, это лучший способ. Одним из улучшений является использование функции xml_find_first , которая всегда будет возвращать значение или возвращать NA, если узел не найден.

Ответ №1:

Вы могли бы использовать purrr библиотеку, чтобы помочь и избежать явного цикла

 library(purrr)
dates <- main_node %>% map_chr(. %>% xml_find_first(".//time") %>% xml_text())
titles <- main_node %>% map_chr(. %>% xml_find_first(".//span[@class='headline']") %>% xml_text())
summaries <- main_node %>% map_chr(. %>% xml_find_first(".//p[@class='entry-content']") %>% xml_text())
 

При этом используется тот факт, что xml_find_first вернется NA , если элемент не найден, как указано @Dave2e.

Но также в целом увеличение списка путем добавления каждой итерации в цикле очень неэффективно в R. Лучше предварительно выделить вектор (поскольку он будет иметь известную длину), а затем присваивать значения каждой итерации соответствующему слоту ( out[i] <- val ) . На самом деле нет ничего плохого в самих циклах в R; на самом деле это просто перераспределение памяти, которое может замедлить работу.