来自R的网站的刮痧表

时间:2017-05-04 08:29:44

标签: r web-scraping

我试图使用R从以下链接中提取表格: https://pubchem.ncbi.nlm.nih.gov/compound/1983#section=DrugBank-Interactions&fullscreen=true

我尝试了以下内容:

 url <- "https://pubchem.ncbi.nlm.nih.gov/compound/1983#section=DrugBank-Interactions&fullscreen=true"
require(XML)
url.table <- readHTMLTable(url, which = 1, header = FALSE, stringsAsFactors = FALSE)

我收到以下错误:

     Error in (function (classes, fdef, mtable)  : 
 unable to find an inherited method for function ‘readHTMLTable’ for 
signature ‘"NULL"’
In addition: Warning message:
XML content does not seem to be XML: 
'https://pubchem.ncbi.nlm.nih.gov/compound/1983#section=DrugBank-
Interactions&fullscreen=true'

我不太熟悉网页抓取,有没有办法将表格从上面的链接中提取到R?另外,我如何确定数据的存储格式; XML,JSON等?

感谢。

2 个答案:

答案 0 :(得分:4)

正如其他人所指出的那样,问题是数据是通过Javascript而不是HTML加载的,所以你需要一个能够执行JS的工具来提取信息。 Ian在他的回答中演示了使用RSelenium控制机器上的浏览器来完成任务。在这种情况下,还有另一种不需要RSelenium的方式。

使用Chrome(其他浏览器也可能这样做),您可以打开开发人员工具并查看浏览器的网络活动。虽然这是开放的,但是如果您加载上面的链接,您可以看到所有的网页所涉及的后台活动。这很重要,因为Javascript不只是让数据神奇地出现,而是从某处。此选项卡将让我们查看数据的来源。

当我这样做时,我看到的是: Network Activity

下一步需要一些调查工作 - 我们需要找到加载数据的步骤。通常这将采用JSON格式。在活动列表的大部分时间里,我们看到有两个JSON步骤,一个用于索引,一个用于数据。我们可以右键单击并在新选项卡中打开。 JSON step

This link包含(乍一看)表格中的所有数据。我们现在可以将此链接读入R并提取表格。

library(httr)
library(jsonlite)
library(magrittr)

json = GET("https://pubchem.ncbi.nlm.nih.gov/rest/pug_view/data/compound/1983/JSON/?") %>% 
  content(as='text') %>% 
  fromJSON()

GET是一个http动词,用于从网站检索数据,来自httr包。 contentGET的结果作为文本提取,fromJSON将其转换为R中的列表(并且来自jsonlite包。现在我们有一个大的列表,我们可以导航到查找数据。

json$Record$Section$TOCHeading

 [1] "2D Structure"                           "3D Conformer"                          
 [3] "LCSS"                                   "Names and Identifiers"                 
 [5] "Chemical and Physical Properties"       "Related Records"                       
 [7] "Chemical Vendors"                       "Drug and Medication Information"       
 [9] "Agrochemical Information"               "Pharmacology and Biochemistry"         
[11] "Use and Manufacturing"                  "Identification"                        
[13] "Safety and Hazards"                     "Toxicity"                              
[15] "Literature"                             "Patents"                               
[17] "Biomolecular Interactions and Pathways" "Biological Test Results"               
[19] "Classification"  

您正在寻找的数据是“生物分子相互作用和途径”(第17个元素),它导致另一个数据框架,显示药物库交互是第三行。

json$Record$Section$Section[[17]]$TOCHeading
[1] "Protein Bound 3-D Structures" "Biosystems and Pathways"      "DrugBank Interactions" 

这给出了一个包含一列的data.frame,每行都是一个包含数据帧的长度为1的列表。

dbi = json$Record$Section$Section[[17]]$Information[[3]]$Table

我们可以编写一个函数并使用一些lapply来提取表格。

extractValues = function(i,d){
  sv = dbi[d,][[1]][i,][[1]]$StringValue
  out = data.frame(Key = sv[1],Value = sv[2],stringsAsFactors = F)
  return(out)
}

dbi_Tables = lapply(1:nrow(dbi),function(d){
  out = lapply(1:nrow(dbi[d,][[1]]),extractValues,d=d) %>% 
    do.call(rbind,.)
  return(out)
})

现在你有一个键/值表列表。

不可否认,这比一个很好的rvest调用要多得多,而且这个JSON是 super 凌乱,但作为处理JS加载数据的策略,它可以快得多并且不如RSelenium脆弱。

答案 1 :(得分:3)

这是一个解决javascript问题的RSelenium方法:

library(RSelenium)
library(rvest)

#this sets up the phantomjs driver
pjs <- wdman::phantomjs()

#open a connection to it
dr <- rsDriver(browser = 'phantomjs')
remdr <- dr[['client']]

#go to the site
remdr$navigate("https://pubchem.ncbi.nlm.nih.gov/compound/1983#section=DrugBank-Interactions&fullscreen=true")

#get tables
tables <- remdr$findElements('class', 'table-container')

tableList <- list()
for(i in 1:length(tables)){
  x <- tables[[i]]$getElementAttribute('innerHTML') %>%
    unlist() %>%
    read_html() %>%
    html_table()

  tableList[[i]] <- x[[1]]
}

请注意,输出是一个数据帧列表 - 这是必要的,因为它们可以有不同的长度。

如果您遇到错误,可能需要在代码中加入一些暂停,以说明RSelenium可能会不堪重负。我不能想到围绕这个问题的另一个好方法。