使用R语言rvest爬取数据时缺失信息处理难题求助
解决rvest爬取时缺失元素导致数据框绑定失败的问题
问题场景
使用rvest爬取https://expresso.pt/api/molecule/search?q=trotinete&page=**PAGENUMBER**&offset=0的文章数据时,当html_nodes或html_text定位的元素不存在,脚本在rbind数据框时崩溃。尝试过tryCatch、if语句但未彻底解决。
核心问题分析
原代码的主要问题是:
- 直接全局提取节点(如
html_nodes(".title a")),若某类节点部分缺失,会导致对应向量长度与其他列不匹配,rbind时因维度不一致报错。 - 用
ifelse(length(.) == 0, NA, .)仅处理了整列全空的情况,无法处理单条数据缺失元素的场景。
优雅处理缺失元素的策略
- 按条目容器批量处理:先定位每个文章的父容器节点,再对每个容器单独提取字段,确保每条数据的各字段一一对应,不会出现长度不匹配。
- 使用
html_element替代html_nodes:针对单元素提取(每个条目仅一个的字段,如标题、日期),html_element会返回与输入节点数量一致的结果,缺失元素自动填充NA,避免空向量。 - 避免循环
rbind:用列表收集每条数据,最后一次性合并为数据框,既高效又避免维度错误。 - 强化单字段错误处理:对可能出错的字段(如文章正文),在提取时加入兜底逻辑。
修改后的完整代码
library(rvest) library(dplyr) library(purrr) # 定义文章正文提取函数,优化错误处理 get_article <- function(link_article) { tryCatch({ article_page <- read_html(link_article) # 提取正文并拼接,若节点不存在则返回NA article_text <- article_page %>% html_element("#article-body-1") %>% html_text2() # html_text2更适合处理带结构的文本 Sys.sleep(2) return(article_text) }, error = function(e) { message("抓取文章失败: ", link_article) return(NA_character_) }) } search_term <- "trotinete" # 用列表存储所有爬取结果,避免循环rbind scrape_results <- list() for(page_result in seq(from = 1, to = 2)) { link <- paste0("https://expresso.pt/api/molecule/search?q=", search_term, "&page=", page_result, "&offset=0") message("正在抓取页面: ", link) Sys.sleep(2) page <- read_html(link) # 定位每个文章的父容器节点,这是关键:按条目处理 article_containers <- page %>% html_nodes(".search-result-item") # 若当前页面无文章,跳过 if(length(article_containers) == 0) next # 对每个容器提取字段,确保字段一一对应 page_data <- map_dfr(article_containers, function(container) { tibble( title = container %>% html_element(".title a") %>% html_text2(), link_article = container %>% html_element(".title a") %>% html_attr("href") %>% {ifelse(grepl("^https://", .), ., paste0("https://expresso.pt", .))}, date = container %>% html_element(".timeStamp") %>% html_attr("datetime"), section = container %>% html_element(".mainSection") %>% html_text2(), summary = container %>% html_element(".lead") %>% html_text2(), author = container %>% html_element(".author") %>% html_text2() ) }) # 批量提取文章正文 page_data$complete_article <- map_chr(page_data$link_article, get_article) # 将当前页面数据加入结果列表 scrape_results[[page_result]] <- page_data } # 合并所有页面数据为数据框 expresso_scraper <- bind_rows(scrape_results) # 导出为CSV write.csv2(expresso_scraper, "expresso.csv", row.names = FALSE)
关键修改说明
- 按条目容器处理:通过
html_nodes(".search-result-item")获取每个文章的父容器,确保每条数据的字段提取基于同一容器,不会出现长度不匹配。 html_element的使用:替代html_nodes提取单元素,自动为缺失元素填充NA,无需额外判断长度。map_dfr:批量处理容器节点,直接生成数据框,简化代码且避免维度错误。html_text2:比html_text更适合处理网页文本,自动处理换行和多余空格。- 列表收集结果:避免循环
rbind的性能问题和维度错误,最后用bind_rows合并更高效。
内容的提问来源于stack exchange,提问作者Bruno
相关产品推荐
相关产品推荐

