Как передать несколько значений в форму представления rvest

Это продолжение предыдущей темы. Код работает фантастически для одного значения, но я получаю следующую ошибку при попытке передать более 1 значения, я получаю ошибку, основанную на длине функции. Ошибка в vapply(элементы, кодирование, символ (1)): значения должны быть длиной 1, а результат FUN (X [ 1]) - длиной 3

Вот пример кода. В большинстве случаев мне удавалось просто назвать объект и очистить его.

library(httr)
library(rvest)
library(dplyr)

b<-c('48127','48180','49504')

POST(
 url = "http://www.nearestoutlet.com/cgi-bin/smi/findsmi.pl", 
 body = list(zipcode = b), 
 encode = "form"
) -> res

Мне было интересно, если цикл для вставки значений в форму будет правильным путем? Однако мои навыки написания циклов все еще находятся в стадии разработки, и я не уверен, где их разместить; кроме того, когда я вызываю цикл, он не печатает построчно, он просто возвращает нулевые результаты.

#d isn't listed in the above code as it returns null    
d<-for(i in 1:3){nrow(b)}

2 ответа

Решение

Вот подход для отправки нескольких запросов POST

library(httr)
library(rvest)
b <- c('48127','48180','49504')

Для каждого элемента в b выполните функцию, которая отправит соответствующий запрос POST

res <- lapply(b, function(x){
  res <- POST(
    url = "http://www.nearestoutlet.com/cgi-bin/smi/findsmi.pl", 
    body = list(zipcode = x), 
    encode = "form"
  ) 
  res <- read_html(content(res, as="raw")) 
})

Теперь для каждого элемента списка res Вы должны выполнить шаги синтаксического анализа, описанные в hrbrmstr: Как я могу очистить CGI-корзину с помощью rvest и R?

library(tidyverse)

Я буду использовать код hrbrmstr, так как он король, и он вам уже понятен. Единственное, что мы делаем здесь, это выполняем это на каждом элементе res список.

res_list = lapply(res, function(x){
    rows <- html_nodes(x, "table[width='300'] > tr > td")
    ret <- data_frame(
    record = !is.na(html_attr(rows, "bgcolor")),
    text = html_text(rows, trim=TRUE)
    ) %>% 
    mutate(record = cumsum(record)) %>% 
    filter(text != "") %>% 
    group_by(record) %>% 
    summarise(x = paste0(text, collapse="|")) %>% 
    separate(x, c("store", "address1", "city_state_zip", "phone_and_or_distance"), sep="\\|", extra="merge")
  return(ret)
}
)

или используя map от purrr

res %>%
  map(function(x){
    rows <- html_nodes(x, "table[width='300'] > tr > td")
    data_frame(
      record = !is.na(html_attr(rows, "bgcolor")),
      text = html_text(rows, trim=TRUE)
      ) %>% 
      mutate(record = cumsum(record)) %>% 
      filter(text != "") %>% 
      group_by(record) %>% 
      summarise(x = paste0(text, collapse="|")) %>% 
      separate(x, c("store", "address1", "city_state_zip", "phone_and_or_distance"),
               sep="\\|", extra="merge") -> ret
    return(ret)
  }
  )

Если вы хотите это в кадре данных:

res_df <- data.frame(do.call(rbind, res_list), #rbinds list elements 
                     b = rep(b, times = unlist(lapply(res_list, length)))) #names the rows according to elements in b

Вы можете поместить значения в пост, как показано ниже,

 b<-c('48127','48180','49504')

    for(i in 1:length(b)) {

    POST(
     url = "http://www.nearestoutlet.com/cgi-bin/smi/findsmi.pl", 
     body = list(zipcode =b[i]), 
     encode = "form"
    ) -> res

    # YOUR CODES HERE (for getting content of the page etc.)

    }

Но поскольку для каждого другого значения почтового индекса значение res будет отличаться, вам нужно поместить остальные коды в область, которую я прокомментировал. В противном случае вы получите только последнее значение.

Другие вопросы по тегам