【问题标题】:creating a loop for "load" and "save" processes为“加载”和“保存”进程创建循环
【发布时间】:2021-04-09 19:28:58
【问题描述】:

我有一个 data.frame (dim: 100 x 1) 包含一个 url 链接列表,每个 url 看起来像这样:https:blah-blah-blah.com/item/123/index.do

列表(该列表是一个名为my_list 的data.frame,有100 行和一个名为col 的单列,采用字符格式$ col: chr)看起来像这样:

 1 "https:blah-blah-blah.com/item/123/index.do"
 2" https:blah-blah-blah.com/item/124/index.do"
 3 "https:blah-blah-blah.com/item/125/index.do"

etc.

我正在尝试将这些 url 中的每一个导入 R 并将对象共同保存为与文本挖掘过程兼容的对象。

我知道如何手动成功转换每个网址(在列表中):

library(pdftools)
library(tidytext)
library(textrank)
library(dplyr)
library(tm)

#1st document
url <- "https:blah-blah-blah.com/item/123/index.do"

article <- pdf_text(url)

成功创建此“文章”文件后,我可以对其进行检查:

str(article)

chr [1:13] 

看起来像这样:

[1] "abc ....."

[2] "def ..."

etc etc

[15] "ghi ...:

从这里,我可以成功地将它保存为 RDS 文件:

saveRDS(article, file = "article_1.rds")

有没有办法同时对所有 100 篇文章执行此操作?也许有一个循环?

类似:

for (i in 1:100) {

url_i <- my_list[i,1]

article_i <- pdf_text(url_i)

saveRDS(article_i, file = "article_i.rds")

}

如果编写正确,它会将每篇文章保存为 RDS 文件(例如 article_1.rds、article_2.rds、...article_100.rds)。

是否可以将所有这些文章保存到一个rds 文件中?

