从 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 没有开发工具。