如果把XML看作传统的关系数据库,那么XPath就是SQL。R语言中的XML包可用来解析处理XML或是HTML数据。在之前的文章中,我们了解到readHTMLTable函数,如果页面中的数据是一个规整的表格,用它是很方便的,但如果页面中是一些非结构化的数据,就要用到XML包中的其它函数了。其中最重要两个函数是xmlTreeParse()和getNodeSet(),前者负责抓取页面数据并形成树状结构,后者对抓取的数据根据XPath语法来选取特定的节点集合。下面用一个简单的例子来看一下这两个函数配合使用的效果。
这里是一个简单的XML文档,下面是一些简单的节点选取操作:
library(XML)
# 解析XML文件
doc <- xmlParse('http://www.w3school.com.cn/example/xmle/books.xml')
# 选取属于 bookstore 子元素的第一个 book 元素
getNodeSet(doc,'/bookstore/book[1]')
# 选取属于 bookstore 子元素的最后一个 book 元素。
getNodeSet(doc,'/bookstore/book[last()]')
# 选取最前面的两个属于 bookstore 元素的子元素的 book 元素
getNodeSet(doc,'/bookstore/book[position()<3]')
# 选取所有拥有名为 lang 的属性的 title 元素。
getNodeSet(doc,'//title[@lang]')
#选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性
getNodeSet(doc,"//title[@lang='eng']")
# 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
getNodeSet(doc,"/bookstore/book[price>35.00]")
# 选取 bookstore 元素中的 book 元素的所有 title 元素,且 price 元素的值须大于 35.00。
getNodeSet(doc,"/bookstore/book[price>35.00]/title")
# 选取 book 元素的所有 title 和 price 元素
getNodeSet(doc,"//book/title | //book/price")
# 选取文档中的所有 title 和 price 元素
getNodeSet(doc,"//title | //price")
# 选取 bookstore 元素的所有子元素
getNodeSet(doc,"/bookstore/*")
# 选取所有带有属性的 title 元素
getNodeSet(doc,"//title[@*]")
# 选择所有属性lang的值
unlist(getNodeSet(doc,"//title/@lang"),use.names = FALSE)
# title结点下的所有文本
getNodeSet(doc,"//title/text()")
如果你比较习惯操作R,也可以用xmlToList转成list格式再说。如果说XML的结构比较简单,我们可以使用xmlToDataFrame直接将其转为R语言中的data.frame。下面再来看一个操作豆瓣API的例子和其它一些有用的函数。
# 豆瓣的例子
url<-'http://api.douban.com/movie/subject/imdb/tt0111161'
# 解析获得树结构数据
doc <- xmlTreeParse(url,useInternal=TRUE,encoding="UTF-8")
# 获得根结点信息
top <- xmlRoot(doc)
# 获取子节点
top[[5]]
# 子节点名称
xmlName(top[[5]])
# 按名字取子节点
top[['author']]
# 与上一命令相同
xmlChildren(top)[['author']]
# 显示子节点数量
xmlSize(top)
# 获取节点元素
xmlValue(top[['author']])
# 获取节点属性
xmlAttrs(top[[5]])
#获取节点属性值
xmlGetAttr(top[[5]],name='href')
# 向量化操作,获取所有子结点的名字
sapply(xmlChildren(top), xmlName)
# 使用专门的xmlSApply函数,等价于上一条
xmlSApply(top, xmlName)
# 使用xpath语法进行XML查询
# 找出合乎条件的节点,例如找出所有主演的节点集合
(node <- getNodeSet(top,
path="//db:attribute[@name='cast']"))
# 然后取出其中的元素
sapply(node,xmlValue)
# 也可以从根节点一步操作得到结果
(cast <- xpathSApply(top,
path="//db:attribute[@name='cast']",
xmlValue))
# 取出link节点中的属性和属性值
# 定义命名空间,再使用前述函数
namespaces <-c(ns='http://www.w3.org/2005/Atom')
getNodeSet(top,"//ns:link", namespaces)
xpathSApply(top,"//ns:link",
fun=xmlAttrs,
namespaces=namespaces)
xpathApply(top,"//ns:link",
fun=xmlGetAttr,
'href',
namespaces=namespaces)