【问题讨论】:

    标签: r for-loop pdftools


    【解决方案1】:

    请注意list 不是一个好的对象名称,因为这将 暂时覆盖list() 函数。我认为这通常是好的 根据变量的内容命名变量。也许url_df 会是 好名字。

    library(pdftools)
    #> Using poppler version 20.09.0
    library(tidyverse)
    
    url_df <-
      data.frame(
        url = c(
          "https://www.nimh.nih.gov/health/publications/autism-spectrum-disorder/19-mh-8084-autismspecdisordr_152236.pdf",
          "https://www.nimh.nih.gov/health/publications/my-mental-health-do-i-need-help/20-mh-8134-mymentalhealth-508_161032.pdf"
        )
      )
    

    由于网址已经在data.frame 中,我们可以将文本数据存储在 一个额外的列。这样数据就可以很容易地供以后使用 步骤。

    text_df <- 
      url_df %>% 
      mutate(text = map(url, pdf_text))
    

    我们现在可以存储所有数据,而不是将每个文本保存在单独的文件中 在一个文件中:

    saveRDS(text_df, "text_df.rds")
    

    由于历史原因,for 循环在 R 社区中不是很流行。 base R 具有 *apply() 函数族,它提供了一个函数 迭代的方法。 tidyverse 有 purrr 包和 map*() 改进了 *apply() 函数的函数。

    我建议看看 https://purrr.tidyverse.org/ 了解更多。

    【讨论】:

    • 这些网址中的每一个都以“index.do”结尾 - 这可能是问题吗?
    • 您的一个或多个网址不正确。尝试将 pdf_text 封装在一个函数中,在 pdf_text 之前打印 url,这样你就可以看到哪个失败了
    • @Noob url 是什么并不重要,只要它返回的东西可以被 pdf_text 解析
    • 通过描述内容来选择名称是个好建议。但是在小范围内覆盖现有名称是完全没有问题的,这并没有错。 (特别是在 R 中,您甚至可以继续使用 list 函数,即使您使用该名称作为局部变量!)
    • @Noob 我的评论的重点是,这是不必要的。尤其是my_list 比普通的list更糟糕:这是一个完全没有描述性的名称。前缀my_ 纯粹是视觉上的混乱,因为它没有提供任何有用的信息。
    【解决方案2】:

    您的数据中似乎有某些 url 不是有效的 pdf 文件。您可以将其包装在 tryCatch 中以处理错误。如果您的数据框名为df,其中包含url 列,您可以这样做:

    library(pdftools)
    
    lapply(seq_along(df$url), function(x) {
      tryCatch({
        saveRDS(pdf_text(df$url[x]), file = sprintf('article_%d.rds', x)),
      },error = function(e) {})
    })
    

    【讨论】:

    • 我认为您的代码中有一个小错字:file = sprintf('article_%d.rds', x)), ...我将其更改为(删除了逗号):file = sprintf('article_%d.rds', x))
    • 不幸的是,这段代码仍然没有工作。我收到如下错误: PDF 错误:找不到预告片字典 PDF 错误:找不到预告片字典 PDF 错误:无法读取外部参照表,PDF 错误:可能不是 PDF 文件(仍然继续),警告消息: 1:在 open.connection(con, "rb") : 无法打开 URL,在 for (i in seq_along(specs)) { : 关闭未使用的连接 4
    • 您的代码生成的最终文件如下所示: [[1]] NULL [[2]] NULL [[3]] NULL [[4]] NULL [[5]] NULL 。 .. [[28]] NULL [[29]] NULL [[30]] NULL [[31]] NULL
    • 您需要告诉我们实际的 URL,因为它看起来 不是 实际上是 PDF 文件。
    【解决方案3】:

    假设您有一个名为my_dfdata.frame,其中有一列包含您的pdf 位置URL。正如您的 cmets 所说,似乎某些 URL 会导致 PDF 损坏。在这些情况下,您可以使用tryCatch 报告哪些链接已损坏并手动检查这些链接有什么问题。

    您可以像这样在for 循环中执行此操作:

    my_df <- data.frame(url = c(
      "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf", # working pdf
      "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pfd" # broken pdf
    ))
    
    # make some useful new columns
    my_df$id <- seq_along(my_df$url)
    my_df$status <- NA
    
    for (i in my_df$id) {
      
      my_df$status[i] <- tryCatch({
        
        message("downloading ", i) # put a status message on screen
        article_i <- suppressMessages(pdftools::pdf_text(my_df$url[i]))
        saveRDS(article_i, file = paste0("article_", i, ".rds"))
        "OK"
        
      }, error = function(e) {return("FAILED")}) # return the string FAILED if something goes wrong
      
    }
    my_df$status
    #> [1] "OK"     "FAILED"
    

    我在示例数据中加入了一个断开的链接,目的是展示它的外观。

    或者,您可以使用 apply 系列中的循环。不同之处在于,*apply 不是遍历向量并应用相同的代码直到向量结束,而是采用一个函数,将其应用于列表的每个元素(或可以转换为列表的对象)并返回结果从每次迭代一次。许多人一开始发现 *apply 函数令人困惑,因为通常人们在一行中定义和应用函数。让我们让函数更明确:

    s_download_pdf <- function(link, id) {
      tryCatch({
        message("downloading ", id) # put a status message on screen
        article_i <- suppressMessages(pdftools::pdf_text(link))
        saveRDS(article_i, file = paste0("article_", id, ".rds"))
        "OK"
        
      }, error = function(e) {return("FAILED")})
    }
    

    现在我们有了这个功能,让我们用它来下载所有文件。我正在使用mapply,它一次遍历两个向量,在本例中为idurl 列:

    my_df$status <- mapply(s_download_pdf, link = my_df$url, id = my_df$id)
    my_df$status
    #> [1] "OK"     "FAILED"
    

    我认为您选择哪种方法没有太大区别,因为您的互联网连接而不是R 会限制速度。只是觉得您可能会喜欢这种比较。

    【讨论】:

    • 感谢您的回答!这是否首先需要您创建一个名为“articles”的对象?
    • 你只需要一个向量。我认为您有一个名为listdata.frame(顺便说一句不要使用该名称),其中包含一个名为col 的列,其中包含链接。在这种情况下,要获取向量,只需使用list$col。您可以使用它而不是 article 并保持其余代码不变。
    • 谢谢!我会试试这个!我将其名称更改为“my_list”
    • 我刚刚尝试了您的代码并收到以下错误:PDF 错误:可能不是 PDF 文件(仍然继续) PDF 错误(127):十六进制字符串中的非法字符 PDF 错误:可能'找不到预告片字典 PDF 错误:找不到预告片字典 PDF 错误:无法读取外部参照表 poppler_pdf_text 中的错误(加载文件(pdf),opw,upw):PDF 解析失败。
    • 你知道我做错了什么吗?谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-03
    • 2011-03-05
    相关资源
    最近更新 更多