从R中的XML对象列表中错误地提取重复值

时间:2018-05-08 17:27:34

标签: r xml-parsing lapply

我在使用lapply包中的xml_find_firstxml2来从xml对象列表中提取节点时出现问题。我从Scopus API中提取了几千条记录。由于我一次只能获得25条记录,因此我运行它以便获得包含100个元素的列表,每个元素包含25条记录。我知道有些记录缺少值,所以我的目标是随机播放,直到我得到一个列表,每个记录都是它自己的元素,然后使用lapplyxml_find_first以便我和#39 ; ll在适当的地方获取空值。问题是我最终会拉出重复的值,就好像所有内容仍然嵌套在它们的初始列表中一样。

这是一个可重复的示例,其中包含2个元素的列表,每个元素包含2个记录,最后一个缺少citedby-count

```{r}
library(xml2)

# Simulate how data come in from Scopus
# Build 2 list elements, 2 entries each
el1 <- read_xml(
"<feed>
  <blah>Bunch of stuff I don't need</blah>
  <blah>Bunch of other stuff I don't need</blah>
  <entry>
    <eid>2-s2.0-1542382496</eid>
    <citedby-count>9385</citedby-count>
  </entry>
  <entry>
    <eid>2-s2.0-0032721879</eid>
    <citedby-count>4040</citedby-count>
  </entry>
</feed>"
)
el2 <- read_xml( # This one's missing citedby-count for last entry
"<feed>
  <blah>Bunch of stuff I don't need</blah>
  <blah>Bunch of other stuff I don't need</blah>
  <entry>
    <eid>2-s2.0-0041751098</eid>
    <citedby-count>3793</citedby-count>
  </entry>
  <entry>
    <eid>2-s2.0-73449149291</eid>
  </entry>
</feed>"
)
# Combine into list
lst <- list(el1,el2)
# Check
lst
```

这给了我:

enter image description here

我的目标是提取条目,使它们成为列表项。这样,xml_find_first应该为缺少citedby-count的条目添加空值。

```{r}
# Pull entry nodes
lst2 <- lapply(lst, xml_find_all, "//entry")
# Unlist
lst2 <- unlist(lst2, recursive=FALSE)
# Check - each entry is its own element
lst2
```

enter image description here

挂断是指当我尝试提取一些我知道在某些条目中缺少的节点时,会遗漏一个空缺的节点。 xml_find_first应该这样做。但...

```{r}
cbc <- lapply(lst2, xml_find_first, "//citedby-count")
cbc <- lapply(cbc, xml_text)
cbc # Repeats the first values of original nesting
```

enter image description here

所以我检查了xml_find_all会发生什么:

```{r}
cbc2 <- lapply(lst2, xml_find_all, "//citedby-count")
cbc2 <- lapply(cbc2, xml_text)
cbc2 # Elements contain all values from initial nesting
```

enter image description here

与上面lst2的输出相比,没有任何意义。出于某种原因,拉动文本会保留初始嵌套中的值,即使在查看最终的xml对象列表时它也不会显示。我很难过。

1 个答案:

答案 0 :(得分:1)

事实上,正如@ Dave2e评论的那样,不要简单地使用&#34;随时随地对于子元素,使用//进行XPath搜索(特别是后代或自我搜索),因为搜索将在整个文档上运行。

如果我没有明确地调用原始文档,这怎么可能?如果您在任何 xml_find 列表上运行str(),您将看到该对象带有Rcpp指向当前节点的节点的外部指针文档可根据需要进行调用。实际上,我相信在调用列表时会显示 node 指针。

str(ls2)    
# List of 4
#  $ :List of 2
#   ..$ node:<externalptr> 
#   ..$ doc :<externalptr> 
#   ..- attr(*, "class")= chr "xml_node"
#  $ :List of 2
#   ..$ node:<externalptr> 
#   ..$ doc :<externalptr> 
#   ..- attr(*, "class")= chr "xml_node"
#  $ :List of 2
#   ..$ node:<externalptr> 
#   ..$ doc :<externalptr> 
#   ..- attr(*, "class")= chr "xml_node"
#  $ :List of 2
#   ..$ node:<externalptr> 
#   ..$ doc :<externalptr> 
#   ..- attr(*, "class")= chr "xml_node"

lst2[[1]]$doc
# <pointer: 0x000000000ca7ff90>

typeof(lst2[[1]]$doc)
# [1] "externalptr"

因此,搜索时请注意上下文。您可以使用点前缀(如@ Dave2e建议),.//或根本没有斜杠来检索子元素,这些元素在这里是等效的。

cbc2 <- lapply(lst2, xml_find_all, "citedby-count")
cbc2 <- lapply(cbc2, xml_text)
cbc2

# [[1]]
# [1] "9385"

# [[2]]
# [1] "4040"

# [[3]]
# [1] "3793"

# [[4]]
# character(0)


cbc2 <- lapply(lst2, xml_find_all, ".//citedby-count")
cbc2 <- lapply(cbc2, xml_text)
cbc2
# [[1]]
# [1] "9385"

# [[2]]
# [1] "4040"

# [[3]]
# [1] "3793"

# [[4]]
# character(0)

请注意.//将搜索从当前节点开始的所有后代(即子孙,孙子等)。见What is the difference between .// and //* in XPath?