首页 > 解决方案 > 当默认过滤器处于活动状态时下载所有数据

问题描述

BCOGC保留了不列颠哥伦比亚省东北部钻井申请数据库。默认情况下,某些过滤器处于活动状态,仅突出显示上个月内已批准的申请,即使申请数据库拥有 30K+ 记录:

在此处输入图像描述

过滤器停用时:

在此处输入图像描述

要下载整个数据集、删除或停用任何过滤器,请单击操作 > 下载 > CSV。我想使用 R 自动下载整个数据集(包含 30K+ 记录)。

当我使用

library(tidyverse)

df <- read_csv(
  file = 'https://reports.bcogc.ca/ogc/f?p=200:21::CSV::::'
)

它只下载默认查询指定的任何内容,因此大约有 150 条记录,而不是 30K+。

如何使用 R 自动下载整个数据集?这是httr或的任务RSelenium吗?

标签: rseleniumweb-scrapingdownloadrselenium

解决方案


好的,然后我将使用 Selenium,因为它不一定需要 Docker(尽管我使用的示例是使用 Docker :-) 很确定我也可以让 Splash/splashr 来执行此操作,但它涉及文件下载,我认为这和 Splash 后端存在问题。作为splashr作者,如果我在这个例子中也使用 Selenium,我就不必处理 GitHub 问题;-)

无论如何,你应该安装RSelenium. 我真的不能为此提供支持,但它有据可查,而且 rOpenSci 的人非常乐于助人。我强烈建议让 Docker 在你的系统上运行,或者让你的部门设置一个大家都可以使用的 Selenium 服务器。

这个用例有几个陷阱:

  • 我们需要检测的一些元素名称是动态生成的,因此我们必须解决这个问题
  • 这涉及下载 CSV 文件,因此我们需要在 Docker 中映射文件系统路径,以便正确下载
  • 这是一个超级慢的站点,因此您需要计算每次交互后的等待时间(我不打算这样做,因为您可能在较慢或较快的网络上,并且网络速度确实在这里发挥了作用,但不是那么多)

我建议在尝试以下内容之前先了解一下 RSelenium 的小插曲,以了解它的工作原理。您实际上是在编写人工页面交互。

您将需要使用映射目录启动 Docker。有关所有信息,请参阅带有 Rselenium 和 docker 工具箱的下载文件,但这是我在 macOS 机器上的操作方式:

docker run -d -v /Users/hrbrmstr/Downloads://home/seluser/Downloads  -p 4445:4444 selenium/standalone-firefox:2.53.1

这使得 Selenium 可以在端口 4445 上访问,使用 Firefox(b/c Chrome 是邪恶的)并将我的本地下载目录映射到 Docker 容器中 selenium 用户的 Firefox 默认目录。这意味着well_authorizations_issued.csv将去那里(最终)。

现在,我们需要启动 R 并将其连接到这个 Selenium 实例。我们需要创建一个自定义 Firefox 配置文件,因为我们正在将内容保存到磁盘并且我们不希望浏览器提示我们任何内容:

library(RSelenium)

makeFirefoxProfile(
  list(
    browser.download.dir = "home/seluser/Downloads",
    browser.download.folderList = 2L,
    browser.download.manager.showWhenStarting = FALSE, 
    browser.helperApps.neverAsk.saveToDisk =  "text/csv"
  )
) -> ffox_prof

remoteDriver(
  browserName = "firefox", port = 4445L,
  extraCapabilities = ffox_prof
) -> remDr

invisible(remDr$open())
remDr$navigate("https://reports.bcogc.ca/ogc/f?p=AMS_REPORTS:WA_ISSUED")
# Sys.sleep(###)
magick::image_read(openssl::base64_decode(remDr$screenshot()[[1]]))

您将需要取消注释Sys.sleep()s 并在调用之间尝试各种“等待时间”值。有些会很短(1-2 秒),有些会更大(20 秒、30 秒或更高)。

我没有在这里显示屏幕截图的输出,但这是一种计算时间的方法(即在元素交互后继续生成屏幕截图,直到灰色微调框消失 - 等等 - 并记下那是多少秒)。

