Python ElementTreeとは?XMLを簡単に扱える最強ライブラリを徹底解説!

python

「設定ファイルをXML形式で保存したい」
「WebAPIから返ってきたXMLデータを解析したい」
「XMLファイルの中身を読み込んで編集したい」

プログラミングをしていると、XML(エックスエムエル)形式のデータを扱う場面って意外と多いですよね。

そんな時に活躍するのが、Pythonの標準ライブラリに含まれているElementTreeです。追加インストール不要で、XMLの読み込み・作成・編集が簡単にできる便利なツールなんです。

この記事では、ElementTreeの基本から実践的な使い方まで、初心者の方でも分かるように丁寧に解説していきますね。

スポンサーリンク

そもそもXMLって何?基礎知識をおさらい

ElementTreeの説明に入る前に、XMLについて簡単に確認しておきましょう。

XMLとは

XMLは「eXtensible Markup Language(拡張可能なマークアップ言語)」の略で、データを構造的に記述するための形式です。

HTMLと似た見た目をしていますが、HTMLが「Webページの表示」を目的としているのに対し、XMLは「データの保存や受け渡し」を目的としています。

XMLの基本構造

実際のXMLファイルを見てみましょう:

<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
    <book category="fiction">
        <title>ハリー・ポッター</title>
        <author>J.K.ローリング</author>
        <year>1997</year>
        <price>2000</price>
    </book>
    <book category="science">
        <title>ホーキング、宇宙を語る</title>
        <author>スティーヴン・ホーキング</author>
        <year>1988</year>
        <price>1500</price>
    </book>
</bookstore>

構成要素:

  • 宣言部<?xml version="1.0"?>(ファイルの最初)
  • 要素(エレメント)<book><title>など
  • 属性(アトリビュート)category="fiction"のような付加情報
  • テキスト:タグで囲まれた中身の文字列

このように階層構造(ツリー構造)でデータを表現するのがXMLの特徴です。

ElementTreeとは?基本を押さえよう

ElementTreeの正体

ElementTreeは、PythonでXMLを扱うための標準ライブラリです。正式にはxml.etree.ElementTreeという名前で、Python本体に最初から含まれています。

つまり、追加でインストールする必要がなく、すぐに使い始められるんです。これが大きな魅力ですね。

なぜElementTreeが便利なのか

シンプルで分かりやすい
XMLの複雑な仕様を意識せず、Pythonらしいコードでデータを扱えます。

標準ライブラリだから安心
外部ライブラリに依存しないので、環境構築が楽です。

十分な機能
読み込み、検索、作成、編集と、基本的な操作は全てカバーしています。

軽量で高速
大きなXMLファイルもスムーズに処理できます。

他の選択肢もある?

ElementTree以外にも、XMLを扱うライブラリはいくつかあります:

  • lxml:より高機能で高速(別途インストール必要)
  • minidom:DOMベースの処理(標準ライブラリ)
  • xmltodict:XMLを辞書型に変換(別途インストール必要)

でも、まず最初に学ぶならElementTreeが断然おすすめです。シンプルで扱いやすく、ほとんどのケースで十分な性能を発揮してくれますよ。

ElementTreeの基本的な使い方

それでは、実際にコードを書いていきましょう。

インポート方法

まずはライブラリをインポートします。一般的には省略名を使います:

import xml.etree.ElementTree as ET

このas ETという書き方で、長い名前を短く扱えるようになります。多くのコード例でこの形式が使われていますね。

XMLファイルを読み込む

最も基本的な操作、ファイルの読み込みからスタートです。

import xml.etree.ElementTree as ET

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

# ルート要素を取得
root = tree.getroot()

print(root.tag)  # 'bookstore' と表示される

解説:

  • ET.parse()でファイル全体を読み込みます
  • getroot()でルート要素(最上位の要素)を取得します
  • root.tagで要素のタグ名を確認できます

文字列からXMLを読み込む

ファイルではなく、文字列として持っているXMLデータを読み込むこともできます:

import xml.etree.ElementTree as ET

xml_string = '''
<bookstore>
    <book>
        <title>サンプル本</title>
    </book>
</bookstore>
'''

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

print(root.tag)  # 'bookstore' と表示される

