rvest循环爬取EDI数据集日期遇xml_find_all错误的解决方案
批量爬取EDI数据集起止日期的代码修正
问题背景
需要爬取EDI单个地点下147个数据集的起止日期,已能实现单个数据集的信息提取,但批量循环时抛出错误:
Error in UseMethod("xml_find_all") : no applicable method for 'xml_find_all' applied to an object of class "list"
错误原因
原循环存在两个核心问题:
- URL拼接时错误使用整个
package_ids$PackageId向量,生成了所有URL的集合,再用map读取得到列表对象,但html_nodes仅支持单个HTML文档对象,无法直接处理列表。 - 日期提取的选择器未采用已验证的可靠写法。
修正后的代码
方式1:修正for循环
library(rvest) library(dplyr) # 读取搜索页面并提取PackageId url <- "https://portal.edirepository.org/nis/simpleSearch?defType=edismax&q=*:*&fq=-scope:ecotrends&fq=-scope:lter-landsat*&fq=scope:(knb-lter-and)&fl=id,packageid,title,author,organization,pubdate,coordinates&debug=false&start=0&rows=150" webpage <- read_html(url) package_ids <- webpage %>% html_table() %>% .[[4]] %>% select(`Package Id ▵▿`) %>% rename(PackageId = `Package Id ▵▿`) %>% pull(PackageId) # 转为字符向量,方便循环 # 初始化结果列表 result_list <- list() # 循环处理每个PackageId for (pkg_id in package_ids) { # 拼接单个元数据页面URL metadata_url <- paste0("https://portal.edirepository.org/nis/metadataviewer?packageid=", pkg_id) # 读取元数据页面 metadata_page <- read_html(metadata_url) # 提取日期(使用已验证的选择器) begin_date <- metadata_page %>% html_nodes("td:contains('Begin:') + td") %>% html_text2() %>% # html_text2自动处理空白 trimws() end_date <- metadata_page %>% html_nodes("td:contains('End:') + td") %>% html_text2() %>% trimws() # 处理可能的缺失值(部分数据集可能无起止日期) begin_date <- ifelse(length(begin_date) == 0, NA_character_, begin_date) end_date <- ifelse(length(end_date) == 0, NA_character_, end_date) # 存入结果列表 result_list[[pkg_id]] <- tibble( PackageId = pkg_id, Begin = begin_date, End = end_date ) } # 合并为最终数据框 final_df <- bind_rows(result_list) print(final_df)
方式2:使用purrr的map_df(更简洁的tidyverse风格)
library(rvest) library(dplyr) library(purrr) # 读取搜索页面并提取PackageId url <- "https://portal.edirepository.org/nis/simpleSearch?defType=edismax&q=*:*&fq=-scope:ecotrends&fq=-scope:lter-landsat*&fq=scope:(knb-lter-and)&fl=id,packageid,title,author,organization,pubdate,coordinates&debug=false&start=0&rows=150" webpage <- read_html(url) package_ids <- webpage %>% html_table() %>% .[[4]] %>% select(`Package Id ▵▿`) %>% rename(PackageId = `Package Id ▵▿`) %>% pull(PackageId) # 定义单个PackageId的处理函数 fetch_metadata <- function(pkg_id) { metadata_url <- paste0("https://portal.edirepository.org/nis/metadataviewer?packageid=", pkg_id) metadata_page <- read_html(metadata_url) begin_date <- metadata_page %>% html_nodes("td:contains('Begin:') + td") %>% html_text2() %>% trimws() %>% {if (length(.) == 0) NA_character_ else .} end_date <- metadata_page %>% html_nodes("td:contains('End:') + td") %>% html_text2() %>% trimws() %>% {if (length(.) == 0) NA_character_ else .} tibble( PackageId = pkg_id, Begin = begin_date, End = end_date ) } # 批量处理并合并结果 final_df <- map_df(package_ids, fetch_metadata) print(final_df)
关键改进点
- 循环时每次仅处理单个PackageId,生成单个URL并读取单个HTML页面,避免列表对象导致的错误。
- 使用已验证的CSS选择器提取日期,保证提取准确性。
- 增加缺失值处理逻辑,避免因部分数据集无日期导致的代码中断。
- 采用
tibble和bind_rows替代原生数据框操作,更符合tidyverse规范。
内容的提问来源于stack exchange,提问作者tassones
相关产品推荐
相关产品推荐

