【Python】XMLデータの読み込み方法

【Python】XMLデータの読み込み方法

公開: 更新:
CodeCampが提供するDX人材育成が可能なプログラミングやITが学べる公開講座

Pythonでデータ処理を行う場面は、外部システムやAPIからのデータ取得など、さまざまな文脈で頻繁に発生します。データ形式としてよく使われるのがXMLであり、標準モジュールを活用するとファイルからのデータ読み込みを簡単に実現できます。

この記事では、PythonでXMLデータを読み込み、構造を解析するための基本的な手順を解説していきます。巨大なファイルの扱い方や文字化け対策など、サンプルコード付きで解説していきますので、ぜひ参考にしてください。



PythonでXMLデータを読み込む方法

Pythonの標準モジュールであるxml.etree.ElementTreeを活用すると、木構造のデータを一般的な用途では十分な性能で処理できます。外部ライブラリをインストールする手間がなく、すぐに使い始められるのが特徴です。

公式ドキュメントでは、このモジュールの役割を次のように説明しています。

xml.etree.ElementTree モジュールは、XML データを解析および作成するシンプルかつ効率的な API を実装しています。

出典:Python公式ドキュメント xml.etree.ElementTree モジュール

外部APIから取得した情報や複雑な設定ファイルプログラム側で扱う際にも、この標準モジュールが活躍します。なお、信頼できないソースや外部から取得したXMLをパースする場合は、セキュリティ上のリスク(XXE攻撃やBillion Laughs攻撃など)に注意が必要です。

詳細はPython公式ドキュメント XML の脆弱性を参照し、信頼性の低いデータを扱う場合はdefusedxmlパッケージの使用を検討してください。以下では、基本的な読み込み方法を順に解説します。

ファイルから読み込む

既存のXMLファイルからデータを取得するには、parse()関数を使用します。ファイルパスを指定すると、通常はドキュメント全体をツリー構造としてメモリに展開します(XMLParser設定や後述のiterparse()との組み合わせによって、挙動は変化します)。

ファイルサイズが大きい場合はメモリ消費も比例して増加するため、巨大ファイルの処理には後述のiterparse()を使用してください。

以下のコードは、カレントディレクトリにあるファイルからデータを読み込む例です。

import xml.etree.ElementTree as ET

tree = ET.parse('sample.xml')
root = tree.getroot()
print(root.tag)

上記のコードでは、parse()でツリー全体を取得した後に、getroot()でルート要素を取り出しています。このルート要素を起点として、階層を辿りながら必要な情報を探します。

指定したファイルのパスが間違っているとエラーが発生するため、実行前にファイルの配置場所を確認しておくと安心です。

文字列から読み込む

Webスクレイピングなどで取得したXML形式の文字列を処理する場合は、fromstring()関数を利用します。ファイルを介さずにメモリ上の変数を直接パースできるため、一時データの扱いに便利です。

文字列からルート要素を取得する使い方は、以下の通りです。

import xml.etree.ElementTree as ET

xml_data = '<data><item>Sample</item></data>'
root = ET.fromstring(xml_data)
print(root.tag)

上記のコードでは、fromstring()に文字列を渡すだけで、即座にルート要素であるElementオブジェクトが生成されます。parse()のようにツリー全体を格納したオブジェクトを返すわけではない点に注意が必要です。

XML宣言より前に不正な文字が含まれている場合はパースエラーとなります。XML仕様では先頭の空白や改行自体は許容されますが、バイトオーダーマーク(BOM)が含まれている場合、環境やエンコーディング指定によっては、問題になる場合があるため注意してください。

数GBの巨大なファイルを読み込む

ファイルサイズが数GBに及ぶ場合、全体を一度に読み込むとメモリ不足に陥るおそれがあります。このようなケースでは、データを少しずつ解析するiterparse()関数の出番です。

巨大なファイルをストリーム処理するコードは、以下の通りです。

import xml.etree.ElementTree as ET

# events=('start', 'end') でイテレータを回し、ルート参照を取得してからclear()する
context = ET.iterparse('huge_data.xml', events=('start', 'end'))
context = iter(context)
event, root = next(context)  # ルート要素の参照を取得

for event, elem in context:
    if event == 'end' and elem.tag == 'target_element':
        print(elem.text)
        elem.clear()
        # ルートが処理済み要素への参照を蓄積しないよう、ルート配下も解放する
        root.clear()

上記のコードでは、events=('start', 'end')イテレータを回してルート要素の参照を最初に取得し、処理済みの要素に対してroot.clear()を実行することでメモリの蓄積を防いでいます。