ET.fromstring()を使うと、文字列から直接要素を作れます。WebAPIのレスポンスを処理する時などに便利ですね。

要素の取得と検索

XMLから必要なデータを取り出す方法を見ていきましょう。

子要素を取得する

全ての子要素を取得:

import xml.etree.ElementTree as ET

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

# 直接の子要素を全て取得
for child in root:
    print(child.tag, child.attrib)
    # book {'category': 'fiction'}
    # book {'category': 'science'}

forループで子要素を1つずつ処理できます。

特定のタグの子要素だけ取得:

# <book>要素だけを取得
for book in root.findall('book'):
    title = book.find('title').text
    print(title)
    # ハリー・ポッター
    # ホーキング、宇宙を語る

findall()で特定のタグを持つ要素を全て取得します。

要素のテキストと属性を取得

for book in root.findall('book'):
    # テキスト内容を取得
    title = book.find('title').text
    author = book.find('author').text

    # 属性を取得
    category = book.get('category')

    print(f'{title} by {author} ({category})')

ポイント:

  • .textでタグ内のテキストを取得
  • .get('属性名')で属性の値を取得

XPathで検索する

より柔軟な検索には、XPath(エックスパス)という記法が使えます:

# 全ての<title>要素を検索(階層を問わず)
titles = root.findall('.//title')
for title in titles:
    print(title.text)

# category属性が"fiction"の<book>要素を検索
fiction_books = root.findall(".//book[@category='fiction']")
for book in fiction_books:
    title = book.find('title').text
    print(title)

よく使うXPath表現:

  • .//タグ名:全ての階層から検索
  • ./タグ名:直下の子要素だけ検索
  • [@属性名='値']:属性で絞り込み
  • [数字]:n番目の要素を取得(1始まり)

要素が存在するか確認

要素が見つからない時の対処も重要です:

# findは見つからない時Noneを返す
price_element = book.find('price')
if price_element is not None:
    price = price_element.text
    print(f'価格: {price}円')
else:
    print('価格情報なし')

if element is not None:で存在チェックをするのが安全です。

XMLの作成と編集

ElementTreeでは、XMLデータを作ったり変更したりすることもできます。

新しいXMLを作成する

ゼロから新しいXML文書を作ってみましょう:

import xml.etree.ElementTree as ET

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

# 子要素を作成
book = ET.SubElement(root, 'book', category='programming')
title = ET.SubElement(book, 'title')
title.text = 'Python入門'
author = ET.SubElement(book, 'author')
author.text = '山田太郎'
year = ET.SubElement(book, 'year')
year.text = '2023'

# ツリーオブジェクトを作成
tree = ET.ElementTree(root)

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

解説:

  • ET.Element()でルート要素を作成
  • ET.SubElement()で子要素を追加
  • .textでテキスト内容を設定
  • tree.write()でファイルに保存

既存のXMLを編集する

既にあるXMLファイルの内容を変更することもできます:

import xml.etree.ElementTree as ET

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

# 特定の要素を見つけて編集
for book in root.findall('book'):
    title = book.find('title')
    if title.text == 'ハリー・ポッター':
        # 価格を変更
        price = book.find('price')
        price.text = '2500'  # 2000円から2500円に値上げ

# 変更を保存
tree.write('books_updated.xml', encoding='utf-8', xml_declaration=True)

要素を追加する

新しい本を既存のリストに追加してみます:

# 新しい<book>要素を作成
new_book = ET.SubElement(root, 'book', category='technology')
ET.SubElement(new_book, 'title').text = 'AI入門'
ET.SubElement(new_book, 'author').text = '佐藤花子'
ET.SubElement(new_book, 'year').text = '2024'
ET.SubElement(new_book, 'price').text = '3000'

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

要素を削除する

不要な要素を削除することもできます:

# 1997年以前の本を削除
for book in root.findall('book'):
    year = book.find('year')
    if int(year.text) < 1997:
        root.remove(book)

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

root.remove(要素)で子要素を削除できます。

実践的な使用例

実際のプロジェクトでよく使われるパターンをご紹介します。

例1:設定ファイルの読み込み

アプリケーションの設定をXMLで管理するケース:

