【问题标题】:R : Returning a value from loop when manually stopping itR:手动停止循环时从循环返回一个值
【发布时间】:2020-06-04 02:31:30
【问题描述】:

我正在尝试使用 rvest 构建数据库。由于我有很多数据要下载,我尝试编写几个函数来中断抓取过程并在我离开的地方重新启动它。然而,虽然这些功能或多或少地工作,但每当我手动中断它们时,我都会失去输出。有谁知道一个解决方案可以让我在不丢失循环正在构建的数据框的情况下停止该功能?我很乐意提供任何建议!

我尝试从以下网址抓取数据的一些网址:

to_do <- c("https://jobs.51job.com/shenzhen-nsq/116924235.html?s=01&t=0",
          "https://jobs.51job.com/shenzhen-nsq/116923692.html?s=01&t=0",
          "https://jobs.51job.com/shenzhen-nsq/116923628.html?s=01&t=0",
          "https://jobs.51job.com/shenzhen-nsq/116923578.html?s=01&t=0",
          "https://jobs.51job.com/shenzhen-nsq/116920896.html?s=01&t=0")

我为下载创建的函数:

# In order to initiate the dowload
dl_data_start <- function(to_do){
  output <- tibble()
  i = 1
  while (to_do[i] %in% to_do) {
      page <- read_html(to_do[i])
      position <- page %>%
        html_nodes(.,'h1') %>%
        html_text(.)
      resume <- page %>%
        html_nodes(.,'.ltype') %>%
        html_text(.)
      job_offer <- page %>%
        html_nodes(.,'.job_msg') %>%
        html_text(.)
      eps <- page %>%
        html_nodes(.,'.com_msg') %>%
        html_text(.)
      eps_status <- page %>%
        html_nodes(.,'.at:nth-child(1)') %>%
        html_text(.)
      eps_description <- page %>%
        html_nodes(.,'.tmsg') %>%
        html_text(.)
      employees <- page %>%
        html_nodes(.,'.at:nth-child(2)') %>%
        html_text(.)
      category <- page %>%
        html_nodes(.,'.at:nth-child(3)') %>%
        html_text(.)
      salary <- page %>%
        html_nodes(.,'.cn strong') %>%
        html_text(.)
      url <- to_do[i]
      id <- i
      current <- tibble(position,resume,job_offer,eps,eps_description,eps_status,
                        employees,category,salary,url,id)
      output <- bind_rows(output,current)
      print(output[i,])
      i = i + 1
  }
  return(output)
}
# function in order to continue the download where I left it
dl_data_continue <- function(to_do,df,done){
  i = (match(tail(done,n=1),to_do) + 1)
  while (to_do[i] %in% to_do) {
    page <- read_html(to_do[i])
    position <- page %>%
      html_nodes(.,'h1') %>%
      html_text(.)
    resume <- page %>%
      html_nodes(.,'.ltype') %>%
      html_text(.)
    job_offer <- page %>%
      html_nodes(.,'.job_msg') %>%
      html_text(.)
    eps <- page %>%
      html_nodes(.,'.com_msg') %>%
      html_text(.)
    eps_status <- page %>%
      html_nodes(.,'.at:nth-child(1)') %>%
      html_text(.)
    eps_description <- page %>%
      html_nodes(.,'.tmsg') %>%
      html_text(.)
    employees <- page %>%
      html_nodes(.,'.at:nth-child(2)') %>%
      html_text(.)
    category <- page %>%
      html_nodes(.,'.at:nth-child(3)') %>%
      html_text(.)
    salary <- page %>%
      html_nodes(.,'.cn strong') %>%
      html_text(.)
    url <- to_do[i]
    id <- i
    current <- tibble(position,resume,job_offer,eps,eps_description,eps_status,
                      employees,category,salary,url,id)
    df <- bind_rows(df,current)
    print(df[i,])
    i = i + 1
  }
  return(df)
}

我遇到的问题是,每当我中断循环或发生错误时,我都会丢失所有数据。有人可以解决这个问题吗?我尝试了一些方法,例如安全或 tryCatch,但我无法理解这里出了什么问题。 非常感谢。

编辑: 我还用 tryCatch 做了一些尝试。使用下面的函数,代码在遇到问题(例如 HTTP 404 错误)时不再中断。但是当出现错误时,循环会一直卡在有问题的迭代中,所以我必须用错了。

