Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sqlite/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用lxml从python中的xml中删除名称空间和前缀_Python_Xml_Namespaces_Lxml - Fatal编程技术网

使用lxml从python中的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文件需要打开并进行一些更改,其中一个更改是删除名称空间和前缀,然后保存到另一个文件。 以下是xml:

<?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”
}
方法:

  • 为了解析xml文件。。。它至少应该是xml,因此如果不存在声明标记,我们需要检查并添加声明标记
  • 如果我有名称空间。。这很糟糕,因为我不能使用XPath,这正是我想要做的
  • 如果我的文件很大,我应该只对可以想象的最小部分进行操作,在我准备解析它之前,我需要清理这些部分
  • 作用
    
    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()')