如何用rvest抓取FiveThirtyEight中JavaScript渲染的选举预测数据?
解决用rvest抓取FiveThirtyEight 2016民主党初选每日预测数据的问题
嘿,我完全懂你现在的困扰——想用rvest爬取动态生成的图表数据,结果发现数据藏在JavaScript里,静态HTML里只有空壳,折腾了好几天还是没头绪对吧?别慌,咱们一步步拆解这个问题,找到最靠谱的解决方案。
首先得搞明白为什么你之前的代码没效果:read_html()只能抓取页面的静态HTML内容,而这个页面的图表数据是通过JavaScript动态加载并渲染的,所以你拿到的<div id="polling-avg-chart"></div>只是个空容器,实际数据根本不在里面。
接下来给你两个实用的解决思路,优先试试第一个,效率最高:
思路1:直接提取页面中的JavaScript数据变量
很多数据可视化类的网站会把原始数据存在全局JavaScript变量里,方便前端渲染。你可以这样操作:
- 找到存储数据的JS变量:打开目标页面,按
Ctrl+U查看页面源代码,搜索关键词比如pollingAverage、chartData或者dailyPredictions——你要找的每日预测数据大概率藏在某个类似var xxx = {...}的JS对象里。 - 用rvest提取并解析这个变量:把包含数据的脚本内容抓出来,用正则表达式提取JSON格式的字符串,再转成R的数据框。
给你一段可参考的代码:
library(rvest) library(jsonlite) library(stringr) # 目标URL url <- "https://projects.fivethirtyeight.com/election-2016/national-primary-polls/democratic/" # 获取页面HTML page <- read_html(url) # 抓取所有script标签,筛选出包含目标数据的那个 all_scripts <- html_nodes(page, "script") target_script <- html_text(all_scripts[str_detect(html_text(all_scripts), "pollingAverage")]) # 用正则提取JS对象的JSON部分(根据实际找到的变量名调整) json_content <- str_extract(target_script, "var chartData = (.*?);") json_content <- str_remove_all(json_content, c("var chartData = ", ";")) # 解析JSON为数据框 polling_df <- fromJSON(json_content) # 提取每日预测数据(这里的键名需要根据实际JSON结构调整) daily_predictions <- polling_df$pollingAverage head(daily_predictions)
这段代码的核心是找到存储数据的JS变量,直接拿到原始的数值数据,比你去转换SVG坐标(cx/cy)靠谱多了——毕竟坐标还要对应坐标轴刻度计算,既麻烦又容易出错。
思路2:用RSelenium模拟浏览器加载动态内容
如果实在找不到JS变量,或者页面的JS逻辑太复杂,那就用RSelenium模拟真实浏览器的行为,等JavaScript执行完、图表渲染完成后再抓取数据。
步骤如下:
library(RSelenium) library(rvest) library(jsonlite) # 启动Chrome浏览器(需要先安装Selenium Server,或者用Docker简化环境配置) driver <- rsDriver(browser = "chrome", port = 4567L) remDr <- driver[["client"]] # 访问目标页面 remDr$navigate("https://projects.fivethirtyeight.com/election-2016/national-primary-polls/democratic/") # 等待页面完全加载(根据网络情况调整等待时间) Sys.sleep(5) # 获取渲染后的页面源代码 page_source <- remDr$getPageSource()[[1]] page <- read_html(page_source) # 接下来你可以重复思路1的步骤,提取JS变量;或者直接抓取页面上的元素内容 # 比如抓图表的 tooltip 数据,但还是优先找JS变量更高效 # 用完记得关闭浏览器 remDr$close() driver$server$stop()
总结一下
优先尝试思路1,直接提取JS里的原始数据,这是最快捷的方式;如果页面结构特殊找不到变量,再用思路2模拟浏览器。这样你就能轻松把每日预测百分比整理成规整的dataframe了。
内容的提问来源于stack exchange,提问作者Kim
相关产品推荐
相关产品推荐

