You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rvest循环抓取TripAdvisor酒店评论时返回空值的解决方法求助

我明白你遇到的问题了——用rvest循环爬取TripAdvisor评论时,随机出现的空值确实很头疼,明明分步跑大多没问题,批量循环就时不时掉链子。咱们来一步步解决这个问题:

问题根源分析

这种随机空值大概率是这几个原因导致的:

  • 反爬机制拦截:短时间内频繁请求,网站会临时限制你的访问,返回空页面或者部分内容
  • 重复请求浪费资源:你原来的代码里每次循环两次调用read_html(url),既拖慢速度又增加被拦截的概率
  • 节点选择器不够稳健:嵌套过深的选择器容易因为页面微小变化失效
  • 页面加载不完全:偶尔网络波动会导致页面没完全加载就被解析

优化后的解决方案代码

下面是修改后的脚本,针对上述问题做了全方位优化:

library(rvest)
library(dplyr)

# 初始化空数据框
df <- data.frame(review_text = character(), rating = numeric(), stringsAsFactors = FALSE)
x <- 0
max_retries <- 3  # 失败后最多重试3次

# 先爬50页(对应250条评论,足够满足你至少100条的需求)
for(i in 1:50){  
  url <- paste0("https://www.tripadvisor.com/Hotel_Review-g295424-d7760386-Reviews-or", x, "-Hyatt_Regency_Dubai_Creek_Heights-Dubai_Emirate_of_Dubai.html")
  x <- x + 5
  
  success <- FALSE
  retry_count <- 0
  
  # 失败重试机制
  while(!success && retry_count < max_retries){
    tryCatch({
      # 只读取一次页面,避免重复请求
      page <- read_html(url)
      
      # 优化评论文本选择器,更直接定位核心内容
      reviews <- page %>% 
        html_nodes(".hotels-hotel-review-community-content-review-list-parts-ExpandableReview__reviewText--2OVqJ") %>%
        html_text(trim = TRUE)
      
      # 优化评分提取逻辑,避免空值报错
      rating <- page %>% 
        html_nodes(".hotels-hotel-review-community-content-review-list-parts-RatingLine__bubbles--3d2Be span") %>%
        html_attr("class") %>%
        # 用匿名函数处理拆分,避免sapply的潜在问题
        lapply(function(class_str) strsplit(class_str, "_")[[1]][4]) %>%
        unlist() %>%
        as.numeric()
      
      # 确保抓取到有效数据再合并
      if(length(reviews) > 0 && length(rating) > 0){
        # 对齐评论和评分的长度,防止偶尔出现数量不一致的情况
        min_len <- min(length(reviews), length(rating))
        temp_df <- data.frame(
          review_text = reviews[1:min_len],
          rating = rating[1:min_len],
          stringsAsFactors = FALSE
        )
        df <- bind_rows(df, temp_df)
        success <- TRUE
        cat("成功抓取第", i, "页,当前总评论数:", nrow(df), "\n")
      } else {
        warning("第", i, "页未抓取到有效数据,重试中...")
        retry_count <- retry_count + 1
        Sys.sleep(runif(1, 2, 4))  # 重试前等待更久,降低被拦截概率
      }
    }, error = function(e){
      warning("第", i, "页请求失败:", e$message, ",重试中...")
      retry_count <- retry_count + 1
      Sys.sleep(runif(1, 2, 4))
    })
  }
  
  # 每次循环后随机等待1-3秒,模拟人类浏览节奏
  Sys.sleep(runif(1, 1, 3))
}

# 输出最终结果统计
cat("抓取完成,总评论数:", nrow(df), "\n")
head(df)

关键优化点说明

  1. 单次页面读取:把read_html(url)移到循环内部的最前面,只请求一次页面就完成评论和评分的提取,减少请求次数
  2. 失败重试机制:用tryCatch包裹请求逻辑,遇到错误或者空数据时自动重试,避免单次失败就丢失整页数据
  3. 随机延迟:每次循环后加入1-3秒的随机等待,重试时等待更久,有效规避反爬拦截
  4. 稳健的选择器:简化了评论和评分的选择器路径,降低因为页面微小变化导致的失效概率
  5. 数据对齐处理:确保评论和评分的长度一致,避免合并数据框时出现错位问题

额外注意事项

  • 如果还是偶尔出现空值,建议用SelectorGadget工具重新确认页面的CSS选择器——TripAdvisor的class名偶尔会更新
  • 不要盲目扩大循环次数,避免被网站封禁IP;如果需要爬大量数据,可以考虑使用代理IP(新手先从延迟和重试机制开始)
  • 可以先测试小范围循环(比如10页),确认稳定后再扩大到250页

内容的提问来源于stack exchange,提问作者IliasWhoElse

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:50:01