import xml.etree.ElementTree as ET

def load_config(filename):
    """設定ファイルを読み込んで辞書で返す"""
    tree = ET.parse(filename)
    root = tree.getroot()

    config = {}

    # データベース設定
    db = root.find('database')
    config['db_host'] = db.find('host').text
    config['db_port'] = int(db.find('port').text)
    config['db_name'] = db.find('name').text

    # アプリケーション設定
    app = root.find('application')
    config['app_name'] = app.find('name').text
    config['debug_mode'] = app.find('debug').text == 'true'

    return config

# 使い方
config = load_config('config.xml')
print(f"データベース: {config['db_host']}:{config['db_port']}")

例2:RSSフィードの解析

ブログのRSSフィード(更新情報)を読み込むケース:

import xml.etree.ElementTree as ET
import urllib.request

def parse_rss(url):
    """RSSフィードを取得して記事リストを返す"""
    # URLからXMLを取得
    with urllib.request.urlopen(url) as response:
        xml_data = response.read()

    root = ET.fromstring(xml_data)

    articles = []

    # 各記事を処理
    for item in root.findall('.//item'):
        article = {
            'title': item.find('title').text,
            'link': item.find('link').text,
            'pubDate': item.find('pubDate').text,
        }

        # 説明(オプショナル)
        description = item.find('description')
        if description is not None:
            article['description'] = description.text

        articles.append(article)

    return articles

# 使い方
articles = parse_rss('https://example.com/feed.xml')
for article in articles:
    print(f"{article['title']}: {article['link']}")

例3:CSVからXMLへの変換

CSVファイルのデータをXML形式に変換するケース:

import xml.etree.ElementTree as ET
import csv

def csv_to_xml(csv_filename, xml_filename):
    """CSVファイルをXML形式に変換"""
    # ルート要素
    root = ET.Element('employees')

    # CSVを読み込む
    with open(csv_filename, 'r', encoding='utf-8') as f:
        reader = csv.DictReader(f)

        for row in reader:
            # 各行を<employee>要素に
            employee = ET.SubElement(root, 'employee', id=row['id'])
            ET.SubElement(employee, 'name').text = row['name']
            ET.SubElement(employee, 'department').text = row['department']
            ET.SubElement(employee, 'salary').text = row['salary']

    # XMLとして保存
    tree = ET.ElementTree(root)
    tree.write(xml_filename, encoding='utf-8', xml_declaration=True)

    print(f'{csv_filename} を {xml_filename} に変換しました')

# 使い方
csv_to_xml('employees.csv', 'employees.xml')

例4:XMLの整形(インデント追加)

ElementTreeで保存したXMLは改行やインデントがないので、読みにくいことがあります。整形する方法:

import xml.etree.ElementTree as ET

def prettify_xml(element, level=0):
    """XMLを人間が読みやすい形に整形"""
    indent = "\n" + "  " * level

    if len(element):
        if not element.text or not element.text.strip():
            element.text = indent + "  "
        if not element.tail or not element.tail.strip():
            element.tail = indent

        for child in element:
            prettify_xml(child, level + 1)

        if not child.tail or not child.tail.strip():
            child.tail = indent
    else:
        if level and (not element.tail or not element.tail.strip()):
            element.tail = indent

# 使い方
tree = ET.parse('books.xml')
root = tree.getroot()

prettify_xml(root)

tree.write('books_pretty.xml', encoding='utf-8', xml_declaration=True)

よくあるエラーと対処法

ElementTreeを使っていると、いくつか典型的なエラーに遭遇します。対処法を知っておきましょう。

エラー1:ParseError

エラーメッセージ:

xml.etree.ElementTree.ParseError: not well-formed (invalid token)

原因:
XMLの構文が間違っている(タグの閉じ忘れ、特殊文字の処理ミスなど)

対処法:

  • XMLファイルの構文を確認する
  • オンラインのXMLバリデーター(検証ツール)を使って確認
  • エラーメッセージに表示される行番号を確認
try:
    tree = ET.parse('broken.xml')
except ET.ParseError as e:
    print(f'XML解析エラー: {e}')
    # エラー時の処理

エラー2:AttributeError

エラーメッセージ:

