使用rvest爬虫时如何避免open.connection(x, "rb"): HTTP 404错误?
Mercado Libre爬取时随机出现HTTP 404错误的原因与解决方法
问题背景
我将202个Mercado Libre商品页面的URL存储在向量中,尝试通过for循环结合rvest包爬取页面信息。这些URL来自商品列表页,通过以下代码获取:
get_products <- function(n_page) { cat("Scraping index", n_page, "\n") page <- str_c( "https://lista.mercadolivre.com.br/_Desde_", n_page, "_CustId_38356530_NoIndex_True" ) %>% read_html() tibble(url = page %>% html_elements('a.ui-search-link') %>% html_attr('href') %>% str_subset('tracking_id') %>% unique() )} products_url <- map_dfr(seq(1, 49 * 4, by = 48), get_products)
遇到的问题
爬取过程中反复出现如下错误:
error in open.connection(x, "rb") : HTTP error 404
手动访问报错的URL均能正常打开,且错误出现的位置随机——比如第一次运行时向量第6个URL报错,第二次运行该URL却能正常爬取。目前用try()可以避免循环终止,但希望从根源解决问题,无需多次运行代码完成爬取。
当前爬取代码如下:
for (i in 1:length(standard_ad)) { try( collectedtitles <- collect(standard_ad[i],'.ui-pdp-title')) assign('standard_titles', append(standard_titles, collectedtitles)) }
自定义的collect函数:
collect <- function(webpage,section) { page <- read_html(webpage) value <- html_node(page, section) value <- html_text(value) }
错误原因分析
- 反爬机制伪装:Mercado Libre的反爬系统会对高频请求的客户端返回404状态码,这并非真的页面不存在,而是网站临时拒绝爬虫访问的伪装手段。
- 请求频率过高:未设置请求间隔,短时间内连续发送大量请求,触发了网站的限流策略。
- 请求头特征明显:
read_html默认的请求头过于简单,容易被识别为爬虫,网站因此返回假404。
预防与解决方法
核心优化方向
- 添加模拟浏览器的请求头:用真实浏览器的User-Agent、Referer等信息伪装请求,降低被识别为爬虫的概率。
- 设置随机请求间隔:在每次请求之间加入随机等待时间,避免固定频率的请求模式被检测。
- 实现请求重试机制:针对临时返回的404错误,自动重试几次,而非直接放弃。
- 改用更可控的请求方式:用
httr包替代直接调用read_html,灵活处理请求响应。
修改后的代码示例
library(rvest) library(httr) library(purrr) library(stringr) # 改进后的collect函数:带请求头、重试机制 collect <- function(webpage, section, max_retry = 3) { # 模拟真实浏览器的请求头 browser_headers <- add_headers( `User-Agent` = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36", `Accept-Language` = "pt-BR,pt;q=0.9,en;q=0.8", `Referer` = "https://lista.mercadolivre.com.br/" ) # 重试逻辑 for (attempt in 1:max_retry) { # 发送请求 response <- try(GET(webpage, browser_headers), silent = TRUE) # 处理请求错误 if (inherits(response, "try-error")) { Sys.sleep(runif(1, 1.5, 3)) next } # 检查响应状态码 if (status_code(response) == 200) { page <- content(response, "parsed") title_node <- html_node(page, section) return(ifelse(is.null(title_node), NA_character_, html_text(title_node))) } else if (status_code(response) == 404) { # 遇到404随机等待后重试 Sys.sleep(runif(1, 2, 4)) } else { # 其他异常状态码直接返回NA return(NA_character_) } } # 多次重试失败返回NA return(NA_character_) } # 改进后的爬取循环 standard_titles <- c() for (url in standard_ad) { # 每次请求前随机等待1-3秒 Sys.sleep(runif(1, 1, 3)) collected_title <- collect(url, '.ui-pdp-title') standard_titles <- c(standard_titles, collected_title) }
额外注意事项
- 随机等待时间建议设置在1-5秒区间,不要过短,避免触发反爬;
- User-Agent可以定期替换为最新的浏览器标识,避免被网站列入爬虫黑名单;
- 若爬取量较大,可分批次处理URL,每爬取20-30个URL后暂停一段时间;
- 不要在短时间内重复运行爬取代码,给网站服务器留缓冲时间。
内容的提问来源于stack exchange,提问作者Marina Bonatti
相关产品推荐
相关产品推荐