iterparse()はルート要素への参照をイテレータ内部で保持し続けるため、処理済みの子要素をelem.clear()しても、ルートへの参照経由でメモリが解放されない場合があります。

この構造的な問題を回避するには、上記のようにroot.clear()を併用してルートへの参照を断ち切ることが推奨されます。さらに大規模な処理ではlxmlライブラリへの移行も選択肢の一つです。


Python研修一覧はこちら

目的に合うPython研修を一覧形式から探したい方は、ぜひご利用ください。

Python研修を比較する

Java研修一覧はこちら

目的に合うJava研修を一覧形式から探したい方は、ぜひご利用ください。

Java研修を比較する

PHP研修一覧はこちら

目的に合うPHP研修を一覧形式から探したい方は、ぜひご利用ください。

PHP研修を比較する

新入社員研修

目的に合う新入社員研修を一覧形式から探したい方は、ぜひご利用ください。

新入社員研修を比較する

全ての研修からも探したい方はこちら

文字化けエラーに対処する

Windows環境などでShift-JISやCP932エンコーディングのファイルを扱うと、文字化けによるパースエラーが起こりがちです。XML宣言内のエンコーディング指定と、実際に読み込む際の文字コードが一致していないことが主な原因です。

エンコーディングを明示的に指定してファイルを読み込む方法は、以下の通りです。

import xml.etree.ElementTree as ET

with open('data_sjis.xml', 'r', encoding='cp932') as f:
    xml_string = f.read()
    root = ET.fromstring(xml_string)
print(root.tag)

上記のコードでは、組み込みのopen()関数で正しい文字コードを指定してから、文字列としてデータを読み込んでいます。この手順を踏むことによって、エンコーディングの不一致を回避できます。

なお、XMLファイルに<?xml version="1.0" encoding="cp932"?>のようなXML宣言が含まれている場合、fromstring()はPythonのUnicode文字列を受け取るため、XML宣言のエンコーディング属性と矛盾が生じてパースエラーとなる場合があります。このケースでは、XML宣言を除去してから渡すか、xml_string.encode('cp932')でバイト列に戻してからfromstring()に渡す方法が確実です。

データ提供元の仕様を確認し、適切な文字コードを選択することがエラー対策として確実な方法です。XML宣言と実際のエンコーディングが一致していることも合わせて確認してください。

Pythonで読み込んだXMLを解析する方法

PythonでXMLの読み込みが完了したら、次は目的のデータを抽出するために要素を解析するステップに進みます。標準モジュールのxml.etree.ElementTreeや用途によっては外部ライブラリを活用するのが適した選択肢となります。

本章で解説する主な解析アプローチは、以下の通りです。

  • find()で最初の要素を取得する
  • findall()で全ての要素を取得する
  • XPathを使って検索する
  • 名前空間のある要素に対応する
  • pandasでデータフレームに変換する

それぞれのメソッドやライブラリは、用途に合わせて使い分けるのがポイントです。階層構造が複雑なデータでも、適切なアプローチを選ぶことによって、効率的に情報を抽出できます。

find()で最初の要素を取得する

find()メソッドは、指定したタグ名に一致する最初の要素を検索して返すメソッドです。タグ名のみを渡した場合は直下の子要素のみが検索対象となる仕様です。

