在Python中通过“ElementTree”解析带有名称空间的XML

2024-06-18 13:02:06 发布

您现在位置:Python中文网/ 问答频道 /正文

我有下面的XML要使用Python的ElementTree解析:

<rdf:RDF xml:base="http://dbpedia.org/ontology/"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:owl="http://www.w3.org/2002/07/owl#"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
    xmlns="http://dbpedia.org/ontology/">

    <owl:Class rdf:about="http://dbpedia.org/ontology/BasketballLeague">
        <rdfs:label xml:lang="en">basketball league</rdfs:label>
        <rdfs:comment xml:lang="en">
          a group of sports teams that compete against each other
          in Basketball
        </rdfs:comment>
    </owl:Class>

</rdf:RDF>

我想找到所有的owl:Class标记,然后提取其中所有rdfs:label实例的值。我使用以下代码:

tree = ET.parse("filename")
root = tree.getroot()
root.findall('owl:Class')

由于名称空间的原因,我得到了以下错误。

SyntaxError: prefix 'owl' not found in prefix map

我试图在http://effbot.org/zone/element-namespaces.htm处阅读文档,但由于上面的XML有多个嵌套的名称空间,因此仍然无法使其正常工作。

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


Tags: orghttpwwwrdfxmlowllabelclass
3条回答

下面介绍如何使用lxml实现这一点,而不必硬编码名称空间或扫描它们的文本(正如Martijn Pieters提到的那样):

from lxml import etree
tree = etree.parse("filename")
root = tree.getroot()
root.findall('owl:Class', root.nsmap)

更新

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

还有一个案子,我是怎么处理的:

<?xml version="1.0" ?><Tag1 xmlns="http://www.mynamespace.com/prefix">
<Tag2>content</Tag2></Tag1>

没有前缀的xmlns意味着未固定的标记将获得此默认命名空间。这意味着在搜索Tag2时,需要包含命名空间才能找到它。然而,lxml创建了一个没有作为键的nsmap条目,我找不到搜索它的方法。所以,我创建了一个新的名称空间字典

namespaces = {}
# response uses a default namespace, and tags don't mention it
# create a new ns map using an identifier of our choice
for k,v in root.nsmap.iteritems():
    if not k:
        namespaces['myprefix'] = v
e = root.find('myprefix:Tag2', namespaces)

ElementTree对于名称空间不是很聪明。您需要给.find()findall()iterfind()方法一个显式的名称空间字典。这没有很好的记录:

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

root.findall('owl:Class', namespaces)

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

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

如果您可以切换到^{} library更好;该库支持相同的ElementTree API,但在元素的.nsmap属性中为您收集命名空间。

注意:这是一个对Python的ElementTree标准库非常有用的答案,无需使用硬编码名称空间。

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

>>> from io import StringIO
>>> from xml.etree import ElementTree
>>> my_schema = u'''<rdf:RDF xml:base="http://dbpedia.org/ontology/"
...     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
...     xmlns:owl="http://www.w3.org/2002/07/owl#"
...     xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
...     xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
...     xmlns="http://dbpedia.org/ontology/">
... 
...     <owl:Class rdf:about="http://dbpedia.org/ontology/BasketballLeague">
...         <rdfs:label xml:lang="en">basketball league</rdfs:label>
...         <rdfs:comment xml:lang="en">
...           a group of sports teams that compete against each other
...           in Basketball
...         </rdfs:comment>
...     </owl:Class>
... 
... </rdf:RDF>'''
>>> my_namespaces = dict([
...     node for _, node in ElementTree.iterparse(
...         StringIO(my_schema), events=['start-ns']
...     )
... ])
>>> from pprint import pprint
>>> pprint(my_namespaces)
{'': 'http://dbpedia.org/ontology/',
 'owl': 'http://www.w3.org/2002/07/owl#',
 'rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
 'rdfs': 'http://www.w3.org/2000/01/rdf-schema#',
 'xsd': 'http://www.w3.org/2001/XMLSchema#'}

然后字典可以作为参数传递给搜索函数:

root.findall('owl:Class', my_namespaces)

相关问题 更多 >