PythonでXMLを扱う完全ガイド【ElementTree入門】

python

API連携やデータ交換でXMLファイルを扱う機会は意外と多いですよね。

「XMLって複雑そう…」と思っている方も多いかもしれませんが、Pythonを使えばとても簡単にXMLの読み込みや書き込みができるんです。

今回は、Python標準ライブラリのElementTreeを使って、XMLを自在に操る方法を解説していきます。初心者の方でもすぐに実践できるように、実例をたっぷり用意しましたよ!


スポンサーリンク

ElementTreeとは?なぜこれを使うの?

ElementTreeは、Pythonに最初から入っている標準ライブラリです。

追加でインストールする必要がなく、すぐに使えるのが大きなメリットですね。軽量で高速、そして使いやすいという三拍子揃ったライブラリなんです。

主な特徴

  • 標準ライブラリ:追加インストール不要
  • シンプルなAPI:直感的で分かりやすい
  • 高速処理:大きなファイルでも効率的
  • 豊富な機能:読み込み、書き込み、検索、編集すべてに対応

基本的なインポート方法

ElementTreeを使うには、以下のようにインポートします。

import xml.etree.ElementTree as ET

慣習としてETという短い名前をつけることが多いですよ。毎回長い名前を書くのは大変ですからね!


XMLファイルを読み込んでみよう

まずは、XMLファイルを読み込んで内容を表示する方法から始めましょう。

サンプルXMLファイル

こんなXMLファイルがあるとします。

<?xml version="1.0" encoding="UTF-8"?>
<books>
    <book id="1">
        <title>Python入門</title>
        <author>山田太郎</author>
        <price>2500</price>
    </book>
    <book id="2">
        <title>データ分析の基礎</title>
        <author>佐藤花子</author>
        <price>3200</price>
    </book>
</books>

このファイルをbooks.xmlとして保存しておきましょう。

読み込みコード

import xml.etree.ElementTree as ET

# XMLファイルを読み込む
tree = ET.parse('books.xml')
root = tree.getroot()

# ルート要素を表示
print(f"ルート要素: {root.tag}")

# すべてのbook要素を取得
for book in root.findall('book'):
    book_id = book.get('id')
    title = book.find('title').text
    author = book.find('author').text
    price = book.find('price').text

    print(f"ID: {book_id}")
    print(f"タイトル: {title}")
    print(f"著者: {author}")
    print(f"価格: {price}円")
    print("---")

実行結果:

ルート要素: books
ID: 1
タイトル: Python入門
著者: 山田太郎
価格: 2500円
---
ID: 2
タイトル: データ分析の基礎
著者: 佐藤花子
価格: 3200円
---

コードの流れはシンプルですよね。parse()でファイルを読み込み、getroot()でルート要素を取得するだけです。


文字列からXMLを読み込む方法

ファイルではなく、文字列として持っているXMLを解析したい場合もあります。

import xml.etree.ElementTree as ET

xml_string = """
<person>
    <name>田中一郎</name>
    <age>30</age>
    <city>東京</city>
</person>
"""

# 文字列からXMLを解析
root = ET.fromstring(xml_string)

name = root.find('name').text
age = root.find('age').text
city = root.find('city').text

print(f"名前: {name}")
print(f"年齢: {age}歳")
print(f"都市: {city}")

実行結果:

名前: 田中一郎
年齢: 30歳
都市: 東京

APIから取得したXMLデータを処理するときは、このfromstring()メソッドが便利ですよ。


XMLの要素を検索する方法

ElementTreeには、要素を検索するための便利なメソッドがいくつかあります。

主な検索メソッド

メソッド説明戻り値
find()最初にマッチした要素を返す1つの要素
findall()マッチしたすべての要素を返す要素のリスト
findtext()最初にマッチした要素のテキストを返す文字列

実例で理解しよう

import xml.etree.ElementTree as ET

xml_data = """
<library>
    <section name="技術書">
        <book>Python実践入門</book>
        <book>Web開発の教科書</book>
    </section>
    <section name="小説">
        <book>夏の物語</book>
        <book>冬の記憶</book>
    </section>
</library>
"""

root = ET.fromstring(xml_data)

# 最初のsection要素を取得
first_section = root.find('section')
print(f"最初のセクション: {first_section.get('name')}")

# すべてのsection要素を取得
all_sections = root.findall('section')
print(f"セクション数: {len(all_sections)}")

# 技術書セクションの最初の本を取得
tech_section = root.find("section[@name='技術書']")
if tech_section is not None:
    first_book = tech_section.findtext('book')
    print(f"技術書の最初の本: {first_book}")

XPath風の記法も使えるので、条件に合った要素を簡単に絞り込めます。


属性(Attribute)の取得と設定

