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

使用XML包readHTMLTable()抓取合并单元格表格异常的解决请求

解决HTML合并单元格表格抓取异常的问题

嘿,我太懂这种抓合并单元格表格踩坑的感觉了!readHTMLTable确实对rowspan/colspan这类合并单元格的支持很差,直接抓取肯定会出现结构混乱、内容缺失的问题。下面给你两个靠谱的解决思路:

方法一:用rvest包灵活处理合并单元格

rvest是更现代的网页抓取工具,配合手动解析单元格的合并属性,可以完美补全合并的内容:

首先安装并加载所需包:

install.packages(c("rvest", "dplyr"))
library(rvest)
library(dplyr)

然后编写抓取和处理逻辑:

# 目标网址
url <- "http://www.gaokao.com/e/20201106/5fa4f625338d0.shtml"

# 模拟浏览器请求头,避免被反爬
session <- session(url, user_agent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.48 Safari/537.36 QQBrowser/7.7.31732.400")
page <- read_html(session)

# 定位页面中的第一个表格
table_node <- page %>% html_nodes("table") %>% first()

# 提取表格的所有行
rows <- table_node %>% html_nodes("tr")

# 初始化变量:存储处理后的行数据、记录需要向下填充的rowspan内容
processed_rows <- list()
rowspan_queue <- list()

# 遍历每一行处理单元格
for (row in rows) {
  cells <- row %>% html_nodes("td, th")
  current_row <- list()
  col_index <- 1
  
  # 先处理上一行遗留的rowspan填充任务
  while (col_index <= length(rowspan_queue)) {
    if (rowspan_queue[[col_index]]$count > 0) {
      current_row[[col_index]] <- rowspan_queue[[col_index]]$value
      rowspan_queue[[col_index]]$count <- rowspan_queue[[col_index]]$count - 1
      col_index <- col_index + 1
    } else {
      # 移除已经完成填充的rowspan项
      rowspan_queue[[col_index]] <- NULL
    }
  }
  
  # 处理当前行的每个单元格
  for (cell in cells) {
    # 获取单元格文本并去除多余空格
    cell_text <- cell %>% html_text(trim = TRUE)
    
    # 获取rowspan和colspan属性,默认值为1(即没有合并)
    rowspan <- cell %>% html_attr("rowspan") %>% as.integer() %>% replace(is.na(.), 1)
    colspan <- cell %>% html_attr("colspan") %>% as.integer() %>% replace(is.na(.), 1)
    
    # 处理colspan:将单元格内容填充到对应的多列中
    for (i in 1:colspan) {
      current_row[[col_index]] <- cell_text
      col_index <- col_index + 1
    }
    
    # 处理rowspan:如果需要向下填充多行,记录到队列中
    if (rowspan > 1) {
      for (i in 0:(colspan-1)) {
        rowspan_queue[[col_index - i - 1]] <- list(value = cell_text, count = rowspan - 1)
      }
    }
  }
  
  # 将处理后的行加入列表
  processed_rows[[length(processed_rows)+1]] <- current_row
}

# 将处理后的行列表转换为标准数据框
max_cols <- max(sapply(processed_rows, length))
table_df <- bind_rows(lapply(processed_rows, function(row) {
  # 统一列数,空缺的位置补NA
  row <- c(row, rep(NA, max_cols - length(row)))
  as.data.frame(t(row), stringsAsFactors = FALSE)
}))

# 设置表格表头(假设第一行是表头)
header_row <- table_df[1,]
table_df <- table_df[-1,]
colnames(table_df) <- header_row

方法二:继续用XML包手动解析补全

如果你习惯用XML包,也可以通过手动解析节点属性来补全合并单元格的内容:

library(XML)
library(RCurl)
library(dplyr)

# 你的原始请求头设置
myHttpheader <- c( 
  "User-Agent"="Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.48 Safari/537.36 QQBrowser/7.7.31732.400", 
  "Accept"="text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", 
  "Accept-Language"="en-us", 
  "Connection"="keep-alive", 
  "Accept-Charset"="GB2312,utf-8;q=0.7,*;q=0.7", 
  "Referer"="http://t.dianping.com/")
url = "http://www.gaokao.com/e/20201106/5fa4f625338d0.shtml"

# 抓取并转码网页内容
temp<-getURL(url,httpheader = myHttpheader,.encoding = "GB2312")
temp1<-iconv(temp,"GB2312","UTF-8")
doc<-htmlParse(temp1,asText = TRUE,encoding = "UTF-8")

# 获取第一个表格的所有行节点
rows <- getNodeSet(doc, "//table[1]/tr")

# 初始化变量
processed_rows <- list()
rowspan_queue <- list()

# 遍历每一行处理
for (row in rows) {
  cells <- getNodeSet(row, ".//td|.//th")
  current_row <- list()
  col_index <- 1
  
  # 处理上一行遗留的rowspan填充
  while (col_index <= length(rowspan_queue)) {
    if (rowspan_queue[[col_index]]$count > 0) {
      current_row[[col_index]] <- xmlValue(rowspan_queue[[col_index]]$node) %>% trimws()
      rowspan_queue[[col_index]]$count <- rowspan_queue[[col_index]]$count - 1
      col_index <- col_index + 1
    } else {
      rowspan_queue[[col_index]] <- NULL
    }
  }
  
  # 处理当前行的单元格
  for (cell in cells) {
    cell_text <- xmlValue(cell) %>% trimws()
    rowspan <- xmlGetAttr(cell, "rowspan", default = "1") %>% as.integer()
    colspan <- xmlGetAttr(cell, "colspan", default = "1") %>% as.integer()
    
    # 填充colspan列
    for (i in 1:colspan) {
      current_row[[col_index]] <- cell_text
      col_index <- col_index + 1
    }
    
    # 记录rowspan填充任务
    if (rowspan > 1) {
      for (i in 0:(colspan-1)) {
        rowspan_queue[[col_index - i - 1]] <- list(node = cell, count = rowspan - 1)
      }
    }
  }
  
  processed_rows[[length(processed_rows)+1]] <- current_row
}

# 转换为数据框并设置表头
max_cols <- max(sapply(processed_rows, length))
table_df <- bind_rows(lapply(processed_rows, function(row) {
  row <- c(row, rep(NA, max_cols - length(row)))
  as.data.frame(t(row), stringsAsFactors = FALSE)
}))

header_row <- table_df[1,]
table_df <- table_df[-1,]
colnames(table_df) <- header_row

核心思路都是手动解析单元格的rowspan和colspan属性,把合并的内容填充到对应的空缺位置,这样就能得到结构完整的表格数据了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 20:52:53