R语言rvest批量爬虫:如何用循环自动化实现多网页数据提取
报错原因
rvest::read_html() 单次调用仅支持传入长度为1的单条URL字符串,直接传入长度为100的URL向量必然触发Error: 'x' must be a string of length 1报错。批量抓取的核心逻辑是对URL向量做逐页迭代,单页执行读取、节点提取操作后,再统一合并所有结果。
推荐实现方案:purrr迭代写法
这种写法不需要手动管理循环索引和存储结构,自带容错、限速能力,代码更简洁:
首先加载依赖、提前定义你已经写好的自定义取数函数:
library(rvest) library(purrr) library(dplyr) # 自定义间隔取数函数 nth_element <- function(vector, starting_position, n) { vector[seq(starting_position, length(vector), n)] }
封装单页抓取逻辑,加入容错、限速机制,避免单页报错中断全流程,也避免高频请求被站点拦截:
# 单页抓取逻辑 scrape_single_page <- function(url) { # 随机停顿1-2秒,降低请求频率 Sys.sleep(sample(1:2, 1)) page <- read_html(url) page_text <- page %>% html_nodes("td") %>% html_text() target_val <- nth_element(page_text, 10, 5) # 返回带源URL的结果,方便后续溯源 return(data.frame( source_url = url, target_content = target_val )) } # 给抓取函数加容错:单页抓取失败时记录错误,不中断整体流程 safe_scrape <- safely(scrape_single_page) # 加全局限速 slow_safe_scrape <- slowly(safe_scrape, rate = rate_delay(pause = 1.5)) # 传入URL向量执行批量抓取 all_raw_res <- map(webpage_urls, slow_safe_scrape)
最后整合结果:
# 提取所有抓取成功的页面结果,合并为统一数据框 final_table <- map_dfr(all_raw_res, ~.x$result) # 可选:提取抓取失败的页面及对应错误信息 failed_info <- keep(all_raw_res, ~!is.null(.x$error)) %>% map_chr(~.x$error$message) failed_pages <- data.frame( source_url = webpage_urls[which(sapply(all_raw_res, ~!is.null(.x$error)))], error_msg = failed_info )
备选方案:基础R for循环实现
不需要额外安装purrr包,逻辑更直白,适合R基础用户:
# 提前创建和URL数量等长的空列表存储结果,比逐行合并效率高 res_list <- vector("list", length = length(webpage_urls)) for (i in seq_along(webpage_urls)) { current_url <- webpage_urls[i] Sys.sleep(sample(1:2, 1)) # 容错处理 tryCatch({ page <- read_html(current_url) page_text <- page %>% html_nodes("td") %>% html_text() target_val <- nth_element(page_text, 10, 5) res_list[[i]] <- data.frame( source_url = current_url, target_content = target_val ) # 打印进度 cat("已完成第", i, "页,剩余", length(webpage_urls)-i, "页\n") }, error = function(e) { cat("第", i, "页抓取失败,URL:", current_url, "错误:", e$message, "\n") res_list[[i]] <- NULL }) } # 合并所有结果为表格 final_table <- do.call(rbind, res_list)
注意事项
- 上述代码默认单页提取的目标值长度固定,如果单页提取结果长度不一致,可以将结果存为列表列,后续再做拆分展开
- 不要删除停顿相关代码,短时间内发起大量高频请求很容易被站点封禁IP,导致所有请求失效
- 如果目标页面是动态渲染(内容由JavaScript加载,静态HTML中不存在目标节点),将
read_html替换为rvest::read_html_live()即可,整体迭代逻辑不需要改动
内容的提问来源于stack exchange,提问作者flâneur
相关产品推荐
相关产品推荐

