Соскоб HTML-таблицы в R - PullRequest
       11

Соскоб HTML-таблицы в R

0 голосов
/ 01 марта 2019

Я пытаюсь получить таблицу по следующему URL: https://www.cenace.gob.mx/DocsMEM/OpeMdo/OfertaCompVent/OferVenta/MDA/Termicas/OfeVtaTermicaHor%20SIN%20MDA%20Hor%202018-12-31%20v2019%2003%2001_01%2000%2001.html

Проблема в том, что таблица не является таблицей html, поэтому html_table () не работает.

Пока я пытался извлечь узел из таблицы, но он ничего не возвращает

url = "https://www.cenace.gob.mx/DocsMEM/OpeMdo/OfertaCompVent/OferVenta/MDA/Termicas/OfeVtaTermicaHor%20BCS%20MDA%20Hor%202018-12-26%20v2019%2002%2024_01%2000%2001.html"
webpage <- read_html(url)
table_html <- html_nodes(webpage, 'table#Tabc')
table <- html_table(table_html)

Ответы [ 2 ]

0 голосов
/ 01 марта 2019

Следующее не очень элегантно, но должно работать!

library(curl)
library(xml2)

url = "https://www.cenace.gob.mx/DocsMEM/OpeMdo/OfertaCompVent/OferVenta/MDA/Termicas/OfeVtaTermicaHor%20BCS%20MDA%20Hor%202018-12-26%20v2019%2002%2024_01%2000%2001.html"
fi <- tempfile()

h <- new_handle(ssl_verifypeer = FALSE)
str_page <- rawToChar(curl_fetch_memory(url, h)$content)
xml_page <- read_html(str_page)
txt <- xml_text(xml_find_all(xml_page, "//script"))
txt <- unlist(strsplit(txt, ";", fixed = TRUE))
str(as.list(txt))

clean <- function(x) trimws(gsub('"', "", x))

cnames <- txt[grep("vnctab\\s*=", txt)]
cnames <- gsub("(^.*?\\[|\\]\\s*$)", "", cnames)
cnames <- clean(unlist(strsplit(cnames, ",")))

tab <- txt[grep("vdatrep\\s*=", txt)]
substr(tab, 1, 1000)
substr(tab, nchar(tab)-1000, nchar(tab))
tab <- gsub("^.*?\\[\\s*\\[", "", tab)
tab <- gsub("\\],*\\s*\\]$", "", tab)
tab_rows <- unlist(strsplit(tab, "\\]\\s*,*\\s*\\["))
tab <- strsplit(tab_rows, ",")

M <- do.call(rbind, lapply(tab, clean))
d1 <- as.data.frame(M[,1:2], stringsAsFactors = FALSE)
d2 <- as.data.frame(apply(M[,-(1:2)], 2, as.double), stringsAsFactors = FALSE)
d <-  cbind(d1, d2)
dim(d); length(cnames)
colnames(d) <- cnames
sapply(d, class)
str(d)
0 голосов
/ 01 марта 2019

Итак, проблема в том, что страница отображается с помощью javascript.Поэтому rvest само по себе не будет работать.Один из самых простых способов исправить это - использовать безголовый веб-браузер.Мы можем использовать PhantomJS.

Сначала загрузите соответствующую версию PhantomJS и поместите исполняемый файл (при условии Windows) в свой рабочий каталог.То есть буквально phantomjs.exe находится в рабочем каталоге скрипта R.

Создайте файл scrape.js:

// scrape.js

var webPage = require('webpage');
var page = webPage.create();

var fs = require('fs');
var path = 'page.html';

page.open('https://www.cenace.gob.mx/DocsMEM/OpeMdo/OfertaCompVent/OferVenta/MDA/Termicas/OfeVtaTermicaHor%20BCS%20MDA%20Hor%202018-12-26%20v2019%2002%2024_01%2000%2001.html', function (status) {
  var content = page.content;
  fs.write(path,content,'w');
  phantom.exit();
});

Этот файл scrape.js после запуска, создаст файл page.html в вашем рабочем каталоге.Вернувшись в R или RStudio, вы можете сделать следующее:

library(tidyverse)
library(rvest)

# Run scrape.js with PhantomJS to create the file page.html
system("./phantomjs scrape.js")

# Now we should be in business as usual:
read_html('page.html') %>%
  html_nodes("table#Tabc") %>%
  html_table(header = TRUE) %>%
  .[[1]] %>%
  as_tibble()

