需求
手上有个项目需要解析xml数据,参考写爬虫的经验,容易想到使用XPath语句进行节点导航。
XPath 是一门在 XML 文档中查找信息的语言,用于在 XML 文档中通过元素和属性进行导航。
XPath 使用路径表达式来选取 XML 文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。
查阅
网上搜了一下,大多推荐使用lxml
模块而非自带的xml
进行查询,三方库确实强大,但lxml
需要单独安装,通用性不足,希望尽量用系统自带的xml
实现。
文档指出xml.etree.ElementTree
可以通过支持的有限的XPath表达式来定位元素,支持语法如下:
语法 | 说明 |
---|---|
tag | 查找所有具有指定名称tag的子元素。例如:country表示所有名为country的元素,country/rank表示所有名为country的元素下名为rank的元素。 |
* | 查找所有元素。如:*/rank表示所有名为rank的孙子元素。 |
. | 选择当前元素。在xpath表达式开头使用,表示相对路径。 |
// | 选择当前元素下所有级别的所有子元素。xpath不能以“//”开头。 |
.. | 选择父元素。如果视图达到起始元素的祖先,则返回None(或空列表)。起始元素为调用find(或findall)的元素。 |
[@attrib] | 选择具有指定属性attrib的所有子元素。 |
[@attrib='value'] | 选择指定属性attrib具有指定值value的元素,该值不能包含引号。 |
[tag] | 选择所有具有名为tag的子元素的元素。 |
[.='text'] | Python3.7+,选择元素(或其子元素)完整文本内容为指定的值text的元素。 |
[tag='text'] | 选择元素(或其子元素)名为tag,完整文本内容为指定的值text的元素。 |
[position] | 选择位于给定位置的所有元素,position可以是以1为起始的整数、表达式last()或相对于最后一个位置的位置(如:last()-1) |
上述完全能够涵盖我的查询工作,决定实现。
实现
代码如下
import xml.etree.cElementTree as ET
tree = ET.ElementTree(file="./reference.kml")
marks = tree.findall(".//Placemark")
注意这里的XPath不支持直接用//
而要写作.//
上述代码没有报错,但返回list始终为[]
无法匹配节点
思考
进入debug console查看tree
变量显示存在根节点,也可以用tree[0][1][2]
进行导航,我???
查看tag名称显示为{http://opengis.net/kml/2.2}XXX
比较奇怪
此时xml文件开头的<kml xmlns="http://opengis.net/kml/2.2">
引起了我的注意
这不就是tag里多出来的字段?
进一步查阅,xmlns全称为xml namespace类似C++的命名空间,用于解决局部名称重复冲突。
进一步的,存在xmlns则需要在XPath查询时指定namespace字典进行导航。
解决
添加namespaces属性即可
import xml.etree.cElementTree as ET
NS = {"i":"http://opengis.net/kml/2.2"}
tree = ET.ElementTree(file="./reference.kml")
marks = tree.findall(".//i:Placemark",namespaces=NS)
查询正常,能够返回匹配的list结果。