使用lxml从python中的xml中删除名称空间和前缀
我有一个xml文件需要打开并进行一些更改,其中一个更改是删除名称空间和前缀,然后保存到另一个文件。 以下是xml:使用lxml从python中的xml中删除名称空间和前缀,python,xml,namespaces,lxml,Python,Xml,Namespaces,Lxml,我有一个xml文件需要打开并进行一些更改,其中一个更改是删除名称空间和前缀,然后保存到另一个文件。 以下是xml: <?xml version='1.0' encoding='UTF-8'?> <package xmlns="http://apple.com/itunes/importer"> <provider>some data</provider> <language>en-GB</language> <
<?xml version='1.0' encoding='UTF-8'?>
<package xmlns="http://apple.com/itunes/importer">
<provider>some data</provider>
<language>en-GB</language>
</package>
那么,如何在脚本中添加代码来删除名称空间和前缀呢
import xml.etree.ElementTree as ET
def remove_namespace(doc, namespace):
"""Remove namespace in the passed document in place."""
ns = u'{%s}' % namespace
nsl = len(ns)
for elem in doc.getiterator():
if elem.tag.startswith(ns):
elem.tag = elem.tag[nsl:]
metadata = '/Users/user1/Desktop/Python/metadata.xml'
tree = ET.parse(metadata)
root = tree.getroot()
remove_namespace(root, u'http://apple.com/itunes/importer')
tree.write('/Users/user1/Desktop/Python/done.xml',
pretty_print=True, xml_declaration=True, encoding='UTF-8')
使用了来自
正如Uku Loskit所建议的,通过搜索以“xmlns”开头的标记,这个方法可以很容易地扩展到删除任何名称空间属性。除此之外,使用 更新 一些标签,如
Comment
在访问tag
属性时返回函数。为此增加了一名警卫。(1) 您需要做的就是:
objectify.deannotate(root, cleanup_namespaces=True)
获取根之后,通过使用
root=tree.getroot()
这里还有两种删除名称空间的方法。第一个使用lxml.etree.QName助手,而第二个使用正则表达式。这两个函数都允许可选的名称空间列表进行匹配。如果未提供名称空间列表,则将删除所有名称空间。属性键也会被清除
from lxml import etree
import re
def remove_namespaces_qname(doc, namespaces=None):
for el in doc.getiterator():
# clean tag
q = etree.QName(el.tag)
if q is not None:
if namespaces is not None:
if q.namespace in namespaces:
el.tag = q.localname
else:
el.tag = q.localname
# clean attributes
for a, v in el.items():
q = etree.QName(a)
if q is not None:
if namespaces is not None:
if q.namespace in namespaces:
del el.attrib[a]
el.attrib[q.localname] = v
else:
del el.attrib[a]
el.attrib[q.localname] = v
return doc
def remove_namespace_re(doc, namespaces=None):
if namespaces is not None:
ns = list(map(lambda n: u'{%s}' % n, namespaces))
for el in doc.getiterator():
# clean tag
m = re.match(r'({.+})(.+)', el.tag)
if m is not None:
if namespaces is not None:
if m.group(1) in ns:
el.tag = m.group(2)
else:
el.tag = m.group(2)
# clean attributes
for a, v in el.items():
m = re.match(r'({.+})(.+)', a)
if m is not None:
if namespaces is not None:
if m.group(1) in ns:
del el.attrib[a]
el.attrib[m.group(2)] = v
else:
del el.attrib[a]
el.attrib[m.group(2)] = v
return doc
首先,用于从标记名中删除命名空间前缀:
>>> root.tag
'{http://apple.com/itunes/importer}package'
>>> etree.QName(root).localname
'package'
然后,使用从树中删除未使用的命名空间声明
完整示例:
from lxml import etree
input_xml = '''
<package xmlns="http://apple.com/itunes/importer">
<provider>some data</provider>
<language>en-GB</language>
</package>
'''
root = etree.fromstring(input_xml)
# Remove namespace prefixes
for elem in root.getiterator():
elem.tag = etree.QName(elem).localname
# Remove unused namespace declarations
etree.cleanup_namespaces(root)
print(etree.tostring(root).decode())
从lxml导入etree
输入xml=“”
一些数据
欧洲标准
'''
root=etree.fromstring(输入xml)
#删除名称空间前缀
对于root.getiterator()中的元素:
elem.tag=etree.QName(elem.localname)
#删除未使用的命名空间声明
etree.cleanup\u名称空间(根)
打印(etree.tostring(root.decode())
输出XML:
<package>
<provider>some data</provider>
<language>en-GB</language>
</package>
一些数据
欧洲标准
您还可以使用XSLT剥离名称空间
XSLT1.0(test.xsl)
输出
<?xml version='1.0' encoding='UTF-8'?>
<package>
<provider>some data</provider>
<language>en-GB</language>
</package>
一些数据
欧洲标准
您可以尝试使用lxml:
# Remove namespace prefixes
for elem in root.getiterator():
namespace_removed = elem.xpath('local-name()')
因此,我意识到这是一个较老的答案,投票率较高且被接受,但如果你正在阅读大文件,并发现自己处于与我相同的困境中;我希望这对你有帮助 这种方法的问题实际上是迭代。不管解析器有多快,做任何事情都可以说。。。10万次就会消耗你的执行时间。话虽如此,我还是要认真思考这个问题,并理解名称空间是如何工作的(或者是“打算工作的”,因为实际上并不需要名称空间)。现在,如果您的xml确实使用名称空间,这意味着您看到的标记如下:
,那么您需要根据您的用例调整这里的方法。我还将包括整个处理方式
免责声明:凭良心,我不能告诉您在解析html/xml时使用正则表达式,请查看SergiyKolesnikov的答案,因为它是有效的,但我有一个边缘案例,所以说。。。让我们来研究一下正则表达式吧!
问题:名称空间剥离需要永远。。。大多数情况下,名称空间只存在于最开始的标记或我们的“根””中。因此,在思考python如何读取中的信息,以及我们唯一的问题子节点是根节点的位置时,为什么不利用它来发挥我们的优势呢
请注意:我正在使用的这个文件作为我的示例,它是一个原始的、可怕的、毫无意义的lulz结构,并承诺在其中的某个地方包含数据
my_file
是我在示例中使用的文件路径,出于专业原因,我无法与您共享;为了得到这个答案,它被缩小了
导入操作系统、系统、子流程、re、io、json
从lxml导入etree
#如果在家里一起玩,你的文件将是“最大的文件”
我的文件=\u最大的\u文件
元内容(
exists=os.path.exists(\u最大的\u文件),
sizeof=os.path.getsize(_最大的文件),
extension_is_a_real_thing=any(re.findall(“\(html | xml)$”,my_file,re.I)),
系统认为它是子进程。检查输出(
[“文件”、“-i”、\u文件]
).decode().split(“:”[-1:[0].strip()
)
打印(json.dumps(meta_stuff,indent=2))
所以对于初学者来说,大小适中,系统最多只能认为是html;文件扩展名既不是xml也不是html
{
“存在”:是的,
“sizeof”:24442371,
“扩展是真实的”:错误,
“系统认为它是”:“text/html;charset=us ascii”
}
方法:
def速度_读取(文件路径):
#我们要低调一点,用这个字符串加上我们自己的。很好
_xml_dec=“”
#更糟糕的是。。rgx for xml现在开始
#
#我们需要提取文档中找到的第一个节点,
#因为出于我们的目的,我们知道它的名称空间uri
#ie:“属性”
#第一个节点:
#我们将取出第一个节点,得到标签的实际名称
#这意味着:
# ...
#我们采摘:
#实际名称
#然后我们要用这个名字做的标签替换整个标签
#通过简单的字符串替换
#
#->“从头开始,捕获<和>”之间的所有内容”
_第一个节点=re.compile(“^(\)”,re.I | re.M | re.U)
#->'从头开始,但在解析XML字符串之后,不要立即给我定义并调用以下函数:
从lxml导入etree
def clean_xml_名称空间(根):
对于root.getiterator()中的元素:
如果isinstance(元素,etree.\u注释):
持续
element.tag=etree.QName(element).localname
etree.cleanup\u名称空间(根)
如果是内置函数,则elem.tag上的find()将失败
<package>
<provider>some data</provider>
<language>en-GB</language>
</package>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*" priority="1">
<xsl:element name="{local-name()}" namespace="">
<xsl:apply-templates select="@*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="@*">
<xsl:attribute name="{local-name()}" namespace="">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
from lxml import etree
tree = etree.parse("metadata.xml")
xslt = etree.parse("test.xsl")
new_tree = tree.xslt(xslt)
print(etree.tostring(new_tree, pretty_print=True, xml_declaration=True,
encoding="UTF-8").decode("UTF-8"))
<?xml version='1.0' encoding='UTF-8'?>
<package>
<provider>some data</provider>
<language>en-GB</language>
</package>
# Remove namespace prefixes
for elem in root.getiterator():
namespace_removed = elem.xpath('local-name()')