# A tibble: 504 x 38
   Codigo `Estatus asigna~  Hora `Limite de desp~ `Limite de desp~ `Costo de Opera~ `Bloque de Pote~ `Costo Incremen~ `Bloque de Pote~
   <chr>  <chr>            <int>            <dbl>            <dbl>            <dbl>            <dbl>            <dbl>            <dbl>
 1 BTY5W~ ECO                  1               35               20           43212.              1.5            1762.              1.5
 2 BTY5W~ ECO                  2               35               20           43212.              1.5            1762.              1.5
 3 BTY5W~ ECO                  3               35               20           43212.              1.5            1762.              1.5
 4 BTY5W~ ECO                  4               35               20           43212.              1.5            1762.              1.5
 5 BTY5W~ ECO                  5               35               20           43212.              1.5            1762.              1.5
 6 BTY5W~ ECO                  6               35               20           43212.              1.5            1762.              1.5
 7 BTY5W~ ECO                  7               35               20           43212.              1.5            1762.              1.5
 8 BTY5W~ ECO                  8               35               20           43212.              1.5            1762.              1.5
 9 BTY5W~ ECO                  9               35               20           43212.              1.5            1762.              1.5
10 BTY5W~ ECO                 10               35               20           43212.              1.5            1762.              1.5
# ... with 494 more rows, and 29 more variables: `Costo Incremental de generacion Bloque 02 ($/MWh)` <dbl>, `Bloque de Potencia 03 (MW)` <dbl>,
#   `Costo Incremental de generacion Bloque 03 ($/MWh)` <dbl>, `Bloque de Potencia 04 (MW)` <dbl>, `Costo Incremental de generacion Bloque 04
#   ($/MWh)` <dbl>, `Bloque de Potencia 05 (MW)` <dbl>, `Costo Incremental de generacion Bloque 05 ($/MWh)` <dbl>, `Bloque de Potencia 06
#   (MW)` <dbl>, `Costo Incremental de generacion Bloque 06 ($/MWh)` <dbl>, `Bloque de Potencia 07 (MW)` <dbl>, `Costo Incremental de generacion
#   Bloque 07 ($/MWh)` <dbl>, `Bloque de Potencia 08 (MW)` <dbl>, `Costo Incremental de generacion Bloque 08 ($/MWh)` <dbl>, `Bloque de Potencia
#   09 (MW)` <dbl>, `Costo Incremental de generacion Bloque 09 ($/MWh)` <dbl>, `Bloque de Potencia 10 (MW)` <dbl>, `Costo Incremental de
#   generacion Bloque 10 ($/MWh)` <dbl>, `Bloque de Potencia 11 (MW)` <dbl>, `Costo Incremental de generacion Bloque 11 ($/MWh)` <dbl>, `Reserva
#   rodante 10 min (MW)` <dbl>, `Costo Reserva rodante 10 min ($/MW)` <dbl>, `Reserva no rodante 10 min (MW)` <dbl>, `Costo Reserva no rodante 10
#   min ($/MW)` <dbl>, `Reserva rodante suplementaria (MW)` <dbl>, `Costo Reserva rodante suplementaria ($/MW)` <dbl>, `Reserva no rodante
#   suplementaria (MW)` <dbl>, `Costo Reserva no rodante suplementaria ($/MW)` <dbl>, `Reserva regulacion secundaria (MW)` <dbl>, `Costo Reserva
#   regulacion secundaria ($/MW` <dbl>

Обновление масштаба до нескольких URL-адресов

Сначала измените файл scrape.js, чтобы он принимал аргументы:

// scrape2.js

var webPage = require('webpage');
var page = webPage.create();
var system = require('system');
var args = system.args;

var fs = require('fs');
var path = args[2];

page.open(args[1], function (status) {
  var content = page.content;
  fs.write(path,content,'w');
  phantom.exit();
});

Затем создайте списки для зацикливания / обхода / отображения (очевидно, это можно было бы очистить / абстрагировать, чтобы было проще поддерживать и было меньше необходимости печатать):

urls <- list(
  'https://www.cenace.gob.mx/DocsMEM/OpeMdo/OfertaCompVent/OferVenta/MDA/Termicas/OfeVtaTermicaHor%20BCS%20MDA%20Hor%202018-12-26%20v2019%2002%2024_01%2000%2001.html',
  'https://www.cenace.gob.mx/DocsMEM/OpeMdo/OfertaCompVent/OferVenta/MDA/Termicas/OfeVtaTermicaHor%20SIN%20MDA%20Hor%202018-12-29%20v2019%2002%2027_01%2000%2001.html',
  'https://www.cenace.gob.mx/DocsMEM/OpeMdo/OfertaCompVent/OferVenta/MDA/Termicas/OfeVtaTermicaHor%20SIN%20MDA%20Hor%202018-12-29%20v2019%2002%2027_01%2000%2001.html'
)

