R中的递归JSON树索引提取

时间:2015-10-17 09:01:45

标签: json r recursion reddit

我在R中提取JSON树的结构时遇到了困难。

考虑以下场景(从Reddit.com提取数据):

library(RJSON)
URL = "http://www.reddit.com/r/newzealand/comments/3p25qy/where_can_i_get_a_chromecast_2/"

X = paste0(gsub("\\?ref=search_posts$","",URL),".json?limit=500")
raw_data = fromJSON(readLines(X, warn = FALSE))
main.node = raw_data[[2]]$data$children
replies = main.node[[2]]$data$replies
node = replies$data$children

现在main.node[[1]]包含与网址上的第一条评论相对应的属性,而replies包含有关第二条评论的回复的信息。我们可以通过查看replies$data$children找到这些回复。但是回复可以嵌套在另一个回复中,这就是为什么要得到它们,我们需要递归地解析树。

下表列出了评论的结构以及我想要获得的输出:

row  comment | reply_to_comment | reply_to_reply | desired_output
1)   *       |                  |                | 1
2)           | *                |                | 1.1
3)           | *                |                | 1.2
4)           |                  | *              | 1.2.1
5)           |                  | *              | 1.2.2
6)           | *                |                | 1.3
7)   *       |                  |                | 2
8)           | *                |                | 2.1
9)           |                  | *              | 2.1.1

到目前为止,我能得到的最接近的代码如下:

 node = main.node
reply_function = function(node){
  struct   = seq_along(node) 
  replies  = node$data$replies
  rep.node = if (is.list(replies)) replies$data$children else NULL
  return(list(struct,lapply(rep.node,function(x) reply_function(x))))
}
[1]  1 2 1 2 1 2 1 2 1 2 1 2 1 2

请注意,如果重新运行,数字可能会更改 - 此数据是动态的。

然而,这种方法不包含整个线程的历史记录,它只告诉我们某个节点可能有多少回复,无论它是原始注释还是对回复的回复。

如果有人对如何提出任何建议,请告诉我,我很乐意听取您的意见。

非常感谢!

2 个答案:

答案 0 :(得分:1)

我会避免递归,只需使用unlist,例如:

library(jsonlite)
URL = "http://www.reddit.com/r/newzealand/comments/3p25qy/where_can_i_get_a_chromecast_2/"
X = paste0(gsub("\\?ref=search_posts$","",URL),".json?limit=500")
raw_data = fromJSON(readLines(X, warn = FALSE))
data = unlist(raw_data)
comments = names(data)[grepl('\\.body$', names(data))]
comments = data[comments]
names(comments) <- NULL
comments

答案 1 :(得分:1)

以下是使用previously answered rjson reader的修改版本的方法。

首先,我们可以修改以前的递归阅读器,以便记录它所处的级别:

get.comments <- function(node, depth=0) {
  if(is.null(node)) {return(list())}
  comment     <- node$data$body
  replies     <- node$data$replies
  reply.nodes <- if (is.list(replies)) replies$data$children else NULL
  return(list(paste0(comment, " ", depth), lapply(1:length(reply.nodes), function(x) get.comments(reply.nodes[[x]], paste0(depth, ".", x)))))
}

现在阅读您的数据:

library(rjson)
URL = "http://www.reddit.com/r/newzealand/comments/3p25qy/where_can_i_get_a_chromecast_2/"
X = paste0(gsub("\\?ref=search_posts$","",URL),".json?limit=500")
rawdat    <- fromJSON(readLines(X, warn = FALSE))
main.node <- rawdat[[2]]$data$children

然后递归地应用该函数并取消列表:

txt <- unlist(lapply(1:length(main.node), function(x) get.comments(main.node[[x]], x)))

现在,txt是注释的向量,最后是级别。例如

"Holy fuck, thank you! Didn't realise this was actually a thing.\n\nfreeeedom 1.1" 

我们可以通过终端空间进行拆分,并获取data.frame:

z<-as.data.frame(do.call(rbind, strsplit(txt, ' (?=[^ ]+$)', perl = TRUE)))

       V2
1       1
2     1.1
3   1.1.1
4 1.1.1.1
5       2
6       3
7       4
8     4.1
9     4.2