【问题标题】:Iterating rvest scrape function gives: "Error in open.connection(x, "rb") : Timeout was reached"迭代 rvest scrape 函数给出:“open.connection(x, “rb”) 中的错误:已达到超时”
【发布时间】:2016-12-27 14:55:33
【问题描述】:

我正在使用“rvest”包抓取this website。当我多次迭代我的函数时,我得到“open.connection(x,“rb”)中的错误:达到超时”。我已经搜索过类似的问题,但答案似乎会导致死胡同。我怀疑它是服务器端的,并且该网站对我可以访问该页面的次数有内置限制。如何研究这个假设?

代码:我有指向底层网页的链接,并想用从相关网页中提取的信息构建一个数据框。我已经稍微简化了我的抓取功能,因为使用更简单的功能仍然会出现问题:

scrape_test = function(link) {

  slit <-  str_split(link, "/") %>%
    unlist()
  id <- slit[5]
  sem <- slit[6]

  name <- link %>% 
    read_html(encoding = "UTF-8") %>%
    html_nodes("h2") %>%
    html_text() %>%
    str_replace_all("\r\n", "") %>%
    str_trim()

  return(data.frame(id, sem, name))
}

我使用 purrr-package map_df() 来迭代函数:

test.data = links %>%
  map_df(scrape_test)

现在,如果我只使用 50 个链接迭代函数,我不会收到任何错误。但是当我增加链接数量时,我遇到了前面提到的错误。此外,我收到以下警告:

  • “在 bind_rows_(x, .id) 中:不等因子级别:强制转换为字符”
  • “关闭未使用的连接 4 (link)”

编辑:以下创建链接对象的代码可用于重现我的结果:

links <- c(rep("http://karakterstatistik.stads.ku.dk/Histogram/NMAK13032E/Winter-2013/B2", 100))

【问题讨论】:

  • 我忘记了“链接 %>%”,因为我在堆栈溢出中直接简化了我的函数,对此感到抱歉。问题在于多次迭代,当您到达太多链接时它突然给出错误。
  • 也许尝试在您的函数中插入Sys.sleep(x)(插入您选择的x)以在请求之间暂停?
  • 是的,这绝对是一个可能的解决方案。我怎么知道“x”应该是什么?有什么技巧还是反复试验?但是,由于我想迭代 1000 个链接作为开始和可能的 32000 个链接,我可能需要另一个解决方案,因为设置 x=2 需要很长时间才能运行。

标签: r function web-scraping rvest


【解决方案1】:

对于大型抓取任务,我通常会执行一个 for 循环,这有助于排除故障。为您的输出创建一个空列表:

d <- vector("list", length(links))

在这里我做了一个 for 循环,带有一个 tryCatch 块,这样如果输出是错误的,我们等待几秒钟再试一次。我们还包括一个counter,如果在五次尝试后仍然出现错误,它会转到下一个链接。此外,我们还有if (!(links[i] %in% names(d))),因此如果我们必须中断循环,我们可以在重新启动循环时跳过我们已经抓取的链接。

for (i in seq_along(links)) {
  if (!(links[i] %in% names(d))) {
    cat(paste("Doing", links[i], "..."))
    ok <- FALSE
    counter <- 0
    while (ok == FALSE & counter <= 5) {
      counter <- counter + 1
      out <- tryCatch({                  
                  scrape_test(links[i])
                },
                error = function(e) {
                  Sys.sleep(2)
                  e
                }
              )
      if ("error" %in% class(out)) {
        cat(".")
      } else {
        ok <- TRUE
        cat(" Done.")
      }
    }
    cat("\n")
    d[[i]] <- out
    names(d)[i] <- links[i]
  }
} 

【讨论】:

  • 哇,这个功能不错!我刚刚运行了代码,但我遇到了一个连续的错误,所以不得不打破它。我如何确定它在哪里破裂?以及如何从断点开始。我不认为我理解if (!(links[i] %in% names(d)))
  • 因为我们使用了for循环,所以在全局环境中创建了一个对象i,并随着每次迭代而更新。因此,如果我们手动中断循环,我们可以通过简单地从控制台调用i 来检查i 的最后一个值;因此links[i] 将返回循环中断的链接。 if(...) 表示仅在 link[i] 不存在于列表元素的 names 中时才执行封装块中的所有内容;如果我们成功执行scrape_test(),我们只设置names(d)[i] &lt;- links[i]。 IE。跳过我们已经成功执行的所有链接scrape_test()
  • 好的,如果我手动中断它,它会自动从中断的地方继续?太酷了,哇!如何以最简单的方式将创建的向量转换为数据框?
  • 太糟糕了,我不能投票给你回答!这真的真的太棒了!非常感谢
  • do.call(rbind, d)(其中d 是列表的名称)。但这只有在d 中的每个元素都是具有相同列数的数据框时才有效。例如。如果像上面的代码一样,如果scrape_test() 在 5 次尝试后仍然失败,并且列表是数据帧和错误对象的混合,那么如果我们将“错误”对象分配给列表元素,它将不起作用。在这种情况下,您必须在调用 do.call(rbind, ...) 之前通过遍历列表来删除错误对象。
猜你喜欢
  • 2016-01-22
  • 2018-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-28
  • 2023-01-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多