默认过滤器处于活动状态时下载所有数据

Download all data when default filter is active

BCOGC keeps a database of applications 用于在不列颠哥伦比亚省东北部钻井。默认情况下,一些过滤器处于活动状态,仅突出显示上个月内批准的应用程序,即使应用程序数据库拥有 30K+ 记录:

过滤器停用时:

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

当我使用

library(tidyverse)

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

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

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

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

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

这个用例有几个陷阱:

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

我建议先通读 RSelenium 的小插图,然后再尝试下面的内容以了解它的工作原理。您实际上是在编写人工页面交互代码。

您将需要 Docker 从映射目录开始。有关所有信息,请参阅 download file with Rselenium & docker toolbox,但这是我在 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 是邪恶的)并将我的本地下载目录映射到 [=100= 中 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() 的注释并在调用之间尝试各种 "wait time" 值。有些会很短(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]]))

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

然后,我们点击下拉"menu"(真的是一个button):

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

然后下载"menu"项(真的是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]]))

^^ 也需要延迟,因为下载 "dialog" 需要几秒钟才能出现(至少对我来说是这样)。

现在,找到真正是 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() 的调用实际上等待下载完成,因此您可以删除显示和解码代码并将输出分配给一个变量并将其用作自动 "wait"er.

我在 2 个不同的 macOS 系统上尝试了 3 次,效果很好。 YMMV.

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

或者,https://github.com/richfitz/stevedore 现在在 CRAN 上,因此它是 starting/stopping 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