孫要素以降も対象にする場合は、XPath記法(例:.//item)を用いることによって、深い階層にある要素にもアクセスできます。

find()メソッドの使い方は、以下の通りです。

import xml.etree.ElementTree as ET

tree = ET.parse("sample.xml")
root = tree.getroot()

# 最初に見つかったitem要素を取得(直下の子要素のみが対象)
first_item = root.find("item")
if first_item is not None:
    print(first_item.text)

# 孫要素以降も検索したい場合はXPath記法を使用する
first_item_deep = root.find(".//item")
if first_item_deep is not None:
    print(first_item_deep.text)

上記のコードでは、タグ名のみを渡したroot.find("item")は直下の子要素のみを検索しています。.//itemのXPath記法を用いた場合は、深さを問わず最初に見つかった要素を返します。

要素が見つかった場合はそのElementオブジェクトを返し、見つからない場合はNoneを返す仕様です。Noneチェックを挟むことによって、要素が存在しないケースでもエラーを回避できます。

findall()で全ての要素を取得する

複数の同名要素をすべて抽出したい場合は、findall()メソッドを使用します。find()と同様に、タグ名のみを渡した場合は直下の子要素のみが検索対象となり、戻り値はリストです。

以下のコードは、特定のタグを持つ要素をリストとして一括取得する例です。

import xml.etree.ElementTree as ET

tree = ET.parse("sample.xml")
root = tree.getroot()

# すべてのitem要素を取得(直下の子要素のみが対象)
all_items = root.findall("item")
for item in all_items:
    print(item.text)

上記のコードでは、findall()で取得したリストをfor文ループ処理し、各要素のtextプロパティからテキストデータを表示しています。

孫要素以降も検索したい場合は、XPath記法(.//タグ名)を利用することによって、深いネスト構造にある同名要素もまとめて取得できます。

XPathを使って検索する

階層が深い要素や特定の属性を持つ要素を検索する際は、XPath形式の指定が役立ちます。ただし、xml.etree.ElementTreeモジュールが対応するのはXPathのサブセット(簡易XPath)のみであり、完全なXPath仕様のすべての機能は利用できません。

完全なW3C XPath仕様を利用したい場合は、lxmlライブラリの使用を検討してください。lxmlC言語ベースのlibxml2を内部で使用しており、高速かつ完全なXPath評価に対応しています。

XPathを活用した要素の検索方法は、以下の通りです。

import xml.etree.ElementTree as ET

tree = ET.parse("sample.xml")
root = tree.getroot()

# XPathを使って孫要素を検索
target_elements = root.findall(".//category/item")
for elem in target_elements:
    print(elem.attrib)

上記のコードでは、.//を用いて階層の深さを問わず目的の要素を検索しています。条件を絞り込むことによって、より複雑なデータ構造からもピンポイントで必要な情報を抽出できる仕組みです。


Python基礎・実践(Django)

企業・法人向けのPython研修では、基礎から応用まで体系的に学べます。

Python研修の詳細

DX社員研修

企業・法人向けのDX研修では、実務に繋がるリスキリングでITレベルを向上させます。

DX研修の詳細

Javaエンジニア育成研修

企業・法人向けのJavaエンジニア育成研修では、Javaの基礎から応用まで確実に習得できます。

Java研修の詳細

新卒・新入社員向け研修

企業・法人に新入社員・新卒社員に向けたプログラミング研修を提供しています。

新入社員研修の詳細

コードキャンプのIT研修を全て見る

名前空間のある要素に対応する

XMLに名前空間(xmlns)が設定されていると、単純なタグ名だけでは、要素を検索できない場合があります。名前空間とは、要素名の衝突を防ぐためにURI(Uniform Resource Identifier)で識別する仕組みです。

名前空間を含むXMLをパースする手順は、以下の通りです。

import xml.etree.ElementTree as ET

tree = ET.parse("sample.xml")
root = tree.getroot()

# 名前空間の辞書を定義
namespaces = {"ns": "http://example.com/ns"}

# 名前空間を指定して検索
items = root.findall("ns:item", namespaces)
for item in items:
    print(item.text)

上記のコードでは、プレフィックスとURIのマッピング辞書を作成し、findall()メソッドの第2引数に渡しています。これにより、名前空間付きの要素もプレフィックス指定で正確に取得できるという仕組みです。

pandasでデータフレームに変換する

データ分析用途では、pandasライブラリを用いてXMLをデータフレームに変換すると便利です。ただし、pd.read_xml()pandas 1.3.0以降で追加された関数であり、それ以前のバージョンでは使用できません。

デフォルトのパーサーがlxmlであるため、pip install lxmlでのインストールが必要です。lxmlが未インストールの場合は、parser='etree'を明示することによって、追加インストール不要の標準モジュールで動作させることもできます。

pandasのread_xml()関数を使った変換方法は、以下の通りです。

import pandas as pd

# XMLファイルをデータフレームに読み込む(lxml未インストール時はparser='etree'を指定)
df = pd.read_xml("sample.xml")
# df = pd.read_xml("sample.xml", parser='etree')  # lxmlなしで動かす場合

# 最初の5行を表示
print(df.head())

上記のコードでは、XMLの構造を解析して表形式に変換し、行と列のデータとして扱っています。なお、read_xml()はフラット(浅い)なXML構造に最適化されており、深くネストされたXMLでは意図した変換にならない場合があります。

複雑な階層構造を持つXMLを変換する際は、xpath引数で取得対象を絞り込むか、ElementTreeで手動解析した結果をデータフレームに変換するなどの追加処理を検討してください。

前提条件を満たした環境であれば、データ分析や機械学習の前処理にそのまま活用できる強力な手法です。詳細はpandas公式ドキュメント read_xml()を参照してください。

PythonのXML読み込みに関するよくある質問

PythonでXMLを扱うのにおすすめのライブラリはありますか?

用途によって適したライブラリは異なります。標準のxml.etree.ElementTreeは追加インストール不要で手軽に利用でき、基本操作には十分です。

より高速な処理や完全なXPathサポートが必要な場合はlxmlが、スクレイピングに特化した柔軟な解析を行いたい場合はBeautifulSoupが選ばれる傾向にあります。

読み込みだけではなくXMLの新規作成もできますか?

はい、ElementTreeなどのモジュールを使用すれば、新しいXMLデータを作成可能です。要素を一つずつ構築し、木構造を組み立てていく手順を踏みます。

ルート要素を作成した後に子要素を追加していくことによって、柔軟に任意の構造を持ったデータを生成する仕組みです。

編集したXMLデータをファイルへ保存できますか?

解析や編集を行った後のデータは、ファイルとして出力して保存できます。標準モジュールでは木構造オブジェクトの書き出し用メソッドが用意されているのが特徴です。

出力時にエンコーディングやXML宣言の有無などを指定して保存を完了させます。これによって、別のシステムへ連携する際にも適切な形式でデータを受け渡せます。

※上記コンテンツの内容やソースコードはAIで確認・デバッグしておりますが、間違いやエラー、脆弱性などがある場合は、コメントよりご報告いただけますと幸いです。

ITやプログラミングに関するコラム


ITやプログラミングに関するニュース

ブログに戻る

コメントを残す

コメントは公開前に承認される必要があることにご注意ください。

企業・法人向けのIT・プログラミング・生成AI研修を探す、比較する - IT・プログラミングを知って学べるコネクトメディア CodeCampが提供するDX人材育成が可能なプログラミングやITが学べる公開講座 - IT・プログラミングを知って学べるコネクトメディア コードキャンプが提供する無料で学べるプログラミングスクール講座 - IT・プログラミングを知って学べるコネクトメディア コードキャンプDX人材育成研修 - IT・プログラミングを知って学べるコネクトメディア 3.5日の研修で、年間1,600時間の削減効果が見込まれる。東京きらぼしフィナンシャルグループのDX人材育成事例 - IT・プログラミングを知って学べるコネクトメディア 配属3ヶ月で30%の生産性向上を実現するいよぎんコンピュータサービスの新人研修に迫る - IT・プログラミングを知って学べるコネクトメディア 金融業界の業務効率化を加速するニッセイアセットマネジメントの生成AI×GAS活用研修事例 - IT・プログラミングを知って学べるコネクトメディア 【製造業のDX人材育成事例】デジタル人材の即戦力化を実現する、日本ガイシ株式会社の異動者向オンボーディング研修 - ITやプログラミングを知って学べるコネクトメディア フューチャーアーキテクト株式会社が実現した新入社員向けIT研修プログラムでタスクフォース制度が主体的な学びと成長を生み出す - IT・プログラミングを知って学べるコネクトメディア コードキャンプDX人材育成研修 - IT・プログラミングを知って学べるコネクトメディア コードキャンプIT・プログラミング研修事例/【IT新入社員研修】オンラインとオフラインの最適バランスを実現したFutureOneの導入事例 - IT・プログラミングを知って学べるコネクトメディア コードキャンプIT・プログラミング研修事例/【新入社員研修】柔軟なハイブリッド型Java研修で実現した新卒20名の成長と成果|サークレイス株式会社 - ITやプログラミングを知って学べるコネクトメディア コードキャンプIT・プログラミング研修事例/現場により近いところにデジタルを根付かせるDX基礎講座研修|株式会社ブリヂストン - ITやプログラミングを知って学べるコネクトメディア コードキャンプIT・プログラミング研修事例/業務の効率化・DX推進に向けたIT人材育成への第一歩|株式会社カナエ - ITやプログラミングを知って学べるコネクトメディア 企業・法人向けのIT・プログラミング研修 - ITやプログラミングを知って学べるコネクトメディア

新着記事

対象者別で探す

子供(小学生・中学生・高校生)向け
プログラミング教室検索する

子供(小学生・中学生・高校生)がロボットやプログラミング言語を学ぶことができるオフラインからオンラインスクールを検索、比較することが可能です。

子供(小学生・中学生・高校生)
プログラミング教室検索する

ITやプログラムなどの
最新情報を検索する

日々、新しいITやプログラミング言語の情報が流れていきますが、特定の情報を時系列でニュースやコラムを確認することができます。

ITやプログラムなどの
最新情報を検索する