paths <- list(
  'page1.html',
  'page2.html',
  'page3.html'
)

args_list <- map2(urls, paths, paste)

# We are only using this function for the file creation side-effects,
# so we can use walk instead of map. 
# This creates the files: page1.html, page2.html, and page3.html 
walk(args_list, ~ system(paste("./phantomjs scrape2.js", .)))

На этом этапе вам, вероятно, захочется добавить информацию в функцию:

read_page <- function(page) {
  read_html(page) %>%
    html_nodes("table#Tabc") %>%
    html_table(header = TRUE) %>%
    .[[1]] %>%
    as_tibble()
}

И оттуда вы можете повторно использовать список путей для отображения вашей новой функции:

paths %>%
  map(~ read_page(.)) %>%
  bind_rows()

# A tibble: 9,000 x 38
   Codigo `Estatus asigna~  Hora `Limite de desp~ `Limite de desp~ `Costo de Opera~ `Bloque de Pote~ `Costo Incremen~ `Bloque de Pote~
   <chr>  <chr>            <int>            <dbl>            <dbl>            <dbl>            <dbl>            <dbl>            <dbl>
 1 BTY5W~ ECO                  1               35               20           43212.              1.5            1762.              1.5
 2 BTY5W~ ECO                  2               35               20           43212.              1.5            1762.              1.5
 3 BTY5W~ ECO                  3               35               20           43212.              1.5            1762.              1.5
 4 BTY5W~ ECO                  4               35               20           43212.              1.5            1762.              1.5
 5 BTY5W~ ECO                  5               35               20           43212.              1.5            1762.              1.5
 6 BTY5W~ ECO                  6               35               20           43212.              1.5            1762.              1.5
 7 BTY5W~ ECO                  7               35               20           43212.              1.5            1762.              1.5
 8 BTY5W~ ECO                  8               35               20           43212.              1.5            1762.              1.5
 9 BTY5W~ ECO                  9               35               20           43212.              1.5            1762.              1.5
10 BTY5W~ ECO                 10               35               20           43212.              1.5            1762.              1.5
# ... with 8,990 more rows, and 29 more variables: `Costo Incremental de generacion Bloque 02 ($/MWh)` <dbl>, `Bloque de Potencia 03 (MW)` <dbl>,
#   `Costo Incremental de generacion Bloque 03 ($/MWh)` <dbl>, `Bloque de Potencia 04 (MW)` <dbl>, `Costo Incremental de generacion Bloque 04
#   ($/MWh)` <dbl>, `Bloque de Potencia 05 (MW)` <dbl>, `Costo Incremental de generacion Bloque 05 ($/MWh)` <dbl>, `Bloque de Potencia 06
#   (MW)` <dbl>, `Costo Incremental de generacion Bloque 06 ($/MWh)` <dbl>, `Bloque de Potencia 07 (MW)` <dbl>, `Costo Incremental de generacion
#   Bloque 07 ($/MWh)` <dbl>, `Bloque de Potencia 08 (MW)` <dbl>, `Costo Incremental de generacion Bloque 08 ($/MWh)` <dbl>, `Bloque de Potencia
#   09 (MW)` <dbl>, `Costo Incremental de generacion Bloque 09 ($/MWh)` <dbl>, `Bloque de Potencia 10 (MW)` <dbl>, `Costo Incremental de
#   generacion Bloque 10 ($/MWh)` <dbl>, `Bloque de Potencia 11 (MW)` <dbl>, `Costo Incremental de generacion Bloque 11 ($/MWh)` <dbl>, `Reserva
#   rodante 10 min (MW)` <dbl>, `Costo Reserva rodante 10 min ($/MW)` <dbl>, `Reserva no rodante 10 min (MW)` <dbl>, `Costo Reserva no rodante 10
#   min ($/MW)` <dbl>, `Reserva rodante suplementaria (MW)` <dbl>, `Costo Reserva rodante suplementaria ($/MW)` <dbl>, `Reserva no rodante
#   suplementaria (MW)` <dbl>, `Costo Reserva no rodante suplementaria ($/MW)` <dbl>, `Reserva regulacion secundaria (MW)` <dbl>, `Costo Reserva
#   regulacion secundaria ($/MW` <dbl>
...