Python ElementTreeモジュール。メソッド "find", "findall&quot を使用する際に、XML ファイルの名前空間を無視してマッチする要素を見つける方法。

ElementTreeモジュールで、ソースXMLファイルのいくつかの要素を見つけるために、"findall"のメソッドを使用したいと思います。

しかし、ソースXMLファイル(test.xml)には名前空間があります。サンプルとして、xmlファイルの一部を切り捨ててみました。

<?xml version="1.0" encoding="iso-8859-1"?>
<XML_HEADER xmlns="http://www.test.com">
    <TYPE>Updates</TYPE>
    <DATE>9/26/2012 10:30:34 AM</DATE>
    <COPYRIGHT_NOTICE>All Rights Reserved.</COPYRIGHT_NOTICE>
    <LICENSE>newlicense.htm</LICENSE>
    <DEAL_LEVEL>
        <PAID_OFF>N</PAID_OFF>
        </DEAL_LEVEL>
</XML_HEADER>

Pythonコードのサンプルは以下の通りです。

from xml.etree import ElementTree as ET
tree = ET.parse(r"test.xml")
el1 = tree.findall("DEAL_LEVEL/PAID_OFF") # Return None
el2 = tree.findall("{http://www.test.com}DEAL_LEVEL/{http://www.test.com}PAID_OFF") # Return <Element '{http://www.test.com}DEAL_LEVEL/PAID_OFF' at 0xb78b90>

一応動作はするのですが、名前空間 "{http://www.test.com}" があるので、各タグの前に名前空間を追加するのは非常に不便です。

find"、"findall"などのメソッドを使うときに、名前空間を無視するにはどうしたらよいでしょうか。

xmlns属性を削除してからパースすると、ツリー内の各タグの先頭に名前空間が付かない。

import re

xmlstring = re.sub(' xmlns="[^"]+"', '', xmlstring, count=1)
解説 (3)

これまでの回答では、namespaceの値をスクリプトに明示的に記述していました。より一般的な解決策としては、私はむしろxmlから名前空間を抽出することをお勧めします。

import re
def get_namespace(element):
  m = re.match('\{.*\}', element.tag)
  return m.group(0) if m else ''

そして、それをfindメソッドで使用します。

namespace = get_namespace(tree.getroot())
print tree.find('./{0}parent/{0}version'.format(namespace)).text
解説 (2)

エレガントな文字列フォーマット構成も使用できます。

ns='http://www.test.com'
el2 = tree.findall("{%s}DEAL_LEVEL/{%s}PAID_OFF" %(ns,ns))

または、PAID_OFFがツリーの1つのレベルにしか現れないことを確認した場合。

el2 = tree.findall(".//{%s}PAID_OFF" % ns)
解説 (0)