R语言网页抓取:提取class='result-item'中特定关键词后内容的gsub正则表达式问题求助
问题:抓取美国115届国会议员数据时,正则表达式提取详情字段失败的排查与修正
我最近尝试抓取美国国会网站上115届议员的数据,想要构建一个包含Name、State、District、Party、ServedHouse、ServedSenate字段的数据框。Name字段通过专属的result-heading class已经顺利提取,但State、District这类详情字段因为都使用相同的result-item class,提取时遇到了麻烦。我参考相关内容修改了代码,试图通过gsub()配合正则表达式提取关键词后的内容,但始终没能成功,推测是正则表达式的写法出了问题。
以下是我最初的代码:
library (dplyr) library (xml2) library (rvest) Congress100 <- read_html("https://www.congress.gov/members?q=%7B%22congress%22%3A115%7D") name <- Congress100 %>% rvest::html_nodes('body') %>% xml2::xml_find_all("//span[contains(@class, 'result-heading')]") %>% rvest::html_text() Name <- unique(name) details <- Congress100 %>% rvest::html_nodes('body') %>% xml2::xml_find_all("//span[contains(@class, 'result-item')]") %>% rvest::html_text() State <- gsub('^.*State:\ns*|\\s*<\/p>\n.*$', '', details) District <- gsub('^.*District:\ns*|\\s*<\/p>\n.*$', '', details) Party <- gsub('^.*Party:\ns*|\\s*<\/p>\n.*$', '', details) ServedHouse <- gsub('^.*House:\ns*|\\s*<\/p>\n.*$', '', details) ServedSenate <- gsub('^.*Senate:\ns*|\\s*<\/p>\n.*$', '', details) df <- data.frame(Name, State, District, Party, ServedHouse, ServedSenate)
问题排查
我仔细检查了代码后,发现两个核心问题:
- 数据结构混乱:原代码把所有议员的
result-item详情提取成了一个大向量,导致每个gsub处理的是所有详情的混合文本,无法对应到单个议员的信息。 - 正则表达式错误:
- 正则里包含了
<\/p>,但html_text()已经把HTML标签完全移除了,这部分内容根本不存在,自然匹配不到。 \ns*的写法有误,在R字符串中反斜杠需要转义,应该写成\\s*;而且这种前后截断的匹配逻辑不适合独立的result-item条目。
- 正则里包含了
修正方案
更可靠的做法是先按议员分组,把每个议员的所有详情放在一起处理,再将详情转换成键值对提取对应字段:
library(dplyr) library(xml2) library(rvest) library(purrr) library(stringr) library(tibble) # 读取网页 Congress115 <- read_html("https://www.congress.gov/members?q=%7B%22congress%22%3A115%7D") # 提取每个议员的容器节点 members <- Congress115 %>% html_nodes(".expanded") # 定义函数,从单个议员节点提取所有字段 extract_member_info <- function(node) { # 提取姓名 member_name <- node %>% html_node(".result-heading") %>% html_text(trim = TRUE) # 提取该议员的所有详情项 member_details <- node %>% html_nodes(".result-item") %>% html_text(trim = TRUE) # 将详情项转换为命名向量(键值对) details_keyval <- member_details %>% str_split_fixed(":\\s*", 2) %>% as.data.frame(stringsAsFactors = FALSE) %>% deframe() # 提取所需字段,缺失则设为NA state <- ifelse(is.null(details_keyval["State"]), NA, details_keyval["State"]) district <- ifelse(is.null(details_keyval["District"]), NA, details_keyval["District"]) party <- ifelse(is.null(details_keyval["Party"]), NA, details_keyval["Party"]) served_house <- ifelse(is.null(details_keyval["House"]), NA, details_keyval["House"]) served_senate <- ifelse(is.null(details_keyval["Senate"]), NA, details_keyval["Senate"]) # 返回该议员的 tibble tibble( Name = member_name, State = state, District = district, Party = party, ServedHouse = served_house, ServedSenate = served_senate ) } # 批量处理所有议员,合并为数据框 congress_df <- map_dfr(members, extract_member_info)
方案说明
- 先通过
.expanded类提取每个议员的独立容器,确保每个议员的信息是独立的,避免混合。 - 将每个议员的详情项拆分成键值对,这样可以直接通过键名(比如"State")提取对应的值,比正则匹配更稳定。
- 处理了字段缺失的情况(比如参议员没有
District字段),避免出现错误。
内容的提问来源于stack exchange,提问作者M1ke
相关产品推荐
相关产品推荐

