web-dev-qa-db-ja.com

lxmlを使用してpythonのxmlから名前空間とプレフィックスを削除します

私はxmlファイルを開いて変更する必要があります。それらの変更の1つは、名前空間とプレフィックスを削除して、別のファイルに保存することです。ここにxmlがあります:

<?xml version='1.0' encoding='UTF-8'?>
<package xmlns="http://Apple.com/iTunes/importer">
  <provider>some data</provider>
  <language>en-GB</language>
</package>

必要なその他の変更を加えることはできますが、名前空間とプレフィックスを削除する方法がわかりません。これは私が必要とするreusklt xmlです:

<?xml version='1.0' encoding='UTF-8'?>
<package>
  <provider>some data</provider>
  <language>en-GB</language>
</package>

そして、これがxmlを開いて解析して保存する私のスクリプトです:

metadata = '/Users/user1/Desktop/Python/metadata.xml'
from lxml import etree
parser = etree.XMLParser(remove_blank_text=True)
open(metadata)
tree = etree.parse(metadata, parser)
root = tree.getroot()
tree.write('/Users/user1/Desktop/Python/done.xml', pretty_print = True, xml_declaration = True, encoding = 'UTF-8')

では、名前空間とプレフィックスを削除するコードをスクリプトに追加するにはどうすればよいでしょうか。

15
speedyrazor

Uku Loskitの提案に従ってタグを置き換えます。それに加えて、 lxml.objectify.deannotate を使用します。

from lxml import etree, objectify

metadata = '/Users/user1/Desktop/Python/metadata.xml'
parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse(metadata, parser)
root = tree.getroot()

####    
for elem in root.getiterator():
    if not hasattr(elem.tag, 'find'): continue  # (1)
    i = elem.tag.find('}')
    if i >= 0:
        elem.tag = elem.tag[i+1:]
objectify.deannotate(root, cleanup_namespaces=True)
####

tree.write('/Users/user1/Desktop/Python/done.xml',
           pretty_print=True, xml_declaration=True, encoding='UTF-8')

[〜#〜]更新[〜#〜]

Commentなどの一部のタグは、tag属性にアクセスすると関数を返します。そのためのガードを追加しました。 (1)

26
falsetru
_>>> root.tag
'{http://latest/nmc-omc/cmNrm.doc#measCollec}measCollecFile'
>>> etree.QName(root.tag).localname
'measCollecFile'
_

ソース

補遺:_lxml.etree.QName_は、構築時に要素も受け入れます。したがって、etree.QName(root.tag).localnameは次と同等です。

_etree.QName(root).localname
_
19
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')

here からのコードのスニペットを使用このメソッドは、「xmlns」で始まるタグを検索することにより、名前空間属性を削除するように簡単に拡張できます

4
Uku Loskit

あなたがする必要があるのは:

_objectify.deannotate(root, cleanup_namespaces=True)
_

ルートを取得した後、root = tree.getroot()を使用して

1
kmonsoor

XSLTを使用して名前空間を取り除くこともできます...

XSLT 1.(test.xsl)

<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>

Python

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>
1
Daniel Haley

あなたが試すことができます:

# Remove namespace prefixes
for elem in root.getiterator():
    namespace_removed = elem.xpath('local-name()')
1
lechat

名前空間を削除する他の2つの方法を次に示します。 1つ目はlxml.etree.QNameヘルパーを使用し、2つ目は正規表現を使用します。どちらの関数でも、名前空間のオプションリストを照合できます。名前空間リストを指定しない場合、すべての名前空間が削除されます。属性キーも削除されます。

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
0
Bruce