Как отправить форму входа в пакет REST без аргумента кнопки

Я пытаюсь очистить веб-страницу, требующую аутентификации с помощью html_session() и html_form() из пакета rvest. Я нашел это, например. предоставленный Хэдли Уикхэмом, но я не могу настроить его в своем случае.

united <- html_session("http://www.united.com/")
account <- united %>% follow_link("Account")
login <- account %>%
         html_nodes("form") %>%
         extract2(1) %>%
         html_form() %>%
         set_values(
                `ctl00$ContentInfo$SignIn$onepass$txtField` = "GY797363",
                `ctl00$ContentInfo$SignIn$password$txtPassword` = password)
account <- account %>% 
submit_form(login, "ctl00$ContentInfo$SignInSecure")

В моем случае я не могу найти значения, заданные в форме, поэтому я пытаюсь дать пользователю и передать напрямую:   set_values ​​( "электронная почта", "пароль" )

Я также не знаю, как обращаться к кнопке отправки, поэтому я попробовал:   submit_form (счета, Логин)

Ошибка для функции submit_form:   Ошибка в именах (подчиняется) [[1]]: индекс за пределами

Любая идея о том, как это сделать, оценивается. Спасибо вам

Ответ 1

В настоящее время эта проблема аналогична открытой проблеме № 159 в пакете rvest, которая вызывает проблемы, когда не все поля в форма имеет значение type. Эта покупка может быть исправлена ​​в будущем выпуске.

Однако мы можем обойти проблему, обезглавливая базовую функцию rvest:::submit_request.

Основная проблема - вспомогательная функция is_submit. Первоначально он определялся следующим образом:

is_submit <- function(x) tolower(x$type) %in% c("submit", 
        "image", "button")

Как логично, однако, это не удается в двух сценариях:

  • Элемент type отсутствует.
  • Элемент type NULL.

Оба они происходят в объединенной форме входа. Мы можем решить эту проблему, добавив две проверки внутри функции.

custom.submit_request <- function (form, submit = NULL) 
{
  is_submit <- function(x) {
    if (!exists("type", x) | is.null(x$type)){
      return(F);
    }
    tolower(x$type) %in% c("submit", "image", "button")
  } 
  submits <- Filter(is_submit, form$fields)
  if (length(submits) == 0) {
    stop("Could not find possible submission target.", call. = FALSE)
  }
  if (is.null(submit)) {
    submit <- names(submits)[[1]]
    message("Submitting with '", submit, "'")
  }
  if (!(submit %in% names(submits))) {
    stop("Unknown submission name '", submit, "'.\n", "Possible values: ", 
         paste0(names(submits), collapse = ", "), call. = FALSE)
  }
  other_submits <- setdiff(names(submits), submit)
  method <- form$method
  if (!(method %in% c("POST", "GET"))) {
    warning("Invalid method (", method, "), defaulting to GET", 
            call. = FALSE)
    method <- "GET"
  }
  url <- form$url
  fields <- form$fields
  fields <- Filter(function(x) length(x$value) > 0, fields)
  fields <- fields[setdiff(names(fields), other_submits)]
  values <- pluck(fields, "value")
  names(values) <- names(fields)
  list(method = method, encode = form$enctype, url = url, values = values)
}

Для патча обезьяны нам нужно использовать пакет R.utils (установите через install.packages("R.utils"), если у вас его нет).

library(R.utils)

reassignInPackage("submit_request", "rvest", custom.submit_request)

Оттуда мы можем запросить собственный запрос.

account <- account %>% 
     submit_form(login, "ctl00$ContentInfo$SignInSecure")

И это работает!

(Ну, "работает" является неправильным. Из-за того, что United использует более агрессивные требования к аутентификации, включая известные браузеры, это приводит к 301 Unauthorized. Однако он исправляет ошибку).

Полный воспроизводимый пример включал пару других незначительных изменений кода:

library(magrittr)
library(rvest)

url <- "https://www.united.com/web/en-US/apps/account/account.aspx"
account <- html_session(url)
login <- account %>%
  html_nodes("form") %>%
  extract2(1) %>%
  html_form() %>%
  set_values(
    `ctl00$ContentInfo$SignIn$onepass$txtField` = "USER",
    `ctl00$ContentInfo$SignIn$password$txtPassword` = "PASS")
account <- account %>% 
  submit_form(login, "ctl00$ContentInfo$SignInSecure")