现在,上面提到的一个棘手的问题是弄清楚复选框在哪里关闭过滤器,因为它有一个动态的id. 但是,我们实际上并不会单击复选框 b/c,创建该应用程序的愚蠢傻瓜不知道他们在做什么,并且实际上将点击事件困在了span它周围的元素中,所以我们必须找到包含复选框标签文本的元素li然后转到该span元素并单击它。

box <- remDr$findElement(using = "xpath", value = "//li[contains(., 'Approval Date is in the last')]/span")
box$clickElement()
# Sys.sleep(###)
magick::image_read(openssl::base64_decode(remDr$screenshot()[[1]]))

^^ 肯定需要延迟(当您单击自己时,您可能会亲眼看到它旋转了一段时间,因此您可以计算它并添加一些缓冲秒数)。

然后,我们点击下拉“菜单”(真的是一个button):

btn1 <- remDr$findElement(using = "css", "button#WA_ISSUED_actions_button")
btn1$clickElement()
# Sys.sleep(###)
magick::image_read(openssl::base64_decode(remDr$screenshot()[[1]]))

然后下载“菜单”项(它真的是button

btn2 <- remDr$findElement(using = "css", "button#WA_ISSUED_actions_menu_14i")
btn2$clickElement()
# Sys.sleep(###)
magick::image_read(openssl::base64_decode(remDr$screenshot()[[1]]))

^^ 还需要 rly 需要延迟,因为下载“对话框”需要几秒钟才能出现(至少对我来说是这样)。

现在,找到真正是a标签的 CSV 框:

lnk <- remDr$findElement(using = "css", "a#WA_ISSUED_download_CSV")
lnk$clickElement()
### WAIT A WHILE
magick::image_read(openssl::base64_decode(remDr$screenshot()[[1]]))

最后一点是你必须尝试的。处理请求需要一段时间,然后传输 ~9MB 文件。调用rmDr$screenshot()实际等待下载完成,因此您可以删除显示和解码代码并将输出分配给变量并将其用作自动“等待”。

我在 2 个不同的 macOS 系统上尝试了这个 3x,它运行良好。YMMV。

我猜你最终会想要自动化这个,所以你可以system()调用脚本的顶部来启动 Selenium Docker 容器,然后执行其余的位,然后发出另一个system()调用来关闭 Docker 容器。

或者,https://github.com/richfitz/stevedore现在在 CRAN 上,因此它是启动/停止 Docker 容器(以及许多其他东西)的纯 R 接口,因此您可以使用它而不是system()调用。

如果你不能使用 Docker,你需要在你的 Windows 机器上为 Firefox 安装一个“webdriver”可执行文件,并获取 Selenium Java 存档,确保你已经安装了 Java,然后执行各种手动咒语来实现它(这是超出了这个答案的范围)。

这是上述内容的缩短的连续版本:

library(RSelenium)

# start Selenium before doing this

makeFirefoxProfile(
  list(
    browser.download.dir = "home/seluser/Downloads",
    browser.download.folderList = 2L,
    browser.download.manager.showWhenStarting = FALSE, 
    browser.helperApps.neverAsk.saveToDisk =  "text/csv"
  )
) -> ffox_prof

remoteDriver(
  browserName = "firefox", port = 4445L,
  extraCapabilities = ffox_prof
) -> remDr

invisible(remDr$open())
remDr$navigate("https://reports.bcogc.ca/ogc/f?p=AMS_REPORTS:WA_ISSUED")
# Sys.sleep(###)

box <- remDr$findElement(using = "xpath", value = "//li[contains(., 'Approval Date is in the last')]/span")
box$clickElement()
# Sys.sleep(###)

btn1 <- remDr$findElement(using = "css", "button#WA_ISSUED_actions_button")
btn1$clickElement()
# Sys.sleep(###)

btn2 <- remDr$findElement(using = "css", "button#WA_ISSUED_actions_menu_14i")
btn2$clickElement()
# Sys.sleep(###)

lnk <- remDr$findElement(using = "css", "a#WA_ISSUED_download_CSV")
lnk$clickElement()
### WAIT A WHILE
done <- remDr$screenshot()

# stop Selenium 

推荐阅读