通过'在Python中解析带有名称空间的XML;元素树&x27;

通过'在Python中解析带有名称空间的XML;元素树&x27;,python,xml,xml-parsing,xml-namespaces,elementtree,Python,Xml,Xml Parsing,Xml Namespaces,Elementtree,我想使用Python的ElementTree解析以下XML: 篮球联赛 互相竞争的一组运动队 打篮球 我想找到所有owl:Class标记,然后提取其中所有rdfs:label实例的值。我正在使用以下代码: tree=ET.parse(“文件名”) root=tree.getroot() root.findall('owl:Class') 由于名称空间的原因,我得到以下错误 SyntaxError:在前缀映射中找不到前缀“owl” 我尝试在上阅读该文档,但由于上面的XML具有多个嵌套名称空

我想使用Python的
ElementTree
解析以下XML:


篮球联赛
互相竞争的一组运动队
打篮球
我想找到所有
owl:Class
标记,然后提取其中所有
rdfs:label
实例的值。我正在使用以下代码:

tree=ET.parse(“文件名”)
root=tree.getroot()
root.findall('owl:Class')
由于名称空间的原因,我得到以下错误

SyntaxError:在前缀映射中找不到前缀“owl”
我尝试在上阅读该文档,但由于上面的XML具有多个嵌套名称空间,因此仍然无法使其正常工作


请告诉我如何更改代码以查找所有的
owl:Class
标记。

ElementTree在名称空间方面不太聪明。您需要为
.find()
findall()
iterfind()
方法提供一个显式的命名空间字典。这一点没有很好的记录:

namespaces = {'owl': 'http://www.w3.org/2002/07/owl#'} # add more as needed

root.findall('owl:Class', namespaces)
前缀只能在传入的
名称空间
参数中查找。这意味着您可以使用任何您喜欢的名称空间前缀;API分离了
owl:
部分,在
namespaces
字典中查找相应的名称空间URL,然后更改搜索以查找XPath表达式
{http://www.w3.org/2002/07/owl}类
。当然,您也可以自己使用相同的语法:

root.findall('{http://www.w3.org/2002/07/owl#}Class')

如果你能换到更好的东西;该库支持相同的ElementTree API,但在元素的
.nsmap
属性中为您收集名称空间。

下面介绍如何使用lxml实现这一点,而无需硬编码名称空间或扫描文本(如Martijn Pieters所述):

更新

5年后,我仍然遇到这个问题的变化。正如我在上面所展示的,lxml很有帮助,但并非在所有情况下都是如此。当涉及到合并文档时,评论者可能对这种技术有一个正确的观点,但我认为大多数人在简单地搜索文档时都有困难

下面是另一个案例以及我是如何处理的:

<?xml version="1.0" ?><Tag1 xmlns="http://www.mynamespace.com/prefix">
<Tag2>content</Tag2></Tag1>
注意:这对于Python的ElementTree标准库来说非常有用,而不需要使用硬编码的名称空间

要从XML数据中提取命名空间的前缀和URI,可以使用
ElementTree.iterparse
函数,仅解析命名空间开始事件(start ns):


我一直在使用类似的代码,并发现它总是值得阅读的文档。。。像往常一样

findall()将只查找当前标记的直接子元素。所以,不是全部

让您的代码处理以下内容可能是值得的,特别是当您处理大型复杂的xml文件时,这样子元素(等等)也包括在内。 如果您知道元素在xml中的位置,那么我想这很好!只是觉得这值得记住

root.iter()
参考:
Element.findall()只查找带有标记的元素,这些标记是当前元素的直接子元素。Element.find()查找带有特定标记的第一个子元素,Element.text访问元素的文本内容。Element.get()访问元素的属性:“

以名称空间格式获取名称空间,例如
{myNameSpace}”
,您可以执行以下操作:

from io import StringIO
from xml.etree import ElementTree
def get_namespaces(xml_string):
    namespaces = dict([
            node for _, node in ElementTree.iterparse(
                StringIO(xml_string), events=['start-ns']
            )
    ])
    namespaces["ns0"] = namespaces[""]
    return namespaces
root=tree.getroot()
ns=re.match(r'{.*}',root.tag).group(0)
这样,您可以在以后的代码中使用它来查找节点,例如使用字符串插值(Python3)

link=root.find(f“{ns}link”)

我的解决方案基于@Martijn Pieters的评论:

register\u命名空间
只影响序列化,而不影响搜索

所以这里的技巧是使用不同的字典进行序列化和搜索

namespaces = {
    '': 'http://www.example.com/default-schema',
    'spec': 'http://www.example.com/specialized-schema',
}
现在,注册所有名称空间以进行解析和写入:

for name, value in namespaces.iteritems():
    ET.register_namespace(name, value)
对于搜索(
find()
findall()
iterfind()
),我们需要一个非空前缀。向这些函数传递一个修改过的字典(这里我修改了原始字典,但这必须在注册名称空间之后进行)

现在,来自
find()
系列的函数可以与
default
前缀一起使用:

print root.find('default:myelem', namespaces)
但是


默认名称空间中的元素不使用任何前缀。

这基本上是Davide Brunato的答案,但我发现他的答案存在严重问题,默认名称空间是空字符串,至少在我的python 3.6安装中是如此。我从他的代码中提炼出的、对我有用的函数如下:

from io import StringIO
from xml.etree import ElementTree
def get_namespaces(xml_string):
    namespaces = dict([
            node for _, node in ElementTree.iterparse(
                StringIO(xml_string), events=['start-ns']
            )
    ])
    namespaces["ns0"] = namespaces[""]
    return namespaces
其中,
ns0
只是空名称空间的占位符,您可以用任意随机字符串替换它

如果我这样做:

my_namespaces = get_namespaces(my_schema)
root.findall('ns0:SomeTagWithDefaultNamespace', my_namespaces)

它还使用默认名称空间为标记生成正确答案。

谢谢。你知道如何直接从XML中获取名称空间,而不用硬编码吗?或者我怎么能忽视它?我尝试了findall({*}Class'),但在我的例子中它不起作用;正如答案中所述,
lxml
为您完成了这项工作,
xml.etree.ElementTree
模块没有。但是,如果您试图匹配一个特定的(已经硬编码的)元素,那么您也在尝试匹配一个特定名称空间中的特定元素。该名称空间在文档之间的更改不会超过元素名称的更改。您也可以使用元素名对其进行硬编码。@Jon:
register\u namespace
只影响序列化,而不影响搜索。可能有用的小添加:当使用
cElementTree
而不是
ElementTree
时,
findall
不会将名称空间作为关键字参数,而只是作为普通参数,即
self.namespaces['default'] = self.namespaces['']
print root.find('default:myelem', namespaces)
tree.write(destination)
from io import StringIO
from xml.etree import ElementTree
def get_namespaces(xml_string):
    namespaces = dict([
            node for _, node in ElementTree.iterparse(
                StringIO(xml_string), events=['start-ns']
            )
    ])
    namespaces["ns0"] = namespaces[""]
    return namespaces
my_namespaces = get_namespaces(my_schema)
root.findall('ns0:SomeTagWithDefaultNamespace', my_namespaces)