如何用R的selenider/rvest点击JS按钮抓取多页选举数据表格?
解决方案:无需Selenium,通过表单回发爬取多页表格
这类ASP.NET驱动的分页页面,核心是通过表单POST回发实现翻页,而非直接的GET链接。我们可以用rvest+httr模拟这个过程,步骤如下:
1. 分析分页机制
页面的分页按钮触发的是__doPostBack JavaScript函数,本质是向服务器提交包含状态参数的表单,关键参数包括:
__VIEWSTATE、__VIEWSTATEGENERATOR、__EVENTVALIDATION:ASP.NET的页面状态验证字段,每次回发后会更新__EVENTTARGET:触发回发的控件ID(这里是表格的ID:ctl00$ContentPlaceHolderBody$gvLocalities)__EVENTARGUMENT:分页指令,格式为Page$页码(如Page$2表示第2页)
2. 完整爬虫代码
加载依赖包
library(rvest) library(httr) library(tidyverse)
初始化会话与获取第一页数据
# 目标页面URL base_url <- "https://electorate.aec.gov.au/LocalitySearchResults.aspx?filter=0872&filterby=Postcode" # 创建会话以保持Cookie和会话状态 session <- session(base_url) first_page <- session %>% read_html() # 提取第一页表格 first_table <- first_page %>% html_element("#ctl00_ContentPlaceHolderBody_gvLocalities") %>% html_table(header = TRUE) %>% clean_names() # 提取页面状态字段(必须,否则后续回发会失败) viewstate <- first_page %>% html_element("#__VIEWSTATE") %>% html_attr("value") viewstategenerator <- first_page %>% html_element("#__VIEWSTATEGENERATOR") %>% html_attr("value") eventvalidation <- first_page %>% html_element("#__EVENTVALIDATION") %>% html_attr("value") # 提取总页数(从分页摘要文本中解析) page_summary <- first_page %>% html_element(".pagingSummary") %>% html_text() total_pages <- as.integer(str_extract(page_summary, "of (\\d+)") %>% str_remove("of "))
定义单页爬取函数
get_single_page <- function(page_num, session, vs, vsg, ev) { # 构造POST表单数据 form_data <- list( "__VIEWSTATE" = vs, "__VIEWSTATEGENERATOR" = vsg, "__EVENTVALIDATION" = ev, "__EVENTTARGET" = "ctl00$ContentPlaceHolderBody$gvLocalities", "__EVENTARGUMENT" = paste0("Page$", page_num) ) # 提交POST请求 response <- session %>% session_post( url = base_url, body = form_data, encode = "form" ) # 解析返回页面 new_page <- response %>% read_html() # 提取当前页表格 current_table <- new_page %>% html_element("#ctl00_ContentPlaceHolderBody_gvLocalities") %>% html_table(header = TRUE) %>% clean_names() # 更新状态字段(下一页请求需要用新的状态值) new_vs <- new_page %>% html_element("#__VIEWSTATE") %>% html_attr("value") new_vsg <- new_page %>% html_element("#__VIEWSTATEGENERATOR") %>% html_attr("value") new_ev <- new_page %>% html_element("#__EVENTVALIDATION") %>% html_attr("value") # 返回表格和新状态 list(table = current_table, vs = new_vs, vsg = new_vsg, ev = new_ev) }
遍历所有页面并合并数据
# 初始化存储列表 all_tables <- list(first_table) current_vs <- viewstate current_vsg <- viewstategenerator current_ev <- eventvalidation # 从第2页开始循环爬取 for (page in 2:total_pages) { page_result <- get_single_page(page, session, current_vs, current_vsg, current_ev) all_tables[[page]] <- page_result$table # 更新状态字段 current_vs <- page_result$vs current_vsg <- page_result$vsg current_ev <- page_result$ev # 可选:添加延迟避免请求过快被拦截 Sys.sleep(1) } # 合并所有表格为数据框 final_data <- bind_rows(all_tables)
关键说明
- 必须使用
session保持会话状态,否则服务器会拒绝重复请求 - 每次回发后要更新
__VIEWSTATE等状态字段,这些值是动态生成的,不能复用 - 提取总页数时,依赖页面的
.pagingSummary元素文本(格式为Page X of Y),如果页面结构变化,需要调整正则提取规则
内容的提问来源于stack exchange,提问作者Hassan Nasir Mirbahar
相关产品推荐
相关产品推荐