AttributeError: 'NoneType' object has no attribute 'text'

原因:
find()findall()で要素が見つからず、Noneに対して.textなどを呼び出している

対処法:
存在チェックを必ず行う

# 悪い例
title = book.find('title').text  # titleが存在しないとエラー

# 良い例
title_element = book.find('title')
if title_element is not None:
    title = title_element.text
else:
    title = '(タイトルなし)'

エラー3:UnicodeEncodeError

エラーメッセージ:

UnicodeEncodeError: 'ascii' codec can't encode characters

原因:
日本語などの非ASCII文字を含むXMLを正しくエンコードせずに保存している

対処法:
encoding='utf-8'を必ず指定する

# 正しい保存方法
tree.write('output.xml', encoding='utf-8', xml_declaration=True)

エラー4:FileNotFoundError

エラーメッセージ:

FileNotFoundError: [Errno 2] No such file or directory: 'data.xml'

原因:
指定したファイルが存在しない、またはパスが間違っている

対処法:
ファイルの存在を確認してから処理する

import os

xml_file = 'data.xml'

if os.path.exists(xml_file):
    tree = ET.parse(xml_file)
else:
    print(f'{xml_file} が見つかりません')

ElementTreeを使う時のベストプラクティス

実務で使う時に知っておきたい、より良い使い方のコツをご紹介します。

1. 名前空間の扱い

XMLで名前空間(namespace)が使われている場合の対処法:

# 名前空間付きのXML
xml_string = '''
<root xmlns:custom="http://example.com/custom">
    <custom:item>データ</custom:item>
</root>
'''

root = ET.fromstring(xml_string)

# 名前空間を辞書で定義
namespaces = {'custom': 'http://example.com/custom'}

# 検索時に名前空間を指定
items = root.findall('custom:item', namespaces)
for item in items:
    print(item.text)

2. 大きなXMLファイルの処理

メモリ効率を考えた処理方法:

import xml.etree.ElementTree as ET

# iterparse を使うとメモリ効率が良い
for event, element in ET.iterparse('huge_file.xml', events=('end',)):
    if element.tag == 'book':
        title = element.find('title').text
        print(title)

        # 処理済みの要素をメモリから解放
        element.clear()

iterparse()を使うと、ファイル全体を一度にメモリに読み込まず、必要な部分だけ処理できます。

3. デフォルト値の設定

要素が見つからない時のデフォルト値を設定する書き方:

def get_text(element, tag, default=''):
    """要素からテキストを取得、なければデフォルト値を返す"""
    found = element.find(tag)
    return found.text if found is not None else default

# 使い方
for book in root.findall('book'):
    title = get_text(book, 'title', '(タイトルなし)')
    price = get_text(book, 'price', '0')
    print(f'{title}: {price}円')

4. 複数の条件での検索

より複雑な検索をする方法:

# 価格が2000円以上でカテゴリがfictionの本
expensive_fiction = []

for book in root.findall('book'):
    if book.get('category') == 'fiction':
        price = int(book.find('price').text)
        if price >= 2000:
            expensive_fiction.append(book)

print(f'{len(expensive_fiction)}冊見つかりました')

まとめ:ElementTreeでXML処理をマスターしよう

Python ElementTreeは、XMLを扱うための強力で使いやすいツールです。

この記事のポイントをおさらい:

  • ElementTreeはPython標準ライブラリで追加インストール不要
  • ET.parse()でファイル、ET.fromstring()で文字列から読み込み
  • find()findall()で要素を検索できる
  • .textでテキスト、.get()で属性を取得
  • XPathを使うとより柔軟な検索が可能
  • ET.Element()SubElement()で新規作成
  • 要素の存在チェック(if element is not None)が重要
  • encoding='utf-8'を指定して保存する

設定ファイル、データ交換、WebAPIのレスポンス処理など、XMLが必要な場面は多岐にわたります。ElementTreeをマスターすれば、これらの処理が簡単にできるようになりますよ。

最初は少し複雑に感じるかもしれませんが、基本的な操作から始めて徐々に慣れていけば大丈夫です。実際に手を動かしながら、XMLとElementTreeの使い方を身につけていってくださいね!

コメント

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