Python ElementTree模块:如何在使用方法“find”,“findall”时忽略XML文件的命名空间来定位匹配元素,
我想使用“findall”的方法来查找ElementTree模块中源xml文件的一些元素。
但是,源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”等方法时忽略名称空间?
而不是修改XML文档本身,最好parsing它,然后修改结果中的标记。 这样你可以处理多个名字空间和名字空间别名:
from StringIO import StringIO import xml.etree.ElementTree as ET # instead of ET.fromstring(xml) it = ET.iterparse(StringIO(xml)) for _, el in it: if '}' in el.tag: el.tag = el.tag.split('}', 1)[1] # strip all namespaces root = it.root
这是基于这里的讨论: http : //bugs.python.org/issue18304
如果您在parsingxml之前从xml中删除了xmlns属性,那么树中的每个标记都不会有一个名称空间。
import re xmlstring = re.sub(' xmlns="[^"]+"', '', xmlstring, count=1)
到目前为止的答案明确地把名字空间值放在脚本中。 对于更通用的解决scheme,我宁愿从xml中提取命名空间:
def get_namespace(element): m = re.match('\{.*\}', element.tag) return m.group(0) if m else ''
并在查找方法中使用它:
namespace = get_namespace(tree.getroot()) print tree.find('./{0}parent/{0}version'.format(namespace)).text
这是nonagon的答案的扩展,它也剥夺了属性的命名空间:
from StringIO import StringIO import xml.etree.ElementTree as ET # instead of ET.fromstring(xml) it = ET.iterparse(StringIO(xml)) for _, el in it: if '}' in el.tag: el.tag = el.tag.split('}', 1)[1] # strip all namespaces for at in el.attrib.keys(): # strip namespaces of attributes too if '}' in at: newat = at.split('}', 1)[1] el.attrib[newat] = el.attrib[at] del el.attrib[at] root = it.root
您也可以使用优雅的string格式化结构:
ns='http://www.test.com' el2 = tree.findall("{%s}DEAL_LEVEL/{%s}PAID_OFF" %(ns,ns))
或者,如果您确定PAID_OFF只出现在树中的一个级别中:
el2 = tree.findall(".//{%s}PAID_OFF" % ns)