在Python中解析Google Earth KML文件(lxml,命名空间)

问题描述 投票:0回答:2

我正在尝试使用 xml 模块将 .kml 文件解析为 Python(在我用于 HTML 的 BeautifulSoup 中未能完成此操作之后)。

因为这是我第一次这样做,所以我遵循了官方的教程,一切都很顺利,直到我尝试构造一个迭代器来通过根迭代提取数据:

from lxml import etree
tree=etree.parse('kmlfile')

这是我尝试模拟的教程中的示例

如果您知道您只对单个标签感兴趣,则可以将其名称传递给 getiterator() 以让它为您过滤:

for element in root.getiterator("child"):
    print element.tag, '-', element.text

我想获取“地标”下的所有数据,所以我尝试了

for i in tree.getiterterator("Placemark"):
    print i, type(i)

这没有给我任何东西。起作用的是:

for i in tree.getiterterator("{http://www.opengis.net/kml/2.2}Placemark"):
    print i, type(i)

我不明白这是怎么发生的。 www.opengis.net 列在文档开头的标签中 (kml xmlns="http://www.opengis.net/kml/2.2"...) ,但我不明白

  • {} 中的部分与我的具体示例有何关系

  • 为什么和教程不一样

  • 以及我做错了什么

非常感谢任何帮助!

python xml xpath lxml xml-namespaces
2个回答
10
投票

这是我的解决方案。 因此,最重要的事情就是阅读 Tomalak 发布的this。这是对命名空间的很好的描述,而且很容易理解。

我们将使用 XPath 来导航 XML 文档。它的表示法类似于文件系统,其中父级和子级由斜杠 / 分隔。 here 解释了语法,但请注意,对于 lxml 实现,某些命令是不同的。

###问题

我们的目标是提取城市名称:

<name>
下的
<Placemark>
的内容。这是相关的 XML:

<Placemark> <name>CITY NAME</name> 

与我上面发布的非功能代码等效的 XPath 是:

tree=etree.parse('kml document')
result=tree.xpath('//Placemark/name/text()')

其中需要

text()
部分才能获取位置
//Placemark/name
中包含的文本。

现在这不起作用,正如 Tomalak 指出的那样,因为这两个节点的名称实际上是

{http://www.opengis.net/kml/2.2}Placemark
{http://www.opengis.net/kml/2.2}name
。大括号中的部分是默认命名空间。它没有出现在实际文档中(这让我很困惑),但它是在 XML 文档的开头定义的,如下所示:

xmlns="http://www.opengis.net/kml/2.2"

###解决方案

我们可以通过设置

namespaces
参数来为 xpath 提供命名空间:

xpath(X, namespaces={prefix: namespace})

这对于具有实际前缀的命名空间来说很简单,例如在本文档中

<gx:altitudeMode>relativeToSeaFloor</gx:altitudeMode>
,其中
gx
前缀在文档中定义为
xmlns:gx="http://www.google.com/kml/ext/2.2"

但是,Xpath 不理解默认名称空间是什么(参见 docs)。因此,我们需要欺骗它,就像托马拉克上面建议的那样:我们为默认值发明一个前缀并将其添加到我们的搜索词中。例如,我们可以将其称为 kml。这段代码确实起到了作用:

tree.xpath('//kml:Placemark/kml:name/text()', namespaces={"kml":"http://www.opengis.net/kml/2.2"})

教程提到还有一种ETXPath方法,其工作方式与 Xpath 类似,只不过将名称空间写在大括号中而不是在字典中定义它们。因此,输入的样式为

{http://www.opengis.net/kml/2.2}Placemark


0
投票

我发现帕特里克上面的回答非常有帮助。 只需在此处添加几行短行,即可提取每个地标的坐标:

from lxml import etree as ET
tree = ET.parse(f)
root = tree.getroot()
#need this as elements are tagged with namespaces
namespaces = root.nsmap
namespaces.pop(None, None)
placemarks = tree.xpath('//kml:Placemark', namespaces=namespaces)
for p in placemarks:
    name = p.find('.//kml:name', namespaces).text
    coords = p.find('.//kml:Point/kml:coordinates', namespaces).text
    longitude, latitude, height = coords.split(',')
    print(f"Placemark: {name}, longitute={longitude}, latitude={latitude}")
© www.soinside.com 2019 - 2024. All rights reserved.