R rvest 抓取反应性 iframe table
R rvest scraping reactive iframe table
我正在尝试从 iframe 网站中的 table 中提取数据(所有国家和年份)。下面是我试过的一些代码。
通过阅读此处的各种帖子,我设法获得了 iframe https://apps7.unep.org/contribution/ 中 table 的实际网站地址。我尝试了几种提取数据的方法,包括各种 xpath 和 html 节点,但似乎没有任何效果。我之前没有处理过反应性 tables,非常感谢您的帮助。如果有人能为我提供正确的代码,那将是非常棒的。
我愿意接受 selenium 是绝对必要的,但不太了解它,如果可能的话我更喜欢 rvest
提前致谢
library(rvest)
library(tidyverse)
library(dplyr)
# This address has the table within an iframe
url = "https://www.unep.org/about-un-environment/funding-and-partnerships/check-your-contributions"
# This table gets the website where the data in the iframe actually comes from
base_website = url %>%
read_html(url) %>%
html_node("iframe") %>%
html_attr("src")
# I would like to get the data from the table on the above website
my_table = read_html(base_website) %>%
#extract(1)
#html_node("body div") %>%
html_table()
我建议使用包 datapasta
。将行和列复制并粘贴到文本编辑器以清除名称行。然后在插件部分使用 datapasta
paste_as_tribble 进行复制和粘贴。
library(datapasta)
mydata<- tibble::tribble(.... ) head(mydata)
#A tibble: 6 x 6
Country Region Share Pledge Paid Unpaid
<chr> <chr> <dbl> <dbl> <dbl> <dbl>
1 Netherlands Europe 9090909. 9229600 9229600 0
2 Germany Europe 8857042. 8887815. 8887815. 0
3 France Europe 7550550 7550550 7550550 0
4 United States of America North America 0 6615000 6615000 0
5 Belgium Europe 4687600 6110370 6110370 0
6 Sweden Europe 5053036 5053036 5053036 0
解释:
每个页面都有动态内容,包括 iframe 文档。数据来自 API 个基于可用年份的调用。
年份由一个 js 文件确定 https://apps7.unep.org/contribution/js/app.72fa461e.js
,您可以从您请求的 iframe 文档中提取该文件。
currentYear: function() {
var e = new Date;
return e.getFullYear()
},
yearsRange: function() {
for (var e = this.currentYear, t = [], a = e; a >= 1973; a--) t.push(a);
return t
}
您可以从此文件中提取开始年份,并对当前年份使用相同的逻辑。
接下来,生成完整的年份集并与基础 API 端点结合:
years <- start_year:end_year
requests <- paste0("https://apps7.unep.org/dwh/api/v1/contribution/by-year/", years)
发出请求以将 JSON 响应收集到列表中。然后应用自定义函数将数据提取为 DataFrame。这涉及生成 DataFrame 的嵌套调用,以便处理每个 API 响应包含 resp$data$amt
的嵌套列表。内部 DataFrame 映射到最终的外部 DataFrame。
您需要处理不同年份和地区的项目数量不同的事实,例如2019 年
我通过合并一个包含所有可能键的 DataFrame 来做到这一点
map_dfr(., function(x) {
merge(map(x, ~ ifelse(is.null(.x), NA, .x)) %>%
data.frame(), df_blank, all = T) %>% .[rowSums(is.na(.)) != ncol(.), ]
})
在同一部分中,我还处理了从 2019 年开始出现在某些 region_name 条目中的 NULL:
我还确保将年份添加到返回的内部 DataFrame。
您可以根据需要重新排列列。
速度相当快,但您可以开始优化,例如并行化请求。
回复:
library(rvest)
library(jsonlite)
library(tidyverse)
year_data <- function(x) {
info <- x$resp$data$amt
year_name <- info %>% names()
year <- year_name %>% as.integer()
year_df <- info %>%
.[[year_name]] %>%
map_dfr(., function(x) {
merge(map(x, ~ ifelse(is.null(.x), NA, .x)) %>%
data.frame(), df_blank, all = T) %>% .[rowSums(is.na(.)) != ncol(.), ]
}) %>%
mutate(Year = year)
return(year_df)
}
df_blank <- data.frame(
"country_iso2" = NA_character_,
"country_iso3" = NA_character_,
"country_name" = NA_character_,
"ec" = NA_character_,
"ef_paid" = NA_integer_,
"ef_pledged" = NA_integer_,
"region_name" = NA_character_,
"unep_region_code" = NA_character_,
"visc" = NA_character_
)
url <- "https://www.unep.org/about-un-environment/funding-and-partnerships/check-your-contributions"
# This table gets the website where the data in the iframe actually comes from
contribution_iframe_src <- url %>%
read_html(url) %>%
html_element("iframe[src*='/contribution/']") %>%
html_attr("src")
contribution_page <- read_html(contribution_iframe_src) # this is a dynamic page requiring JS to run. Instead, go to the JS file and extract required info.
contribution_years_js_file <- contribution_page %>%
html_element("[href*='contribution/js']") %>%
html_attr("href") %>%
url_absolute(contribution_iframe_src)
js_file <- read_html(contribution_years_js_file)
start_year <- js_file %>%
html_text() %>%
str_match("yearsRange.*>=(\d{4});") %>%
.[, 2] %>%
as.integer()
end_year <- as.integer(format(Sys.Date(), "%Y"))
years <- start_year:end_year
requests <- paste0("https://apps7.unep.org/dwh/api/v1/contribution/by-year/", years)
data <- map(requests, read_json)
df <- map_dfr(data, year_data)
示例输出:
我正在尝试从 iframe 网站中的 table 中提取数据(所有国家和年份)。下面是我试过的一些代码。
通过阅读此处的各种帖子,我设法获得了 iframe https://apps7.unep.org/contribution/ 中 table 的实际网站地址。我尝试了几种提取数据的方法,包括各种 xpath 和 html 节点,但似乎没有任何效果。我之前没有处理过反应性 tables,非常感谢您的帮助。如果有人能为我提供正确的代码,那将是非常棒的。
我愿意接受 selenium 是绝对必要的,但不太了解它,如果可能的话我更喜欢 rvest
提前致谢
library(rvest)
library(tidyverse)
library(dplyr)
# This address has the table within an iframe
url = "https://www.unep.org/about-un-environment/funding-and-partnerships/check-your-contributions"
# This table gets the website where the data in the iframe actually comes from
base_website = url %>%
read_html(url) %>%
html_node("iframe") %>%
html_attr("src")
# I would like to get the data from the table on the above website
my_table = read_html(base_website) %>%
#extract(1)
#html_node("body div") %>%
html_table()
我建议使用包 datapasta
。将行和列复制并粘贴到文本编辑器以清除名称行。然后在插件部分使用 datapasta
paste_as_tribble 进行复制和粘贴。
library(datapasta)
mydata<- tibble::tribble(.... ) head(mydata)
#A tibble: 6 x 6
Country Region Share Pledge Paid Unpaid
<chr> <chr> <dbl> <dbl> <dbl> <dbl>
1 Netherlands Europe 9090909. 9229600 9229600 0
2 Germany Europe 8857042. 8887815. 8887815. 0
3 France Europe 7550550 7550550 7550550 0
4 United States of America North America 0 6615000 6615000 0
5 Belgium Europe 4687600 6110370 6110370 0
6 Sweden Europe 5053036 5053036 5053036 0
解释:
每个页面都有动态内容,包括 iframe 文档。数据来自 API 个基于可用年份的调用。
年份由一个 js 文件确定 https://apps7.unep.org/contribution/js/app.72fa461e.js
,您可以从您请求的 iframe 文档中提取该文件。
currentYear: function() {
var e = new Date;
return e.getFullYear()
},
yearsRange: function() {
for (var e = this.currentYear, t = [], a = e; a >= 1973; a--) t.push(a);
return t
}
您可以从此文件中提取开始年份,并对当前年份使用相同的逻辑。
接下来,生成完整的年份集并与基础 API 端点结合:
years <- start_year:end_year
requests <- paste0("https://apps7.unep.org/dwh/api/v1/contribution/by-year/", years)
发出请求以将 JSON 响应收集到列表中。然后应用自定义函数将数据提取为 DataFrame。这涉及生成 DataFrame 的嵌套调用,以便处理每个 API 响应包含 resp$data$amt
的嵌套列表。内部 DataFrame 映射到最终的外部 DataFrame。
您需要处理不同年份和地区的项目数量不同的事实,例如2019 年
我通过合并一个包含所有可能键的 DataFrame 来做到这一点
map_dfr(., function(x) {
merge(map(x, ~ ifelse(is.null(.x), NA, .x)) %>%
data.frame(), df_blank, all = T) %>% .[rowSums(is.na(.)) != ncol(.), ]
})
在同一部分中,我还处理了从 2019 年开始出现在某些 region_name 条目中的 NULL:
我还确保将年份添加到返回的内部 DataFrame。
您可以根据需要重新排列列。
速度相当快,但您可以开始优化,例如并行化请求。
回复:
library(rvest)
library(jsonlite)
library(tidyverse)
year_data <- function(x) {
info <- x$resp$data$amt
year_name <- info %>% names()
year <- year_name %>% as.integer()
year_df <- info %>%
.[[year_name]] %>%
map_dfr(., function(x) {
merge(map(x, ~ ifelse(is.null(.x), NA, .x)) %>%
data.frame(), df_blank, all = T) %>% .[rowSums(is.na(.)) != ncol(.), ]
}) %>%
mutate(Year = year)
return(year_df)
}
df_blank <- data.frame(
"country_iso2" = NA_character_,
"country_iso3" = NA_character_,
"country_name" = NA_character_,
"ec" = NA_character_,
"ef_paid" = NA_integer_,
"ef_pledged" = NA_integer_,
"region_name" = NA_character_,
"unep_region_code" = NA_character_,
"visc" = NA_character_
)
url <- "https://www.unep.org/about-un-environment/funding-and-partnerships/check-your-contributions"
# This table gets the website where the data in the iframe actually comes from
contribution_iframe_src <- url %>%
read_html(url) %>%
html_element("iframe[src*='/contribution/']") %>%
html_attr("src")
contribution_page <- read_html(contribution_iframe_src) # this is a dynamic page requiring JS to run. Instead, go to the JS file and extract required info.
contribution_years_js_file <- contribution_page %>%
html_element("[href*='contribution/js']") %>%
html_attr("href") %>%
url_absolute(contribution_iframe_src)
js_file <- read_html(contribution_years_js_file)
start_year <- js_file %>%
html_text() %>%
str_match("yearsRange.*>=(\d{4});") %>%
.[, 2] %>%
as.integer()
end_year <- as.integer(format(Sys.Date(), "%Y"))
years <- start_year:end_year
requests <- paste0("https://apps7.unep.org/dwh/api/v1/contribution/by-year/", years)
data <- map(requests, read_json)
df <- map_dfr(data, year_data)
示例输出: