首页 > 解决方案 > 如何从 R 中的文档搜索 Web 界面抓取/自动下载 PDF 文件?

问题描述

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

最近,我学会了如何从我正在使用的网站“webscrape”单个 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”?

谢谢

标签: rweb-scrapingtextnlptidyr

解决方案


我看到有人建议您使用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 可以让您访问让您探索 json 数据结构的 GUI,您会看到有一个“结果”条目,其中包含搜索的 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().

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 访问,请参阅此处的文档。这将是访问他们数据的正确和最安全的方式。


推荐阅读