一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

如何用R动态提取嵌入iframe的网页图表数据

时间:2026-06-05 10:17:37 编辑:袖梨 来源:一聚教程网

本文介绍如何使用 chromote 驱动无头 chrome,自动定位并提取由 javascript 动态渲染的 iframe 中的图表原始数据(如 google charts),绕过静态 html 无法获取动态内容的限制,并最终结构化为 r 数据框。

本文介绍如何使用 chromote 驱动无头 chrome,自动定位并提取由 javascript 动态渲染的 iframe 中的图表原始数据(如 google charts),绕过静态 html 无法获取动态内容的限制,并最终结构化为 r 数据框。

现代网页(尤其是基于 Wix、Thunderbolt 等可视化建站平台构建的站点)常将交互式图表封装在 <iframe> 中,其 src 地址并非硬编码于主页面 HTML,而是由前端 JavaScript 运行时动态生成并注入 DOM。这意味着仅靠 httr::GET() + rvest 解析原始 HTML 必然失败——你看到的只是占位 <div id="comp-iw3d16s21">,真实链接藏在 JS 执行后的 DOM 树深处。

要可靠提取此类数据,核心思路是:让 R 控制一个真实浏览器环境,完整执行页面 JS,再从渲染后的 DOM 中精准定位并读取数据。以下是以 Immostat 市场数据页 中“Office Investment Prices (gross € psqm)”图表为例的完整实现流程:

✅ 步骤 1:启动无头浏览器并加载页面

使用 chromote 创建会话,导航至目标页面,并等待 JS 渲染完成(loadEventFired() + 短暂休眠确保 iframe 加载):

library(chromote)library(rvest)library(jsonlite)library(dplyr)b <- ChromoteSession$new()b$Page$navigate("https://www.php.cn/link/0c031fe92fdc7596e4f20c56c661319d")b$Page$loadEventFired()Sys.sleep(0.5)  # 确保 iframe DOM 已挂载

✅ 步骤 2:通过语义化 XPath 定位动态 iframe

不依赖易变的 ID 或 class,而是以标题文本 "Office Investment Prices (gross € psqm)" 为锚点,用 XPath 向下查找关联的 iframe 元素并提取 src:

iframe_src <- b$Runtime$evaluate('  const xpath = '//h2[text()="Office Investment Prices (gross € psqm)"]/../following-sibling::div//iframe';  const iframe = document.evaluate(xpath, document, null,     XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null).iterateNext();  iframe ? iframe.getAttribute("src") : null;')$result$valueif (is.null(iframe_src)) stop("未找到目标 iframe")iframe_src#> "https://www-immostat-com.filesusr.com/html/ae915e_6e3e7e9d19bc8d5af1f3e4b96ae5c686.html"

⚠️ 注意:XPath 中使用 following-sibling::div//iframe 是因实际 DOM 结构中 iframe 嵌套在兄弟 div 内;若结构变化,需用浏览器开发者工具重新验证路径。

✅ 步骤 3:解析 iframe 页面中的图表初始化脚本

目标 iframe 页面内含 Google Charts 初始化代码(如 var dataIDF = google.visualization.arrayToDataTable([...]))。我们用 rvest 提取该 <script> 块,提取关键行,并将其改造为可直接返回 JSON 的表达式:

script_content <- read_html(iframe_src) %>%  html_element(xpath = "//script[contains(., 'var dataIDF')]") %>%  html_text()# 提取并重写:将 arrayToDataTable(...) 替换为 JSON.stringify(...)data_js <- script_content %>%  str_split("") %>% unlist() %>%  str_subset("var dataIDF") %>%  str_replace("var dataIDF = google.visualization.arrayToDataTable(", "JSON.stringify(")# 在已运行的 Chrome JS 环境中执行,直接获得 JSON 字符串json_str <- b$Runtime$evaluate(data_js)$result$valuechart_data_list <- parse_json(json_str)

✅ 步骤 4:结构化为规范数据框

原始解析结果为嵌套列表(首行为列名,后续行为数据行),需标准化列名与类型:

# 第一行作为列名,其余行转为命名列表header <- chart_data_list[[1]]data_rows <- chart_data_list[-1]# 统一转换:数值列自动识别(避免字符串化)df <- data_rows %>%  lapply(setNames, header) %>%  do.call(rbind, args = _) %>%  as.data.frame(stringsAsFactors = FALSE) %>%  # 强制数值列转 numeric(示例中第二列为价格)  mutate(`Greater Paris Region` = as.numeric(`Greater Paris Region`))head(df)#>   Quarter Greater Paris Region#> 1 Q1 2006                 4490#> 2 Q2 2006                 4631#> 3 Q3 2006                 4803#> 4 Q4 2006                 4837#> 5 Q1 2007                 4953#> 6 Q2 2007                 5064

? 关键注意事项与优化建议

  • 稳定性优先:避免硬编码 XPath 或类名,始终以用户可见文本(如 h2 标题)为定位基准;
  • 错误防御:添加 tryCatch() 检查 iframe_src 和 json_str 是否为空,防止网络波动或页面改版导致中断;
  • 性能考量:chromote 启动开销较大,若需高频采集,建议复用会话(b$Browser$close() 前保持连接);
  • 替代方案:RSelenium 同样可行,但 chromote 更轻量、调试更直观;若需跨平台部署,可考虑 playwright(R 包 pwa);
  • 合规提醒:遵守 robots.txt 及网站 Terms of Service;对高频请求添加合理延时,必要时设置 User-Agent。

此方法不依赖逆向分析复杂 JS 参数(如 Thunderbolt 的 35 参数 API),而是直击渲染结果,兼具鲁棒性与可维护性——只要图表标题和 iframe 逻辑结构不变,脚本即可长期有效。

热门栏目