使用R抓取层级网页表单所有选项组合及优化需求
尼日利亚INEC投票单元层级表单抓取解决方案
问题核心
你遇到的问题本质是递进式AJAX表单而非传统HTML表单:页面的LGA/Ward/Polling Unit选项通过异步请求动态加载,而非静态表单提交;原代码依赖rvest解析静态HTML,自然拿不到后续层级,且因表单无action属性导致提交报错。同时需要解决列表命名、慢网页抓取效率问题,最终生成标准化表格。
核心解决步骤
1. 定位异步请求接口
用浏览器开发者工具(F12→Network→XHR)抓包:
- 选择State后,页面会发送GET/POST请求到后台接口,返回对应LGA的JSON/HTML数据
- 同理,选择LGA后请求Ward接口,选择Ward后请求Polling Unit接口
- 复制这些接口的URL、请求参数(比如State ID、LGA ID)
2. 层级数据抓取与列表命名
- 先抓取所有State的名称和ID(可从页面静态下拉框或接口获取)
- 循环每个State,请求其对应的LGA列表;再循环每个LGA,请求对应的Ward列表
- 每个Ward对应的Polling Unit结果列表,命名为
{State名称}-{LGA名称}-{Ward名称}格式
3. 慢速不稳定网页的高效抓取策略
- 并行处理:用
furrr包开启多线程并行请求,减少总耗时 - 重试机制:用
httr2::req_retry()设置重试次数(比如3次),处理请求失败 - 结果缓存:用
memoise包缓存已请求的State/LGA/Ward数据,避免重复请求 - 超时设置:给每个请求设置超时(比如10秒),防止长时间阻塞
完整代码示例
library(httr2) library(purrr) library(furrr) library(memoise) library(dplyr) # 初始化并行处理,根据CPU核心数调整workers数量 plan(multisession, workers = 4) # 定义带缓存和重试的请求函数,适配接口返回格式调整resp_body_* memoised_request <- memoise(function(url, params = list()) { req <- request(url) %>% req_url_query(!!!params) %>% req_retry(max_tries = 3, backoff = ~ 2^.x) %>% req_timeout(10) %>% req_perform() %>% resp_body_json() }) # 1. 获取所有State列表(替换为实际抓包的接口) state_url <- "https://inecnigeria.org/api/elections/states" states <- memoised_request(state_url) %>% map_dfr(~tibble(state_name = .x$name, state_id = .x$id)) # 2. 抓取对应State的LGA列表 get_lgas <- function(state_id, state_name) { lga_url <- "https://inecnigeria.org/api/elections/lgas" memoised_request(lga_url, params = list(state_id = state_id)) %>% map_dfr(~tibble( state_name = state_name, lga_name = .x$name, lga_id = .x$id )) } # 3. 抓取对应LGA的Ward列表 get_wards <- function(lga_id, state_name, lga_name) { ward_url <- "https://inecnigeria.org/api/elections/wards" memoised_request(ward_url, params = list(lga_id = lga_id)) %>% map_dfr(~tibble( state_name = state_name, lga_name = lga_name, ward_name = .x$name, ward_id = .x$id )) } # 4. 抓取对应Ward的Polling Unit数据 get_polling_units <- function(ward_id, state_name, lga_name, ward_name) { pu_url <- "https://inecnigeria.org/api/elections/polling-units" memoised_request(pu_url, params = list(ward_id = ward_id)) %>% map_dfr(~tibble( state_name = state_name, lga_name = lga_name, ward_name = ward_name, polling_unit_id = .x$id, polling_unit_name = .x$name, remark = .x$remark %||% "" # 处理空备注 )) } # 5. 层级抓取并生成命名列表 lgas <- future_map2_dfr(states$state_id, states$state_name, get_lgas) wards <- future_pmap_dfr(lgas, ~get_wards(..3, ..1, ..2)) pu_list <- future_pmap(wards, function(state_name, lga_name, ward_name, ward_id) { pu_data <- get_polling_units(ward_id, state_name, lga_name, ward_name) list_name <- paste(state_name, lga_name, ward_name, sep = "-") set_names(list(pu_data), list_name) }) %>% flatten() # 6. 合并为最终标准化表格 final_table <- bind_rows(pu_list) # 查看结果 head(final_table)
最终输出说明
生成的final_table包含以下指定列:
state_name:州名称lga_name:地方政府区域名称ward_name:选区名称polling_unit_id:投票单元IDpolling_unit_name:投票单元名称remark:备注信息
内容的提问来源于stack exchange,提问作者Joke O.
相关产品推荐
相关产品推荐

