Pythonを使った名前空間解析XMLドキュメント


問題
XMLファイルを解析したいですが、ドキュメントにXML名前空間が使用されています。
ソリューション
これを考慮して名前空間を使用した文書:

<?xml version="1.0" encoding="utf-8"?>
<top>
  <author>David Beazley</author>
  <content>
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <title>Hello World</title>
      </head>
      <body>
        <h1>Hello World!</h1>
      </body>
    </html>
  </content>
</top>
この文書を解析して普通のクエリーを実行すると、すべてのステップがかなり煩雑になるので、これは簡単ではないことが分かります。

>>> # Some queries that work
>>> doc.findtext('author')
'David Beazley'
>>> doc.find('content')
<Element 'content' at 0x100776ec0>
>>> # A query involving a namespace (doesn't work)
>>> doc.find('content/html')
>>> # Works if fully qualified
>>> doc.find('content/{http://www.w3.org/1999/xhtml}html')
<Element '{http://www.w3.org/1999/xhtml}html' at 0x1007767e0>
>>> # Doesn't work
>>> doc.findtext('content/{http://www.w3.org/1999/xhtml}html/head/title')
>>> # Fully qualified
>>> doc.findtext('content/{http://www.w3.org/1999/xhtml}html/'
... '{http://www.w3.org/1999/xhtml}head/{http://www.w3.org/1999/xhtml}title')
'Hello World'
>>>
名前空間処理ロジックをツール類として包装することで、このプロセスを簡略化できます。

class XMLNamespaces:
  def __init__(self, **kwargs):
    self.namespaces = {}
    for name, uri in kwargs.items():
      self.register(name, uri)
  def register(self, name, uri):
    self.namespaces[name] = '{'+uri+'}'
  def __call__(self, path):
    return path.format_map(self.namespaces)
このクラスを次のように使います。

>>> ns = XMLNamespaces(html='http://www.w3.org/1999/xhtml')
>>> doc.find(ns('content/{html}html'))
<Element '{http://www.w3.org/1999/xhtml}html' at 0x1007767e0>
>>> doc.findtext(ns('content/{html}html/{html}head/{html}title'))
'Hello World'
>>>
討論する
解析に名前空間が含まれているXMLドキュメントは、より複雑です。上のXMLNamespacesは、完全なURIの代わりに縮約名を使用することができるだけで、少し簡潔になります。
残念ながら、基本的なElementTree解析においては、名前空間の情報を取得する手段がない。しかし、 iterparse() 関数を使用すれば、名前空間の処理範囲に関するより多くの情報を得ることができます。たとえば:

>>> from xml.etree.ElementTree import iterparse
>>> for evt, elem in iterparse('ns2.xml', ('end', 'start-ns', 'end-ns')):
... print(evt, elem)
...
end <Element 'author' at 0x10110de10>
start-ns ('', 'http://www.w3.org/1999/xhtml')
end <Element '{http://www.w3.org/1999/xhtml}title' at 0x1011131b0>
end <Element '{http://www.w3.org/1999/xhtml}head' at 0x1011130a8>
end <Element '{http://www.w3.org/1999/xhtml}h1' at 0x101113310>
end <Element '{http://www.w3.org/1999/xhtml}body' at 0x101113260>
end <Element '{http://www.w3.org/1999/xhtml}html' at 0x10110df70>
end-ns None
end <Element 'content' at 0x10110de68>
end <Element 'top' at 0x10110dd60>
>>> elem # This is the topmost element
<Element 'top' at 0x10110dd60>
>>>
最後に、もしあなたが扱うXMLテキストが他の高度なXML特性に加えて、名前空間にも使われるなら、ElemenntTreeの代わりにlxml関数ライブラリを使うのが一番いいと思います。例えば、lxmlは、DMDを利用したドキュメント検証、より良いXPSサポート、および他のいくつかの高度XML特性などに対してより良いサポートを提供しています。このセクションは、XML解析を簡単にする方法を教えているだけです。
以上はPythonが名前空間を利用してXMLドキュメントの詳細を解析しました。Pythonの名前空間解析XML文書に関する資料は他の関連記事に注目してください。