如何在R语言中处理含rowspan/colspan的HTML表格爬取
问题描述
使用R语言的rvest包爬取目标HTML表格时,默认调用html_table()函数会将包含rowspan属性的单元格内容(包括通过<br>标签分隔的子特征)直接合并,导致特征名称与对应数值无法正确映射。需要将表格处理为每个特征主项、子项分别占一行,数值与特征一一对应的结构化格式。
原HTML表格代码
<!--HTML for Table --> <table frame="hsides" rules="groups" class="rendered small default_table"> <thead> <tr> <th align="center" valign="middle" style="border-top:solid thin;border-bottom:solid thin" rowspan="1" colspan="1">Characteristics</th> <th align="center" valign="middle" style="border-top:solid thin;border-bottom:solid thin" rowspan="1" colspan="1">Values, n (%)</th> </tr> </thead> <tbody> <tr> <td rowspan="3" align="center" valign="middle" style="border-bottom:solid thin" colspan="1">Sex <br />Male <br />Female </td> <td align="center" valign="middle" rowspan="1" colspan="1"></td> </tr> <tr> <td align="center" valign="middle" rowspan="1" colspan="1">75 (74.3)</td> </tr> <tr> <td align="center" valign="middle" style="border-bottom:solid thin" rowspan="1" colspan="1">26 (25.7)</td> </tr> <tr> <td rowspan="2" align="center" valign="middle" style="border-bottom:solid thin" colspan="1">Age <br />&#x0003c;70 years of age <br />&#x02265;70 years of age </td> <td align="center" valign="middle" rowspan="1" colspan="1"></td> </tr> <tr> <td align="center" valign="middle" style="border-bottom:solid thin" rowspan="1" colspan="1">63 (62.4) <br />38 (37.6) </td> </tr> <tr> <td rowspan="2" align="center" valign="middle" style="border-bottom:solid thin" colspan="1">Smoking history <br />Yes <br />No </td> <td align="center" valign="middle" rowspan="1" colspan="1"></td> </tr> <tr> <td align="center" valign="middle" style="border-bottom:solid thin" rowspan="1" colspan="1">93 (92.1) <br />8 (7.9) </td> </tr> <tr> <td align="center" valign="middle" rowspan="1" colspan="1">Histology <br />Adenocarcinoma <br />Squamous <br />NSCLC poorly differentiated <br />Others </td> <td align="center" valign="middle" rowspan="1" colspan="1"> <br />69 (68.3) <br />19 (18.8) <br />9 (8.9) <br />4 (4.0) </td> </tr> <tr> <td align="center" valign="middle" style="border-top:solid thin" rowspan="1" colspan="1">Disease stage <br />IIIB <br />IV </td> <td align="center" valign="middle" style="border-top:solid thin" rowspan="1" colspan="1"> <br />2 (2.3) <br />86 (97.7) </td> </tr> <tr> <td align="center" valign="middle" style="border-top:solid thin;border-bottom:solid thin" rowspan="1" colspan="1">Brain metastases <br />Yes <br />No </td> <td align="center" valign="middle" style="border-top:solid thin;border-bottom:solid thin" rowspan="1" colspan="1">16 (15.8) <br />85 (84.2) </td> </tr> <tr> <td align="center" valign="middle" rowspan="1" colspan="1">PD-L1 TPS% <br />&#x0003c;90% <br />&#x02265;90% </td> <td align="center" valign="middle" rowspan="1" colspan="1"> <br />74 (73.3) <br />27 (26.7) </td> </tr> <tr> <td align="center" valign="middle" style="border-top:solid thin;border-bottom:solid thin" rowspan="1" colspan="1">ECOG PS <br />0 <br />1 <br />2 <br />3 </td> <td align="center" valign="middle" style="border-top:solid thin;border-bottom:solid thin" rowspan="1" colspan="1"> <br />20 (19.8) <br />43 (42.6) <br />30 (29.7) <br />8 (7.9) </td> </tr> <tr> <td align="center" valign="middle" rowspan="1" colspan="1">CCI <br />0&#x02013;2 <br />&#x02265;3 </td> <td align="center" valign="middle" rowspan="1" colspan="1"> <br />91 (90.1) <br />10 (9.9) </td> </tr> <tr> <td align="center" valign="middle" style="border-top:solid thin" rowspan="1" colspan="1">NLR <br />&#x02265;4 <br />&#x0003c;4 </td> <td align="center" valign="middle" style="border-top:solid thin" rowspan="1" colspan="1"> <br />58 (57.4) <br />43 (42.6) </td> </tr> <tr> <td align="center" valign="middle" style="border-top:solid thin;border-bottom:solid thin" rowspan="1" colspan="1">Frailty Scoring System <br />Low <br />Intermediate <br />High </td> <td align="center" valign="middle" style="border-top:solid thin;border-bottom:solid thin" rowspan="1" colspan="1"> <br />28 (27.7) <br />41 (40.6) <br />32 (31.7) </td> </tr> </tbody> </table>
原错误代码及输出
原R代码
library(rvest) tbls <- html_table(read_html("c:/GenderStats.html")) for (t in 1:length(tbls)) { assign(paste0("Table", t), tbls[[t]]) }
当前错误输出(Table1)
# A tibble: 15 × 2 Characteristics `Values, n (%)` <chr> <chr> 1 SexMaleFemale "" 2 SexMaleFemale "75 (74.3)" 3 SexMaleFemale "26 (25.7)" 4 Age<70 years of age≥70 years of age "" 5 Age<70 years of age≥70 years of age "63 (62.4)38 (37.6)" 6 Smoking historyYesNo "" 7 Smoking historyYesNo "93 (92.1)8 (7.9)" 8 HistologyAdenocarcinomaSquamousNSCLC poorly differentiatedOthers "69 (68.3)19 (18.8)9 (8.9)4 (4.0)" 9 Disease stageIIIBIV "2 (2.3)86 (97.7)" 10 Brain metastasesYesNo "16 (15.8)85 (84.2)" 11 PD-L1 TPS%<90%≥90% "74 (73.3)27 (26.7)" 12 ECOG PS0123 "20 (19.8)43 (42.6)30 (29.7)8 (7.9)" 13 CCI0–2≥3 "91 (90.1)10 (9.9)" 14 NLR≥4<4 "58 (57.4)43 (42.6)" 15 Frailty Scoring SystemLowIntermediateHigh "28 (27.7)41 (40.6)32 (31.7)"
期望输出格式(Table2)
# A tibble: 38 × 2 Characteristics `Values, n (%)` <chr> <chr> 1 Sex "" 2 Male "75 (74.3)" 3 Female "26 (25.7)" 4 Age "" 5 <70 years of age "63 (62.4)" 6 >=70 years of age "38 (37.6)" 7 Smoking history "" 8 Yes "93 (92.1)" 9 No "8 (7.9)" 10 Histology "" 11 Adenocarcinoma "69 (68.3)" 12 Squamous "19 (18.8)" 13 NSCLC poorly differentiated "9 (8.9)" 14 Others "4 (4.0)" 15 Disease stage "" 16 IIIB "2 (2.3)" 17 IV "86 (97.7)" 18 Brain metastases "" 19 Yes "16 (15.8)" 20 No "85 (84.2)" 21 PD-L1 TPS% "" 22 <90% "74 (73.3)" 23 >=90% "27 (26.7)" 24 ECOG PS "" 25 0 "20 (19.8)" 26 1 "43 (42.6)" 27 2 "30 (29.7)" 28 3 "8 (7.9)" 29 CCI "" 30 0-2 "91 (90.1)" 31 >=3 "10 (9.9)" 32 NLR "" 33 >=4 "58 (57.4)" 34 <4 "43 (42.6)" 35 Frailty Scoring System "" 36 Low "28 (27.7)" 37 Intermediate "41 (40.6)" 38 High "32 (31.7)"
解决代码
library(rvest) library(dplyr) library(stringr) # 读取HTML文件 html_content <- read_html("c:/GenderStats.html") # 提取tbody中的所有td元素,按列拆分(特征列、数值列) td_elements <- html_content %>% html_elements("tbody td") char_tds <- td_elements[seq(1, length(td_elements), 2)] value_tds <- td_elements[seq(2, length(td_elements), 2)] # 解析单个td:提取文本、按换行拆分、清理空内容、解析HTML实体 parse_single_td <- function(td_node) { td_node %>% html_text2() %>% # 自动处理<br>为换行,同时解析HTML实体 str_split("\n") %>% unlist() %>% str_trim() %>% Filter(f = function(x) x != "") } # 批量解析所有特征列和数值列 char_lists <- lapply(char_tds, parse_single_td) value_lists <- lapply(value_tds, parse_single_td) # 生成最终表格 final_table <- list() for (i in seq_along(char_lists)) { current_chars <- char_lists[[i]] current_values <- value_lists[[i]] # 对齐特征和数值的长度:数值不足时补空字符串 if (length(current_values) < length(current_chars)) { current_values <- c("", current_values) } else if (length(current_values) > length(current_chars)) { current_values <- current_values[1:length(current_chars)] } # 生成当前组的行 group_df <- tibble( Characteristics = current_chars, `Values, n (%)` = current_values ) final_table[[i]] <- group_df } # 合并所有组并输出 Table2 <- bind_rows(final_table) print(Table2, n = Inf)
内容的提问来源于stack exchange,提问作者user2886453
相关产品推荐
相关产品推荐

