【问题标题】:Using R to download data automatically使用 R 自动下载数据
【发布时间】:2019-11-11 17:05:51
【问题描述】:

我想以 pdf 或 excel 格式下载所有数据 this website 的每个州 X 作物年 X 标准报告组合。

我按照本教程做我想做的事。 Download data from URL

但是,我在第二行遇到了错误。

driver <- rsDriver()

Error in subprocess::spawn_process(tfile, ...) : 
group termination: could not assign process to a job: Access is denied

我可以使用其他方法下载这些数据吗?

【问题讨论】:

标签: r selenium curl web-scraping download


【解决方案1】:

首先,检查网站上的 robots.txt 是否有。然后阅读条款和条件(如果有)。限制下面的请求总是很重要的。

检查所有条款和条件后,下面的代码应该可以帮助您开始:

library(httr)
library(xml2)

link <- "https://aps.dac.gov.in/LUS/Public/Reports.aspx"
r <- GET(link)
doc <- read_html(content(r, "text"))
#write_html(doc, "temp.html")

states <- sapply(xml_find_all(doc, ".//select[@name='DdlState']/option"), function(x)
    setNames(xml_attr(x, "value"), xml_text(x)))
states <- states[!grepl("^Select", names(states))]

years <- sapply(xml_find_all(doc, ".//select[@name='DdlYear']/option"), function(x)
    setNames(xml_attr(x, "value"), xml_text(x)))
years <- years[!grepl("^Select", names(years))]

rptfmt <- sapply(xml_find_all(doc, ".//select[@name='DdlFormat']/option"), function(x)
    setNames(xml_attr(x, "value"), xml_text(x)))

stdrpts <- unlist(lapply(xml_find_all(doc, ".//td/a"), function(x) {
    id <- xml_attr(x, "id")
    if (grepl("^TreeView1t", id)) return(setNames(id, xml_text(x)))
}))

get_vs <- function(doc) sapply(xml_find_all(doc, ".//input[@type='hidden']"), function(x)
    setNames(xml_attr(x, "value"), xml_attr(x, "name")))

fmt <- rptfmt[2] #Excel format
for (sn in names(states)) {
    for (yn in names(years)) {
        for (srn in seq_along(stdrpts)) {
            s <- states[sn]
            y <- years[yn]
            sr <- stdrpts[srn]

            r <- POST(link,
                body=as.list(c("__EVENTTARGET"="DdlState",
                    "__EVENTARGUMENT"="",
                    "__LASTFOCUS"="",
                    "TreeView1_ExpandState"="ennnn",
                    "TreeView1_SelectedNode"="",
                    "TreeView1_PopulateLog"="",
                    get_vs(doc),
                    DdlState=unname(s),
                    DdlYear=0,
                    DdlFormat=1)),
                encode="form")
            doc <- read_html(content(r, "text"))

            treeview <- c("__EVENTTARGET"="TreeView1",
                "__EVENTARGUMENT"=paste0("sStandard Reports\\", srn),
                "__LASTFOCUS"="",
                "TreeView1_ExpandState"="ennnn",
                "TreeView1_SelectedNode"=unname(stdrpts[srn]),
                "TreeView1_PopulateLog"="")
            vs <- get_vs(doc)
            ddl <- c(DdlState=unname(s), DdlYear=unname(y), DdlFormat=unname(fmt))
            r <- POST(link, body=as.list(c(treeview, vs, ddl)), encode="form")
            if (r$headers$`content-type`=="application/vnd.ms-excel")
                writeBin(content(r, "raw"), paste0(sn, "_", yn, "_", names(stdrpts)[srn], ".xls"))

            Sys.sleep(5)
        }
    }
}

