【问题标题】:How do I scrape / automatically download PDF files from a document search web interface in R?如何从 R 中的文档搜索 Web 界面抓取/自动下载 PDF 文件?
【发布时间】:2021-07-03 20:58:15
【问题描述】:

我正在使用 R 编程语言进行 NLP(自然语言处理)分析 - 为此,我需要“网络抓取”互联网上的公开信息。

最近,我学会了如何从我正在使用的网站“网络抓取”单个 pdf 文件:

library(pdftools)
library(tidytext)
library(textrank)
library(dplyr)
library(tibble)

#this is an example of a single pdf
url <- "https://www.canlii.org/en/ns/nswcat/doc/2013/2013canlii47876/2013canlii47876.pdf"

article <- pdf_text(url)
article_sentences <- tibble(text = article) %>%
  unnest_tokens(sentence, text, token = "sentences") %>%
  mutate(sentence_id = row_number()) %>%
  select(sentence_id, sentence)


article_words <- article_sentences %>%
  unnest_tokens(word, sentence)


article_words <- article_words %>%
  anti_join(stop_words, by = "word")

#this final command can take some time to run
article_summary <- textrank_sentences(data = article_sentences, terminology = article_words)

#Sources: https://stackoverflow.com/questions/66979242/r-error-in-textrank-sentencesdata-article-sentences-terminology-article-w  ,  https://www.hvitfeldt.me/blog/tidy-text-summarization-using-textrank/

如果您想手动访问单个网站,然后“webscrape”这个网站,上面的代码可以正常工作。现在,我想尝试同时自动下载 10 篇这样的文章,而无需手动访问每个页面。例如,假设我想从这个网站下载前 10 个 pdf:https://www.canlii.org/en/#search/type=decision&text=dog%20toronto

我想我找到了以下网站,该网站讨论了如何做类似的事情(我为我的示例修改了代码):https://towardsdatascience.com/scraping-downloading-and-storing-pdfs-in-r-367a0a6d9199

library(tidyverse)
library(rvest)
library(stringr)

page <- read_html("https://www.canlii.org/en/#search/type=decision&text=dog%20toronto ")

raw_list <- page %>% 
    html_nodes("a") %>%  
    html_attr("href") %>% 
    str_subset("\\.pdf") %>% 
    str_c("https://www.canlii.org/en/#search/type=decision&text=dog", .) 
    map(read_html) %>% 
    map(html_node, "#raw-url") %>% 
    map(html_attr, "href") %>% 
    str_c("https://www.canlii.org/en/#search/type=decision&text=dog", .) %>% 
    walk2(., basename(.), download.file, mode = "wb") 

但这会产生以下错误:

Error in .f(.x[[1L]], .y[[1L]], ...) : scheme not supported in URL 'NA'

谁能告诉我我做错了什么?是否可以下载本网站上出现的前 10 个 pdf 文件并将它们单独保存在 R 中,分别为“pdf1”、“pdf2”、...“pdf9”、“pdf10”?

谢谢

【问题讨论】:

  • 如果我运行到最后一个walk2 命令,您的代码只返回NA。您不能将 NA 传递给 download.file(),因为那不是有效的 URL。您尝试使用的页面通过 javascript 生成其所有数据。您不能将 rvest 用于需要 javascript 的页面。你需要使用类似Rseleniun
  • 感谢您的回复!我对 Rseleniun 知之甚少 - 我现在就开始阅读!
  • 这个包可以做吗? github.com/salimk/Rcrawler

标签: r web-scraping text nlp tidyr


【解决方案1】:

我看到有人建议你使用rselenium,这是一种 模拟浏览器操作,以便 Web 服务器将页面呈现为 如果有人访问该站点。根据我的经验,几乎从来没有 有必要沿着那条路线走。该网站的javascript部分是 与 API 交互,我们可以利用它来规避 Javascript 部分并直接获取原始 json 数据。在 Firefox 中(和 Chrome 在这方面我很相似 假设)您可以右键单击网站并选择“检查元素(Q)”, 转到“网络”选项卡,然后单击重新加载。你会看到每个请求 几秒钟或更短的时间后,将列出浏览器对网络服务器的影响。 我们对具有“类型”json 的那些感兴趣。 当您右键单击一个条目时,您可以选择“在新选项卡中打开”。中的一个 返回 json 的请求附有以下 URL https://www.canlii.org/en/search/ajaxSearch.do?type=decision&text=dogs%20toronto&page=1 在 Firefox 中打开该 URL 会进入一个 GUI,让您可以探索 json 数据结构,你会看到有一个“结果”条目 包含您搜索的前 25 个结果的数据。每一个都有一个 “路径”条目,指向将显示嵌入 PDF 的页面。 事实证明,如果您将“.html”部分替换为“.pdf”,则该路径 直接导致PDF文件。下面的代码利用了所有这些信息。