dl_data_continue_2 <- function(to_do,df,done){
  i = (match(tail(done,n=1),to_do) + 1)
  while (to_do[i] %in% to_do) {
    tryCatch(
      {expr =
        page <- read_html(to_do[i])
      position <- page %>%
        html_nodes(.,'h1') %>%
        html_text(.)
      resume <- page %>%
        html_nodes(.,'.ltype') %>%
        html_text(.)
      job_offer <- page %>%
        html_nodes(.,'.job_msg') %>%
        html_text(.)
      eps <- page %>%
        html_nodes(.,'.com_msg') %>%
        html_text(.)
      eps_status <- page %>%
        html_nodes(.,'.at:nth-child(1)') %>%
        html_text(.)
      eps_description <- page %>%
        html_nodes(.,'.tmsg') %>%
        html_text(.)
      employees <- page %>%
        html_nodes(.,'.at:nth-child(2)') %>%
        html_text(.)
      category <- page %>%
        html_nodes(.,'.at:nth-child(3)') %>%
        html_text(.)
      salary <- page %>%
        html_nodes(.,'.cn strong') %>%
        html_text(.)
      url <- to_do[i]
      id <- i
      current <- tibble(position,resume,job_offer,eps,eps_description,eps_status,
                        employees,category,salary,url,id)
      df <- bind_rows(df,current)
      print(df[i,])
      i = i + 1},
      error = function(e){
        message("* Caught an error on itertion ")
        print(e)
        i = i + 1
      }
    )
  }
  out
}

安全使用,我基本试过了

library(purrr)
dl_safely <- safely(dl_data_continue)

【问题讨论】:

  • 请在purrr::safelytryCatch 中包含您的尝试。
  • 谢谢!我现在已经添加了。
  • 您是否考虑过将元素添加到列表中,并且仅在所有内容完成后也意味着从函数返回列表,然后才在 do.call(rbind, your_list) 而不是在您的函数中执行 df &lt;- bind_rows(df,current),这将通常在原地覆盖对象。注意:这只是初步分析。

标签: r loops return rvest


【解决方案1】:

我在网页抓取中经常遇到这个问题。关键是将中间结果存储在一个环境中,如果您的函数抛出错误,它们可以访问。显而易见的地方是全局环境,但这取决于你如何使用你的函数。如果它是包的一部分,那么您不想写入全局工作区。在这种情况下,您可以将“存储”环境作为包的一部分。

也许最巧妙的方法是在循环完成后删除中间对象,因此只有在循环抛出错误时它才可见/可访问。

这是一个演示原理的函数:

write_data_frames <- function(n)
{
  if(!exists("temporary", .GlobalEnv))
  {
    assign("temporary", list(), envir = globalenv())
    i <- 1
  }
  else
  {
    i <- length(.GlobalEnv$temporary) + 1
  }

  while(i <= n)
  {
    # This is the block where you do your web scraping and store the result
    .GlobalEnv$temporary[[i]] <- data.frame(var1 = rnorm(1), var2 = runif(1))

    # We'll create an error when i == 4
    if(i == 4) stop("Something broke!")
    i <- i + 1
  }
  result <- do.call(rbind, temporary)
  rm("temporary", envir = globalenv())
  return(result)
}

现在,如果我要求它提供 3 行,它应该会返回一个不错的数据框:

write_data_frames(3)
#>         var1      var2
#> 1 -1.6428100 0.1976913
#> 2  0.7136643 0.9684348
#> 3 -0.4845004 0.0294557

它并没有在我们的全局工作区中留下任何东西:

ls()
#> [1] "write_data_frames"

但是假设我要求十行:在这里,它会在第四个循环中抛出错误:

write_data_frames(10)
#> Error in write_data_frames(10) : Something broke!

但是,这一次,我可以使用对象temporary

ls()
#> [1] "temporary"         "write_data_frames"

temporary
#> [[1]]
#>       var1      var2
#> 1 -1.46648 0.1748874
#> 
#> [[2]]
#>          var1      var2
#> 1 -0.03855686 0.5772731
#> 
#> [[3]]
#>        var1      var2
#> 1 0.8228591 0.4115181
#> 
#> [[4]]
#>        var1      var2
#> 1 0.9183934 0.2732575

更好的是,我的函数被设计为从它停止的地方继续,所以如果我再次这样做

write_data_frames(10)
#>           var1      var2
#> 1  -1.46647987 0.1748874
#> 2  -0.03855686 0.5772731
#> 3   0.82285907 0.4115181
#> 4   0.91839339 0.2732575
#> 5   0.54850658 0.9946303
#> 6  -1.39917426 0.9948544
#> 7   0.39525152 0.9234611
#> 8  -1.05899076 0.6226182
#> 9  -2.03137464 0.1218762
#> 10  0.24880216 0.6631982

函数从位置 5 重新启动,没有任何修改。现在,当我们检查全局工作区时,什么都没有了:

ls()
#> [1] "write_data_frames"

【讨论】:

  • 它就像一个魅力!现在我必须做一些阅读才能理解为什么......非常非常感谢!
猜你喜欢
  • 1970-01-01
  • 2015-12-07
  • 2018-12-03
  • 2015-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-15
  • 2023-03-10
相关资源
最近更新 更多