【讨论】:

    【解决方案2】:

    这是我最好的尝试:

    如果您查看网络活动,您会看到发送了一个发布请求:

    请求正文数据:

    如果您向下滚动,您将看到所使用的表单数据。

    body <- structure(list(`__EVENTTARGET` = "TreeView1", `__EVENTARGUMENT` = "sStandard+Reports%5C4", 
                           `__LASTFOCUS` = "", TreeView1_ExpandState = "ennnn", TreeView1_SelectedNode = "TreeView1t4", 
                           TreeView1_PopulateLog = "", `__VIEWSTATE` = "", `__VIEWSTATEGENERATOR` = "", 
                           `__VIEWSTATEENCRYPTED` = "", `__EVENTVALIDATION` = "", DdlState = "35", 
                           DdlYear = "2001", DdlFormat = "1"), .Names = c("__EVENTTARGET", 
                                                                          "__EVENTARGUMENT", "__LASTFOCUS", "TreeView1_ExpandState", "TreeView1_SelectedNode", 
                                                                          "TreeView1_PopulateLog", "__VIEWSTATE", "__VIEWSTATEGENERATOR", 
                                                                          "__VIEWSTATEENCRYPTED", "__EVENTVALIDATION", "DdlState", "DdlYear", 
                                                                          "DdlFormat"))
    

    有一些与会话相关的值:

    attr_names <- c("__EVENTVALIDATION", "__VIEWSTATEGENERATOR", "__VIEWSTATE", "__VIEWSTATEENCRYPTED")
    

    你可以像这样添加它们:

    setAttrNames <- function(attr_name){
      name <- doc %>% 
        html_nodes(xpath = glue("//*[@id = '{attr_name}']")) %>% 
        html_attr(name = "value")
      body[[attr_name]] <<- name
    }
    

    然后您可以添加此会话的特定值:

    library(rvest)
    library(glue)
    url <- "https://aps.dac.gov.in/LUS/Public/Reports.aspx"
    
    doc <- url %>% GET %>% content("text") %>% read_html
    sapply(attr_names, setAttrNames)
    

    发送请求:

    然后你就可以发送请求了:

    response <- POST(
      url = url, 
      encode = "form", 
      body = body,
      hdrs
    )
    response$status_code # still indicates that we have an error in the request.
    

    跟进想法:

    1. 我检查了 cookie。有一个会话 cookie,但它似乎不是请求所必需的。

      1. 添加标题。

    尝试设置请求头

    header <- structure(c("aps.dac.gov.in", "keep-alive", "3437", "max-age=0", 
                          "https://aps.dac.gov.in", "1", "application/x-www-form-urlencoded", 
                          "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36", 
                          "?1", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", 
                          "same-origin", "navigate", "https://aps.dac.gov.in/LUS/Public/Reports.aspx", 
                          "gzip, deflate, br", "de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7"), .Names = c("Host", 
                                                                                                  "Connection", "Content-Length", "Cache-Control", "Origin", "Upgrade-Insecure-Requests", 
                                                                                                  "Content-Type", "User-Agent", "Sec-Fetch-User", "Accept", "Sec-Fetch-Site", 
                                                                                                  "Sec-Fetch-Mode", "Referer", "Accept-Encoding", "Accept-Language"
                          ))
    hdrs <- header %>% add_headers
    response <- POST(
      url = url, 
      encode = "form", 
      body = body,
      hdrs
    )
    

    但是我的请求超时了。

    注意:该网站似乎没有 robots.txt。但请查看网站的条款和条件。

    【讨论】:

    • 感谢您的时间和精力。
    【解决方案3】:

    我尝试自己在工作中运行这两行代码,但得到的错误信息比你更明确。

    Could not open chrome browser.
    Client error message:
         Summary: UnknownError
         Detail: An unknown server-side error occurred while processing the command.
         Further Details: run errorDetails method
    Check server log for further details.
    

    这可能是因为如果您在没有管理员权限的情况下工作,R 无法创建子进程。

    事实上,我曾经在尝试使用 RSelenium 构建机器人时遇到了非常可怕的问题。 rsDriver() 完全不一致,一直在崩溃。我必须将它包含在一个带有错误捕获的循环中以使其保持运行,但随后我不得不手动找出并删除千兆字节的临时文件。 我尝试安装 Docker 并花了很多时间进行设置,但最后我的 Windows 非专业版不支持它。

    解决方案:来自 Python 的Selenium 有很好的文档记录,从不崩溃,就像一个魅力。在Anaconda 的交互式 Spyder 编辑器中编码感觉就像 R。

    当然,如果您愿意,您可以使用 R 中的 system("python myscript.py") 之类的东西来启动进程并将生成的文件返回到 R。

    编辑:Anaconda 或 Selenium 根本不需要管理员权限。我自己运行它,工作没有任何问题。如果您像我一样遇到pip install 命令被 SSL 阻止的问题,您可以使用 --trusted-host 参数绕过它。

    【讨论】:

    • 感谢您的评论。在我最初的问题(我已经稍微编辑过)中,我问是否有替代方法可以使用 Selenium 来实现我不需要的东西。
    【解决方案4】:

    当您必须在网页上运行 javascript 时,Selenium 很有用。对于不需要运行 javascript 的网站(即,如果您需要的信息包含在网页 HTML 中),rvesthttr 是您的最佳选择。

    不过,在您的情况下,要下载文件,只需使用 download.file(),它是基本 R 中的一个函数。

    您问题中的网站目前已关闭(所以我看不到它),但这是一个使用来自另一个网站的随机文件的示例

    download.file("https://cran.r-project.org/doc/contrib/Paradis-rdebuts_en.pdf", "mygreatfile.pdf")
    

    检查它是否有效

    dir()
    # [1] "mygreatfile.pdf"
    

    根据网站的结构,您可以获取文件 url 的列表,然后在 R 中循环下载它们。

    最后,一个额外的提示。根据文件类型以及您对它们的处理方式,您可以将它们直接读入 R(而不是先保存它们)。例如,read.csv() 与 url 一起使用可以直接从网络上读取 csv。其他读取函数可能也能做到这一点。

    更新

    我目前在访问该站点时看到一个内部 500 错误,但我可以通过 wayback machine 看到该站点,因此我可以看到网页上确实有 javascript。当网站恢复运行时,我会尝试下载文件

    【讨论】:

    • @89_Simple 12 小时后,站点仍然关闭。你知道数据存储在其他什么地方吗?
    • 我认为该网站又上线了。这是它的直接链接aps.dac.gov.in/LUS/Public/Reports.aspx
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-05
    • 1970-01-01
    • 2017-09-18
    • 1970-01-01
    • 2016-03-23
    • 1970-01-01
    相关资源
    最近更新 更多