从 xml 抓取 table
scraping table from xml
http://reports.ieso.ca/public/GenOutputbyFuelHourly/PUB_GenOutputbyFuelHourly.xml
我用 rvest 试过了 --
url <-"http://reports.ieso.ca/public/GenOutputbyFuelHourly/PUB_GenOutputbyFuelHourly.xml"
url %>%
read_html() %>%
html_nodes("table") %>% html_table()
那没有用,所以接下来我尝试与 selectorgadget 结合使用 --
url %>%
read_html() %>%
html_nodes(".report td") %>% html_table()
那里也没有骰子。我对 Rselenium 有一些经验,并且能够破解以下解决方案:
library(RSelenium)
remDr <- remoteDriver(port = 4567L, browserName = "phantomjs")
remDr$open()
remDr$navigate(url)
css <- paste0(".report td")
webElems <- remDr$findElements("css", css)
values <- unlist(sapply(webElems, function(x){x$getElementText()}))
其中 "values" 现在是一个向量,每个条目都在 table 中,从左到右和从上到下。然后我基本上可以将其解析为 table - 即每 9 个条目形成一行。
但是,这太乱了,如果可能的话,我希望能够将其作为数据框拉下来。感谢任何建议,并提前致谢。
最后,我确实知道我在 rvest 中使用了 html_ 函数而不是 xml_ 函数 - 我这样做只是因为没有 xml_table 函数。总的来说,我对 HTML 不是很敏锐,所以如果有人能阐明这里的差异,我也会很感激。
在未识别您是浏览器的情况下调用时,它只是 returns 原始 XML 文件。这是一个格式良好的 XML 文件,因此处理起来非常简单(尽管获取嵌套记录有点费力)。
下面直接从XML生成一个"long"数据框:
library(xml2)
library(tidyverse)
doc <- read_xml("http://reports.ieso.ca/public/GenOutputbyFuelHourly/PUB_GenOutputbyFuelHourly.xml")
doc <- xml_ns_strip(doc) # this just reduces XPath query string complexities
数据是"nested"所以我们只需要"unnest"它:
xml_find_all(doc, ".//DailyData") %>%
map_df(~{
xml_find_all(.x, ".//HourlyData") %>%
map_df(~{
xml_find_all(.x, ".//FuelTotal") %>%
map_df(~{
data_frame(
fuel = xml_text(xml_find_first(.x, ".//Fuel"), trim=TRUE),
output = xml_double(xml_find_first(.x, ".//Output"))
)
}) %>%
mutate(hour = xml_double(xml_find_first(.x, ".//Hour")))
}) %>%
mutate(day = as.Date(xml_text(xml_find_first(.x, ".//Day"), trim=TRUE)))
})
## # A tibble: 2,448 x 4
## fuel output hour day
## <chr> <dbl> <dbl> <date>
## 1 NUCLEAR 11671 1 2018-01-01
## 2 GAS 1583 1 2018-01-01
## 3 HYDRO 4152 1 2018-01-01
## 4 WIND 992 1 2018-01-01
## 5 SOLAR 0 1 2018-01-01
## 6 BIOFUEL 3 1 2018-01-01
## 7 NUCLEAR 11672 2 2018-01-01
## 8 GAS 1192 2 2018-01-01
## 9 HYDRO 4093 2 2018-01-01
## 10 WIND 1124 2 2018-01-01
## # ... with 2,438 more rows
然而
当从浏览器上下文中调用时,它使用 XSLT 样式 sheet 来转换数据以生成 table。我们也可以模仿它,vs process XML。它有点快,但是你得到一个 "wide" 数据框与一个 "long" 数据框:
library(xml2)
library(xslt)
library(rvest)
library(tidyverse)
doc <- read_xml("http://reports.ieso.ca/public/GenOutputbyFuelHourly/PUB_GenOutputbyFuelHourly.xml")
xsl <- read_xml("http://reports.ieso.ca/docrefs/stylesheet/GenOutputbyFuelHourly_HTML_t1-1.xsl")
xml_xslt(doc, xsl) %>%
html_node(xpath=".//table[contains(., 'HYDRO')]") %>%
html_table(header=TRUE, fill=TRUE) %>%
tbl_df()
## # A tibble: 408 x 9
## Date Hour NUCLEAR GAS HYDRO WIND SOLAR BIOFUEL `Total Output`
## <chr> <int> <int> <int> <int> <int> <int> <int> <int>
## 1 2018-01-01 1 11671 1583 4152 992 0 3 18401
## 2 2018-01-01 2 11672 1192 4093 1124 0 1 18082
## 3 2018-01-01 3 11672 1040 4231 1265 0 0 18208
## 4 2018-01-01 4 11669 1041 3895 1369 0 0 17974
## 5 2018-01-01 5 11674 1004 3271 1848 0 0 17797
## 6 2018-01-01 6 11682 1048 3292 2022 0 0 18044
## 7 2018-01-01 7 11682 966 3977 2378 0 1 19004
## 8 2018-01-01 8 11682 741 4311 2542 0 4 19280
## 9 2018-01-01 9 11678 650 4058 2719 2 2 19109
## 10 2018-01-01 10 11682 556 4644 2687 13 2 19584
## # ... with 398 more rows
我(个人)倾向于使用前一种 (XML) 方法并致力于重构代码以加快转换速度(我采用 "brute force" 方式)因为这是真实的 "data"。
但是,后者可以工作并且速度更快,并且无需进一步转换就可以成为您需要的格式。
这些都不需要像 RSelenium 或 splashr 这样的额外的外部依赖。
更新
OP 关于样式 sheet 未在 Chrome 上的开发工具中的评论 100% 不正确:
在去 URL 之前,他们可能在 Chrome 没有开发工具。
http://reports.ieso.ca/public/GenOutputbyFuelHourly/PUB_GenOutputbyFuelHourly.xml
我用 rvest 试过了 --
url <-"http://reports.ieso.ca/public/GenOutputbyFuelHourly/PUB_GenOutputbyFuelHourly.xml"
url %>%
read_html() %>%
html_nodes("table") %>% html_table()
那没有用,所以接下来我尝试与 selectorgadget 结合使用 --
url %>%
read_html() %>%
html_nodes(".report td") %>% html_table()
那里也没有骰子。我对 Rselenium 有一些经验,并且能够破解以下解决方案:
library(RSelenium)
remDr <- remoteDriver(port = 4567L, browserName = "phantomjs")
remDr$open()
remDr$navigate(url)
css <- paste0(".report td")
webElems <- remDr$findElements("css", css)
values <- unlist(sapply(webElems, function(x){x$getElementText()}))
其中 "values" 现在是一个向量,每个条目都在 table 中,从左到右和从上到下。然后我基本上可以将其解析为 table - 即每 9 个条目形成一行。
但是,这太乱了,如果可能的话,我希望能够将其作为数据框拉下来。感谢任何建议,并提前致谢。
最后,我确实知道我在 rvest 中使用了 html_ 函数而不是 xml_ 函数 - 我这样做只是因为没有 xml_table 函数。总的来说,我对 HTML 不是很敏锐,所以如果有人能阐明这里的差异,我也会很感激。
在未识别您是浏览器的情况下调用时,它只是 returns 原始 XML 文件。这是一个格式良好的 XML 文件,因此处理起来非常简单(尽管获取嵌套记录有点费力)。
下面直接从XML生成一个"long"数据框:
library(xml2)
library(tidyverse)
doc <- read_xml("http://reports.ieso.ca/public/GenOutputbyFuelHourly/PUB_GenOutputbyFuelHourly.xml")
doc <- xml_ns_strip(doc) # this just reduces XPath query string complexities
数据是"nested"所以我们只需要"unnest"它:
xml_find_all(doc, ".//DailyData") %>%
map_df(~{
xml_find_all(.x, ".//HourlyData") %>%
map_df(~{
xml_find_all(.x, ".//FuelTotal") %>%
map_df(~{
data_frame(
fuel = xml_text(xml_find_first(.x, ".//Fuel"), trim=TRUE),
output = xml_double(xml_find_first(.x, ".//Output"))
)
}) %>%
mutate(hour = xml_double(xml_find_first(.x, ".//Hour")))
}) %>%
mutate(day = as.Date(xml_text(xml_find_first(.x, ".//Day"), trim=TRUE)))
})
## # A tibble: 2,448 x 4
## fuel output hour day
## <chr> <dbl> <dbl> <date>
## 1 NUCLEAR 11671 1 2018-01-01
## 2 GAS 1583 1 2018-01-01
## 3 HYDRO 4152 1 2018-01-01
## 4 WIND 992 1 2018-01-01
## 5 SOLAR 0 1 2018-01-01
## 6 BIOFUEL 3 1 2018-01-01
## 7 NUCLEAR 11672 2 2018-01-01
## 8 GAS 1192 2 2018-01-01
## 9 HYDRO 4093 2 2018-01-01
## 10 WIND 1124 2 2018-01-01
## # ... with 2,438 more rows
然而
当从浏览器上下文中调用时,它使用 XSLT 样式 sheet 来转换数据以生成 table。我们也可以模仿它,vs process XML。它有点快,但是你得到一个 "wide" 数据框与一个 "long" 数据框:
library(xml2)
library(xslt)
library(rvest)
library(tidyverse)
doc <- read_xml("http://reports.ieso.ca/public/GenOutputbyFuelHourly/PUB_GenOutputbyFuelHourly.xml")
xsl <- read_xml("http://reports.ieso.ca/docrefs/stylesheet/GenOutputbyFuelHourly_HTML_t1-1.xsl")
xml_xslt(doc, xsl) %>%
html_node(xpath=".//table[contains(., 'HYDRO')]") %>%
html_table(header=TRUE, fill=TRUE) %>%
tbl_df()
## # A tibble: 408 x 9
## Date Hour NUCLEAR GAS HYDRO WIND SOLAR BIOFUEL `Total Output`
## <chr> <int> <int> <int> <int> <int> <int> <int> <int>
## 1 2018-01-01 1 11671 1583 4152 992 0 3 18401
## 2 2018-01-01 2 11672 1192 4093 1124 0 1 18082
## 3 2018-01-01 3 11672 1040 4231 1265 0 0 18208
## 4 2018-01-01 4 11669 1041 3895 1369 0 0 17974
## 5 2018-01-01 5 11674 1004 3271 1848 0 0 17797
## 6 2018-01-01 6 11682 1048 3292 2022 0 0 18044
## 7 2018-01-01 7 11682 966 3977 2378 0 1 19004
## 8 2018-01-01 8 11682 741 4311 2542 0 4 19280
## 9 2018-01-01 9 11678 650 4058 2719 2 2 19109
## 10 2018-01-01 10 11682 556 4644 2687 13 2 19584
## # ... with 398 more rows
我(个人)倾向于使用前一种 (XML) 方法并致力于重构代码以加快转换速度(我采用 "brute force" 方式)因为这是真实的 "data"。
但是,后者可以工作并且速度更快,并且无需进一步转换就可以成为您需要的格式。
这些都不需要像 RSelenium 或 splashr 这样的额外的外部依赖。
更新
OP 关于样式 sheet 未在 Chrome 上的开发工具中的评论 100% 不正确:
在去 URL 之前,他们可能在 Chrome 没有开发工具。