179

Pythonを使用して解析したい次のXMLがあります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タグを見つけるためにコードを変更する方法を教えてください。

4

7 に答える 7

241

.find()findall()およびiterfind()メソッドに明示的な名前空間ディクショナリを与える必要があります。

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

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

プレフィックスは、渡したパラメーターでのみ検索されnamespacesます。これは、任意の名前空間プレフィックスを使用できることを意味します。APIは部分を分割し、辞書でowl:対応する名前空間URLを検索し、代わりにXPath式を検索するように検索を変更します。もちろん、同じ構文を自分で使用することもできます。namespaces{http://www.w3.org/2002/07/owl}Class

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

ElementTreeドキュメントの「名前空間を使用したXMLの解析」セクションも参照してください。

lxmlライブラリに切り替えることができれば、状況は良くなります。そのライブラリは同じElementTreeAPIをサポートしますが.nsmap、要素の属性で名前空間を収集し、通常は優れた名前空間をサポートします。

于 2013-02-13T12:18:22.470 に答える
62

名前空間をハードコーディングしたり、テキストをスキャンしたりせずに、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はNoneをキーとして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)
于 2014-11-07T18:22:52.877 に答える
39

:これは、ハードコードされた名前空間を使用せずに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)
于 2016-05-24T09:09:56.317 に答える
7

私はこれと同様のコードを使用してきましたが、ドキュメントを読む価値があることがわかりました...いつものように!

findall()は、現在のタグの直接の子である要素のみを検索します。だから、本当にすべてではありません。

特に、サブサブ要素(など)も含まれるように大きくて複雑なxmlファイルを処理している場合は、コードを次のように機能させることを試みる価値があるかもしれません。xmlのどこに要素があるかを知っているなら、それでいいと思います!これは覚えておく価値があると思っただけです。

root.iter()

参照:https : //docs.python.org/3/library/xml.etree.elementtree.html#finding-interesting-elements "Element.findall()は、現在の要素の直接の子であるタグを持つ要素のみを検索します。 Element.find()は特定のタグを持つ最初の子を検索し、Element.textは要素のテキストコンテンツにアクセスします。Element.get()は要素の属性にアクセスします: "

于 2016-08-16T09:51:36.693 に答える
7

名前空間を名前空間形式で取得するには、たとえば{myNameSpace}、次のようにします。

root = tree.getroot()
ns = re.match(r'{.*}', root.tag).group(0)

このようにして、コードの後半でノードを見つけるために使用できます。たとえば、文字列補間(Python 3)を使用します。

link = root.find(f"{ns}link")
于 2018-10-01T12:25:59.707 に答える
0

私の解決策は@MartijnPietersのコメントに基づいています:

register_namespace検索ではなく、シリアル化にのみ影響します。

したがって、ここでの秘訣は、シリアル化と検索に異なる辞書を使用することです。

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()空でないプレフィックスが必要です。これらの関数に変更された辞書を渡します(ここでは元の辞書を変更しますが、これは名前空間が登録された後にのみ行う必要があります)。

self.namespaces['default'] = self.namespaces['']

これで、ファミリの関数をプレフィックスfind()付きで使用できるようになりました。default

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

しかし

tree.write(destination)

デフォルトの名前空間の要素にプレフィックスを使用しません。

于 2019-05-30T11:00:02.053 に答える
0

これは基本的にDavideBrunatoの答えですが、少なくとも私の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)

また、デフォルトの名前空間を使用したタグの正解も生成されます。

于 2021-04-07T16:13:53.000 に答える