XML要素には属性がついていることがよくあります。

属性を取得する

import xml.etree.ElementTree as ET

xml_data = """
<product id="A001" category="electronics" stock="50">
    <name>ワイヤレスマウス</name>
</product>
"""

root = ET.fromstring(xml_data)

# 単一の属性を取得
product_id = root.get('id')
print(f"商品ID: {product_id}")

# すべての属性を辞書で取得
all_attrs = root.attrib
print(f"すべての属性: {all_attrs}")

# 特定の属性が存在するかチェック
if 'stock' in root.attrib:
    stock = root.get('stock')
    print(f"在庫数: {stock}個")

実行結果:

商品ID: A001
すべての属性: {'id': 'A001', 'category': 'electronics', 'stock': '50'}
在庫数: 50個

get()メソッドは、属性が存在しない場合にNoneを返すので、安全に使えます。

属性を設定・変更する

import xml.etree.ElementTree as ET

xml_data = "<product id='A001'><name>キーボード</name></product>"
root = ET.fromstring(xml_data)

# 新しい属性を追加
root.set('price', '5000')
root.set('currency', 'JPY')

# 既存の属性を変更
root.set('id', 'A002')

print(ET.tostring(root, encoding='unicode'))

set()メソッドで簡単に属性を追加・変更できますよ。


新しいXMLを作成する方法

ElementTreeを使えば、ゼロからXMLを作ることもできます。

import xml.etree.ElementTree as ET

# ルート要素を作成
root = ET.Element('employees')

# 従業員1を追加
emp1 = ET.SubElement(root, 'employee', id='E001')
ET.SubElement(emp1, 'name').text = '鈴木太郎'
ET.SubElement(emp1, 'department').text = '営業部'
ET.SubElement(emp1, 'salary').text = '400000'

# 従業員2を追加
emp2 = ET.SubElement(root, 'employee', id='E002')
ET.SubElement(emp2, 'name').text = '高橋花子'
ET.SubElement(emp2, 'department').text = '開発部'
ET.SubElement(emp2, 'salary').text = '450000'

# ツリーを作成
tree = ET.ElementTree(root)

# ファイルに保存(整形あり)
ET.indent(tree, space='    ')  # Python 3.9以降
tree.write('employees.xml', encoding='utf-8', xml_declaration=True)

print("XMLファイルを作成しました!")

生成されるXMLファイル:

<?xml version='1.0' encoding='utf-8'?>
<employees>
    <employee id="E001">
        <name>鈴木太郎</name>
        <department>営業部</department>
        <salary>400000</salary>
    </employee>
    <employee id="E002">
        <name>高橋花子</name>
        <department>開発部</department>
        <salary>450000</salary>
    </employee>
</employees>

SubElement()を使うと、親要素に子要素を次々と追加できて便利です。


XMLの要素を編集・削除する

既存のXMLを編集したり、不要な要素を削除したりすることもできます。

要素の編集

import xml.etree.ElementTree as ET

tree = ET.parse('books.xml')
root = tree.getroot()

# 最初の本の価格を変更
first_book = root.find('book')
price_elem = first_book.find('price')
price_elem.text = '2800'  # 価格を更新

# ファイルに保存
tree.write('books_updated.xml', encoding='utf-8', xml_declaration=True)

テキストや属性を変更したら、必ずwrite()でファイルに保存しましょう。

要素の削除

import xml.etree.ElementTree as ET

tree = ET.parse('books.xml')
root = tree.getroot()

# ID=2の本を削除
for book in root.findall('book'):
    if book.get('id') == '2':
        root.remove(book)
        break

# 保存
tree.write('books_modified.xml', encoding='utf-8', xml_declaration=True)

remove()メソッドで指定した子要素を削除できます。


XPathで高度な検索をする

XPathを使うと、より複雑な条件で要素を検索できます。

import xml.etree.ElementTree as ET

xml_data = """
<store>
    <product category="food" price="500">りんご</product>
    <product category="food" price="300">バナナ</product>
    <product category="electronics" price="5000">マウス</product>
    <product category="electronics" price="8000">キーボード</product>
</store>
"""

root = ET.fromstring(xml_data)

# 食品カテゴリの商品をすべて取得
foods = root.findall("./product[@category='food']")
print("食品:")
for food in foods:
    print(f"  - {food.text} ({food.get('price')}円)")

# 価格が1000円以上の商品を取得(ElementTreeでは制限あり)
# より高度な検索にはlxmlライブラリを使用することをおすすめします

実行結果:

食品:
  - りんご (500円)
  - バナナ (300円)

ElementTreeのXPathサポートは基本的な機能に限られます。もっと複雑な検索が必要な場合は、lxmlライブラリの使用を検討しましょう。


