xmlを編集・生成する方法についての備忘

構造を変えずに属性やテキストに編集を加える場合

読み込んだファイルオブジェクト(Elementオブジェクト)を直接編集し,別名のオブジェクトを作って書き出す.

from lxml import etree as ET

tree = ET.parse(filename)
root = tree.getroot()

# 編集するテキストをXPathで取り出し,編集
for t in root.findall('./path/to/node'):
    t.text = t.text.replace('hoge', 'huga')

# ElementTreeオブジェクトに変更を反映し,ファイルに書き出す
new_tree = ET.ElementTree(root)
new_tree.write(filename, encoding='utf-8', xml_declaration=True)

構造を変えずに値を編集するだけなら,変えたい値を代入するだけで変更できる.
ただしもちろんElementTreeオブジェクトを生成し直して書き出す必要がある.

読み込んだxmlの一部を使い,構造を大きく変える場合

from lxml import etree as ET

tree = ET.parse(filename)
root = tree.getroot()

# ルートの作成
# ET.Elementで指定したタグをルートにする(ここではもとのまま)
new_root = ET.Element(root.tag)
# 辞書形式でルートの属性を指定(ここではもとのまま)
for k, v in root.attrib.items():
    new_root.set(k, v)

# ルートより下のノードを作成
# 元のファイルのノードを一旦格納
n1 = root.find('./path')
# ET.SubElementで,直上のノードとノードのタグを指定
node1 = ET.SubElement(new_root, n1.tag)
# 属性の追加
for k, v in n1.attrib.items():
    node1.set(k, v)

# 元のファイルのノードの一部を編集して追加
n2 = root.find('./path2')
node2 = ET.SubElement(new_root, n2.tag)
for k, v in n2.attrib.items():
    node2.set(k, v)
# テキストの追加
node2.text = 'hoge'

# 元のファイルにないノードを追加
node3 = ET.SubElement(new_root, 'huga')
node3.set('pura', 'piyo')
node3.text = 'picopico'

new_tree = ET.ElementTree(new_root)
# pretty_print=Trueをしないと改行やインデントがされない
new_tree.write(filename, encoding='utf-8', xml_declaration=True, pretty_print=True)

構造を変える場合にはxmlのパーツになるElementオブジェクトを生成する必要がある.

lxmlとxml.etree.elementtree

どちらもほぼ同じメソッドをもっていて,解析するだけなら同様に使えるが,
生成する際のpretty_printオプションにはxml.etree.elementtreeは対応していない.