如何在R中将不规则格式的XML表格转换为tibble?
处理不规则TEI-XML表格转tibble的R方案
核心思路:基于xml2遍历+动态适配行列
摆脱对固定属性或统一单元格数量的依赖,直接遍历表格的行与单元格节点,动态处理每一行的内容后整合成tibble。xml2::as_list()可以作为基础转换工具,但需要额外处理TEI表格节点的结构差异。
具体实现步骤
1. 解析XML并定位表格节点
先加载TEI-XML文档,用XPath定位目标表格节点(TEI规范中表格通常以<table>标签存在,可根据文档实际嵌套结构调整XPath):
library(xml2) library(tibble) library(purrr) # 加载目标TEI-XML文件 tei_doc <- read_xml("your_tei_file.xml") # 定位所有表格节点 table_nodes <- xml_find_all(tei_doc, "//table")
2. 基于xml2::as_list()转换并清理结构
as_list()会将XML节点转为嵌套列表,但TEI表格的单元格可能存在层级差异(比如嵌套文本节点、特殊属性),需要先扁平化每行内容:
# 单个表格的处理函数 process_tei_table <- function(table_node) { # 将XML表格节点转为列表 table_list <- as_list(table_node) # 提取所有行节点(假设行以<row>标签定义) rows <- table_list[names(table_list) == "row"] # 遍历每行,提取单元格文本 row_contents <- map(rows, function(row) { # 提取该行下的所有单元格节点 cells <- row[names(row) == "cell"] # 提取单元格内的文本,处理嵌套节点情况 map_chr(cells, function(cell) { paste(xml_text(as_xml_node(cell)), collapse = " ") }) }) # 动态确定最大列数(适配不规则行) max_cols <- max(lengths(row_contents)) # 给单元格数量不足的行补NA,统一列数 row_contents_padded <- map(row_contents, function(row) { if (length(row) < max_cols) { c(row, rep(NA_character_, max_cols - length(row))) } else { row } }) # 转换为tibble as_tibble(do.call(rbind, row_contents_padded)) } # 批量处理所有表格,添加表格ID区分 tables_tibble <- map_dfr(table_nodes, process_tei_table, .id = "table_id")
3. 适配特殊场景(表头、合并单元格)
如果TEI表格包含表头(<head>节点)或合并单元格(@cols属性),可以扩展函数逻辑:
# 进阶版:处理表头与合并单元格 process_tei_table_advanced <- function(table_node) { table_list <- as_list(table_node) # 提取表头文本(如果存在) header_text <- if ("head" %in% names(table_list)) { xml_text(as_xml_node(table_list$head)) } else { NULL } rows <- table_list[names(table_list) == "row"] row_contents <- map(rows, function(row) { cells <- row[names(row) == "cell"] map(cells, function(cell) { # 处理跨列合并:读取@cols属性,重复文本对应跨列数 col_span <- as.integer(xml_attr(as_xml_node(cell), "cols")) %||% 1 rep(paste(xml_text(as_xml_node(cell)), collapse = " "), col_span) }) %>% unlist() }) max_cols <- max(lengths(row_contents)) row_contents_padded <- map(row_contents, ~c(., rep(NA_character_, max_cols - length(.)))) tbl <- as_tibble(do.call(rbind, row_contents_padded)) # 设置表头(如果有) if (!is.null(header_text)) { colnames(tbl) <- strsplit(header_text, "\\s+")[[1]][1:ncol(tbl)] } tbl }
方案优势
- 无属性依赖:无需硬编码
@role='label'这类特定属性,直接基于TEI标准的<row>、<cell>节点结构遍历,适配多数TEI表格。 - 动态适配行列:自动识别每行的单元格数量,短行自动补NA,完美兼容行列数不规则的表格。
- 扩展性强:可根据需求快速添加对合并单元格、表头、特殊文本节点的处理逻辑,适配不同TEI文档的个性化结构。
内容的提问来源于stack exchange,提问作者Will Hanley
相关产品推荐
相关产品推荐