エラーハンドリング:安全にXMLを扱う

XMLの読み込みや解析では、エラーが発生する可能性があります。

import xml.etree.ElementTree as ET

def safe_parse_xml(filename):
    try:
        tree = ET.parse(filename)
        root = tree.getroot()
        return root
    except FileNotFoundError:
        print(f"エラー: {filename} が見つかりません")
        return None
    except ET.ParseError as e:
        print(f"XML解析エラー: {e}")
        return None

# 使用例
root = safe_parse_xml('data.xml')
if root is not None:
    # 処理を続ける
    print("XMLの読み込みに成功しました")
else:
    print("XMLの読み込みに失敗しました")

try-exceptを使って、適切にエラーを処理することが大切です。


実践例:天気予報データの解析

実際のユースケースとして、天気予報のXMLデータを解析してみましょう。

import xml.etree.ElementTree as ET

weather_xml = """
<weather_forecast>
    <city name="東京">
        <date>2024-03-20</date>
        <temperature>
            <high>18</high>
            <low>10</low>
        </temperature>
        <condition>晴れ</condition>
        <humidity>60</humidity>
    </city>
    <city name="大阪">
        <date>2024-03-20</date>
        <temperature>
            <high>20</high>
            <low>12</low>
        </temperature>
        <condition>曇り</condition>
        <humidity>65</humidity>
    </city>
</weather_forecast>
"""

root = ET.fromstring(weather_xml)

print("本日の天気予報")
print("=" * 40)

for city in root.findall('city'):
    city_name = city.get('name')
    date = city.findtext('date')
    high = city.findtext('temperature/high')
    low = city.findtext('temperature/low')
    condition = city.findtext('condition')
    humidity = city.findtext('humidity')

    print(f"\n【{city_name}】")
    print(f"日付: {date}")
    print(f"気温: {low}℃ ~ {high}℃")
    print(f"天気: {condition}")
    print(f"湿度: {humidity}%")

実行結果:

本日の天気予報
========================================

【東京】
日付: 2024-03-20
気温: 10℃ ~ 18℃
天気: 晴れ
湿度: 60%

【大阪】
日付: 2024-03-20
気温: 12℃ ~ 20℃
天気: 曇り
湿度: 65%

このように、実用的なデータ処理も簡単にできますね。


より高度な機能が必要なら:lxmlライブラリ

ElementTreeは軽量で使いやすいですが、より高度な機能が必要な場合はlxmlライブラリがおすすめです。

lxmlの特徴

  • 完全なXPathサポート
  • XMLスキーマ検証
  • より高速な処理
  • 名前空間の高度な処理

インストール方法

pip install lxml

基本的な使い方

from lxml import etree

# ElementTreeと似たAPIで使える
tree = etree.parse('data.xml')
root = tree.getroot()

# XPathの完全サポート
results = root.xpath('//book[@price > 2000]')

大規模なXML処理や、複雑な検索が必要な場合は、lxmlへの移行を検討しましょう。


よくある質問

Q: 大きなXMLファイルを扱うときの注意点は?

A: ElementTreeは、ファイル全体をメモリに読み込みます。非常に大きなファイル(数GB以上)の場合は、iterparse()を使ったストリーミング処理を検討してください。これなら少ないメモリで処理できますよ。

Q: XMLの整形(インデント)はどうすればいい?

A: Python 3.9以降ならET.indent()が使えます。それ以前のバージョンでは、lxmlや外部ライブラリを使うか、自分で整形関数を作る必要があります。

Q: 名前空間(Namespace)が含まれるXMLはどう扱う?

A: ElementTreeは名前空間もサポートしています。ただし、記述が少し複雑になるので、名前空間を多用する場合はlxmlの方が扱いやすいでしょう。


まとめ:PythonでXMLは怖くない!

PythonのElementTreeを使ったXML処理について、重要なポイントをおさらいします。

今日学んだこと:

  • ElementTreeは標準ライブラリで追加インストール不要
  • parse()でファイル、fromstring()で文字列を読み込める
  • find()findall()で要素を検索できる
  • get()set()で属性を操作できる
  • SubElement()で新しいXMLを作成できる
  • XPathで高度な検索が可能
  • エラーハンドリングで安全な処理を実現

XMLは一見複雑に見えますが、ElementTreeを使えば驚くほど簡単に扱えます。

API連携、設定ファイルの読み書き、データ交換など、XMLを使う場面は意外と多いですよね。今回学んだテクニックを使って、ぜひ実際のプロジェクトでXML処理に挑戦してみてください。

最初は小さなXMLから始めて、徐々に複雑なデータ構造にチャレンジしていくといいでしょう。実践を重ねるほど、XMLの扱いに慣れていきますよ!


コメント

タイトルとURLをコピーしました