XML2-Package:如何处理空节点?
XML2-Package: How to treat empty Nodes?
我正在尝试从 html 站点提取一些数据。我有 500 个节点,其中应该包含日期、标题和摘要。通过使用
url <- "https://www.bild.de/suche.bild.html?type=article&query=Migration&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
它 returns 三个带有日期、标题和摘要的向量,可以将它们编织在一起。至少在理论上。不幸的是,我的代码找到了 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)
}
但这使得简单的三行代码变得不必要的长。所以我想我的问题是:是否有比循环更复杂的方法?
您可以使用 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())
如果@Dave2e 指出没有找到元素,这使用了 xml_find_first
将 return NA
的事实。
但一般来说,通过在循环中追加每次迭代来增加列表在 R 中的效率非常低。最好预先分配向量(因为它的长度已知),然后将每次迭代的值分配给正确的插槽 (out[i] <- val
)。 R 中的循环本身并没有什么问题;实际上只是内存重新分配会减慢速度。
我正在尝试从 html 站点提取一些数据。我有 500 个节点,其中应该包含日期、标题和摘要。通过使用
url <- "https://www.bild.de/suche.bild.html?type=article&query=Migration&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
它 returns 三个带有日期、标题和摘要的向量,可以将它们编织在一起。至少在理论上。不幸的是,我的代码找到了 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)
}
但这使得简单的三行代码变得不必要的长。所以我想我的问题是:是否有比循环更复杂的方法?
您可以使用 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())
如果@Dave2e 指出没有找到元素,这使用了 xml_find_first
将 return NA
的事实。
但一般来说,通过在循环中追加每次迭代来增加列表在 R 中的效率非常低。最好预先分配向量(因为它的长度已知),然后将每次迭代的值分配给正确的插槽 (out[i] <- val
)。 R 中的循环本身并没有什么问题;实际上只是内存重新分配会减慢速度。