library(tidyverse) # tidyverse for the pipe and for `purrr::map*()` functions.
library(httr) # this should already be installed on your machine as `rvest` builds on it
library(pdftools)
#> Using poppler version 20.09.0
library(tidytext)
library(textrank)

base_url <- "https://www.canlii.org"

json_url_search_p1 <-
  "https://www.canlii.org/en/search/ajaxSearch.do?type=decision&text=dogs%20toronto&page=1"

这会下载第 1 页/结果 1 到 25 的 json

results_p1 <-
  GET(json_url_search_p1, encode = "json") %>%
  content()

对于每个结果,我们只提取路径。

result_html_paths_p1 <-
  map_chr(results_p1$results,
          ~ .$path)

我们将“.html”替换为“.pdf”,将基本 URL 与路径结合起来 生成指向 PDF 的完整 URL。最后我们通过管道将其输入purrr::map()pdftools::pdf_text 以便从所有 25 个 PDF 中提取文本。

pdf_texts_p1 <-
  gsub(".html$", ".pdf", result_html_paths_p1) %>%
  paste0(base_url, .) %>%
  map(pdf_text)

如果您想要的不仅仅是第一页,您可能想要 将上面的代码包装在一个函数中,让你切换出“&page=” 范围。您还可以将“&text=”参数作为 功能以便自动抓取其他搜索的结果。

对于任务的其余部分,我们可以在您已有的代码上进行构建。 我们使它成为可以应用于任何文章的功能并应用该功能 使用purrr::map()再次对每个PDF文本。

extract_article_summary <-
  function(article) {
    article_sentences <- tibble(text = article) %>%
      unnest_tokens(sentence, text, token = "sentences") %>%
      mutate(sentence_id = row_number()) %>%
      select(sentence_id, sentence)
    
    
    article_words <- article_sentences %>%
      unnest_tokens(word, sentence)
    
    
    article_words <- article_words %>%
      anti_join(stop_words, by = "word")
    
    textrank_sentences(data = article_sentences, terminology = article_words)
  }

现在这将需要很长时间!

article_summaries_p1 <- 
  map(pdf_texts_p1, extract_article_summary)

或者,您可以使用furrr::future_map() 来利用所有 CPU 机器中的核心并加快进程。

library(furrr) # make sure the package is installed first
plan(multisession)
article_summaries_p1 <- 
  future_map(pdf_texts_p1, extract_article_summary)

免责声明

以上答案中的代码仅用于教育目的。与许多网站一样,此服务限制对其内容的自动访问。 robots.txt 明确禁止机器人访问 /search 路径。因此,建议在下载大量数据之前与网站所有者联系。 canlii 根据个人请求提供 API 访问,请参阅documentation here。这将是访问他们数据的正确和最安全的方式。

【讨论】:

  • 非常感谢您的回答!我现在刚刚启动了代码,等待看看会发生什么(代码仍在运行)!似乎“pdf_texts_p1”包含包含前 25 篇文章的文本。有没有办法将每篇文章中的文本隔离到一个单独的 R 对象中?例如。 “p1”包含第一篇文章的所有文本,“p2”包含第二篇文章的所有文本?
  • 您可以使用list2env() 来完成此操作,但您必须先使用names(pdf_texts_p1) &lt;- paste0("pdf", 1:25) 之类的名称命名列表,然后再使用list2env(pdf_texts_p1, envir = .GlobalEnv)。我不建议这样做。在大多数情况下,最好将数据保存在一个列表中,并使用 purrr::map()base::lapply() 之类的内容对其进行迭代。
  • 当然,一个循环可以解决这个问题。或者,也可以使用purrr::map()。将代码包装在一个函数(可能命名为 download_results_page())中后,该函数将页面作为参数,您只需执行 map(1:20, download_results_page) 即可从第 1 页到第 20 页的所有 PDF 中获取文本。
  • 我认为你是对的,我只是假设这是可以抓取的公共数据。在网页抓取之前与网站所有者联系可能会更好
  • 我认为问题不需要删除,但是如果您想访问的不仅仅是首页,您应该先与他们联系。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-28
  • 2015-04-18
  • 1970-01-01
  • 1970-01-01
  • 2017-03-26
相关资源
最近更新 更多