PythonでXMLをパースする方法ガイド

PythonでElementTree、lxml、SAXなどのライブラリを使用してXMLをパースする方法を学び、データ処理プロジェクトを強化しましょう。
1 分読
How to parse XML in Python main blog image

拡張マークアップ言語(XML)は、構造化データの保存と交換に広く使用されるフォーマットです。XMLファイルは、設定ファイル、データ交換フォーマット、Webサービス応答、サイトマップなど、階層的なデータを表現するために一般的に使用されます。

PythonでのXMLファイルのパースは、Web APIから取得したデータの処理やウェブスクレイピングといった手動プロセスの自動化において特に一般的な作業です。

この記事では、PythonでXMLをパースするために使用できるライブラリについて学びます。これにはElementTreeモジュールlxmlライブラリminidomSimple API for XML (SAX)untangleが含まれます。

XMLファイルの主要概念

PythonでXMLをパースする方法を学ぶ前に、XMLスキーマ定義(XSD)とは何か、XMLファイルを構成する要素を理解する必要があります。この理解は、パースタスクに適したPythonライブラリを選択するのに役立ちます。

XSDは、XMLドキュメントで許可される構造、内容、データ型を定義するスキーマ仕様です。これは、事前定義されたルールセットに対してXMLファイルの構造と内容を検証するための構文として機能します。

XMLファイルには通常、名前空間ルート属性要素テキストコンテンツといった要素が含まれ、これらが構造化されたデータを表します。

  • 名前空間
  • roo
  • 属性
  • 要素
  • テキストコンテンツ

たとえば、Bright Dataのサイトマップは次のXML構造を持っています:

  • urlsetルート要素です。
  • <urlset xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd>urlset 要素固有の名前空間宣言であり、この宣言の規則がurlset 要素に適用されることを示します。この要素の下位にある全ての要素は、この名前空間で定義されたスキーマに準拠する必要があります。
  • url はルート要素の最初の子要素です。
  • locurl 要素の子要素です。

XSDとXMLファイル要素について少し理解が深まったところで、いくつかのライブラリを使ってXMLファイルをパースする方法を学びましょう。

PythonでXMLをパースする様々な方法

デモ目的で、このチュートリアルではXML形式で提供されているBright Dataサイトマップを使用します。以下の例では、Pythonのrequestsライブラリを使用してBright Dataサイトマップの内容を取得します。

Pythonのrequestsライブラリは組み込みではないため、先にインストールする必要があります。以下のコマンドでインストールできます:

pip install requests

ElementTree

ElementTree XML APIは、PythonでXMLデータをパース・作成するためのシンプルで直感的なAPIを提供します。Python標準ライブラリの組み込みモジュールであるため、明示的なインストールは不要です。

例えば、findall()メソッドを使用してルートから全てのurl要素を検索し、loc要素のテキスト値を出力できます。以下のように実行します:

import xml.etree.ElementTree as ET
import requests

url = 'https://brightdata.com/post-sitemap.xml'

response = requests.get(url)
if response.status_code == 200:
   
    root = ET.fromstring(response.content)

    for url_element in root.findall('.//{http://www.sitemaps.org/schemas/sitemap/0.9}url'):
        loc_element = url_element.find('{http://www.sitemaps.org/schemas/sitemap/0.9}loc')
        if loc_element is not None:
            print(loc_element.text)
else:
    print("URLからXMLファイルを取得できませんでした。")

サイトマップ内の全URLが出力に表示されます:

https://brightdata.com/case-studies/powerdrop-case-study
https://brightdata.com/case-studies/ブランド保護からあらゆる角度でアプローチ
https://brightdata.com/case-studies/taking-control-of-the-digital-shelf-with-public-online-data
https://brightdata.com/case-studies/the-seo-transformation
https://brightdata.com/case-studies/data-driven-automated-e-commerce-tools
https://brightdata.com/case-studies/highly-targeted-influencer-marketing
https://brightdata.com/case-studies/data-driven-products-for-smarter-shopping-solutions
https://brightdata.com/case-studies/workplace-diversity-facilitated-by-online-data
https://brightdata.com/case-studies/alternative-travel-solutions-enabled-by-online-data-railofy
https://brightdata.com/case-studies/data-intensive-analytical-solutions
https://brightdata.com/case-studies/canopy-advantage-solutions
https://brightdata.com/case-studies/seamless-digital-automations

ElementTreeはPythonでXMLデータをパースするユーザーフレンドリーな方法であり、XML構造のナビゲーションや操作を容易にする直感的なAPIを備えています。ただしElementTreeには限界もあり、スキーマ検証に対する堅牢なサポートが不足しており、パース前にスキーマ仕様への厳密な準拠を保証する必要がある場合には理想的ではありません。

RSSフィードを読み込む小さなスクリプトであれば、ElementTreeの使いやすいAPIは各フィードアイテムからタイトル、説明、リンクを抽出するのに有用なツールとなります。しかし、複雑な検証や大規模なファイルを扱うユースケースでは、lxmlのような別のライブラリを検討した方が良いでしょう。

lxml

lxmlはPythonでXMLファイルをパースするための高速で使いやすく、機能豊富なAPIです。ただし、Pythonのプリビルドライブラリではありません。一部のLinuxやMacプラットフォームではlxmlパッケージが既にインストールされていますが、他のプラットフォームでは手動インストールが必要です。

lxmlはPyPI経由で配布されており、以下のpipコマンドでインストールできます:

pip install lxml

インストール後、lxmlではfind()findall()findtext()get()get_element_by_id()などの各種APIメソッドを用いてXMLファイルをパースできます。

例えば、findall()メソッドを使用してurl要素を反復処理し、そのloc要素(url要素の子要素)を見つけ、以下のコードで位置情報を出力できます:

from lxml import etree
import requests

url = "https://brightdata.com/post-sitemap.xml"

response = requests.get(url)
if response.status_code == 200:

    root = etree.fromstring(response.content)
    

    for url in root.findall(".//{http://www.sitemaps.org/schemas/sitemap/0.9}url"):
        loc = url.find("{http://www.sitemaps.org/schemas/sitemap/0.9}loc").text.strip()
        print(loc)
else:
    print("URLからXMLファイルを取得できませんでした。")

出力にはサイトマップ内で見つかったすべてのURLが表示されます:

https://brightdata.com/case-studies/powerdrop-case-study
https://brightdata.com/case-studies/ブランド保護からあらゆる角度でアプローチ
https://brightdata.com/case-studies/taking-control-of-the-digital-shelf-with-public-online-data
https://brightdata.com/case-studies/the-seo-transformation
https://brightdata.com/case-studies/data-driven-automated-e-commerce-tools
https://brightdata.com/case-studies/highly-targeted-influencer-marketing
https://brightdata.com/case-studies/data-driven-products-for-smarter-shopping-solutions
https://brightdata.com/case-studies/workplace-diversity-facilitated-by-online-data
https://brightdata.com/case-studies/alternative-travel-solutions-enabled-by-online-data-railofy
https://brightdata.com/case-studies/data-intensive-analytical-solutions
https://brightdata.com/case-studies/canopy-advantage-solutions
https://brightdata.com/case-studies/seamless-digital-automations

これまで、要素を検索してその値を出力する方法を学びました。次に、XMLをパースする前にスキーマ検証について見ていきましょう。このプロセスにより、ファイルがスキーマで定義された指定構造に準拠していることが保証されます。

サイトマップのXSDは次のようになります:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://www.sitemaps.org/schemas/sitemap/0.9"
           xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
           elementFormDefault="qualified"
           xmlns:xhtml="http://www.w3.org/1999/xhtml">

  
  <xs:element name="urlset">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="url" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  
  <xs:element name="url">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="loc" type="xs:anyURI"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

</xs:schema>

サイトマップをスキーマ検証に使用するには、手動でコピーし、schema.xsd という名前のファイルを作成してください。

このXSDを使用してXMLファイルを検証するには、以下のコードを使用します:


from lxml import etree

import requests

url = "https://brightdata.com/post-sitemap.xml"

response = requests.get(url)

if response.status_code == 200:

    root = etree.fromstring(response.content)

    try:
        print("スキーマ検証:")
        schema_doc = etree.parse("schema.xsd")  
        schema = etree.XMLSchema(schema_doc)  
        schema.assertValid(root)  
        print("XML is valid according to the schema.")
    except etree.DocumentInvalid as e:
        print("XML validation error:", e)

ここでは、etree.parse()メソッドを使用して XSD ファイルをパースします。次に、パースされた XSD ドキュメントの内容を使用して XML スキーマを作成します。最後に、assertValid()メソッドを使用して XML ルートドキュメントを XML スキーマに対して検証します。スキーマ検証が成功した場合、出力には「XML はスキーマに従って有効です」のようなメッセージが含まれます。そうでない場合、DocumentInvalid例外が発生します。

出力例:

 スキーマ検証:
    XML はスキーマに従って有効です。

次に、xpathメソッドを使用して要素のパスから要素を検索するXMLファイルを読み込みます。

xpath()メソッドを使用して要素を読み取るには、以下のコードを使用します:

from lxml import etree

import requests

url = "https://brightdata.com/post-sitemap.xml"
response = requests.get(url)

if response.status_code == 200:
   
    root = etree.fromstring(response.content)
    
    print("XPath Support:")
    root = etree.fromstring(response.content)

    namespaces = {"ns": "http://www.sitemaps.org/schemas/sitemap/0.9"}
    for url in root.xpath(".//ns:url/ns:loc", namespaces=namespaces):
        print(url.text.strip())

このコードでは、名前空間プレフィックスns を登録し、名前空間 URIhttp://www.sitemaps.org/schemas/sitemap/0.9 にマッピングします。XPath 式では、ns プレフィックスを使用して名前空間内の要素を指定します。最後に、式.//ns:url/ns:loc は、名前空間内のurl 要素の子であるすべてのloc 要素を選択します。

出力結果は次のようになります:

XPathサポート:

https://brightdata.com/case-studies/powerdrop-case-study
https://brightdata.com/case-studies/ブランド保護からあらゆる角度でアプローチ
https://brightdata.com/case-studies/taking-control-of-the-digital-shelf-with-public-online-data
https://brightdata.com/case-studies/the-seo-transformation
https://brightdata.com/case-studies/data-driven-automated-e-commerce-tools
https://brightdata.com/case-studies/highly-targeted-influencer-marketing
https://brightdata.com/case-studies/data-driven-products-for-smarter-shopping-solutions
https://brightdata.com/case-studies/workplace-diversity-facilitated-by-online-data
https://brightdata.com/case-studies/alternative-travel-solutions-enabled-by-online-data-railofy
https://brightdata.com/case-studies/data-intensive-analytical-solutions
https://brightdata.com/case-studies/canopy-advantage-solutions
https://brightdata.com/case-studies/seamless-digital-automations

ご覧の通り、find() およびfindall() メソッドはxpath メソッドよりも高速です。これは、xpath が結果をすべてメモリに収集してから返すためです。XPath クエリを使用する特別な理由がない限り、find() メソッドの使用が推奨されます。

lxmlはXMLおよびHTMLのパース・操作に強力な機能を提供します。XPath式を用いた複雑なクエリをサポートし、スキーマに対するドキュメントの検証を行い、さらにはeXtensible Stylesheet Language Transformations(XSLT)も許可します。これにより、パフォーマンスと高度な機能が重要なシナリオに最適です。ただし、lxmlはPythonのコアパッケージの一部ではないため、別途インストールが必要であることに留意してください。

大規模または複雑なXMLデータを扱い、高いパフォーマンスと高度な操作の両方が必要な場合は、lxmlの使用を検討すべきです。例えば、XML形式の金融データフィードを処理する場合、株価などの特定要素を抽出するためにXPath式を使用し、正確性を確保するために金融スキーマに対してデータを検証し、さらに分析のためにXSLTを使用してデータを変換する必要が生じる可能性があります。

minidom

minidomはPython標準ライブラリに含まれる軽量でシンプルなXMLパースライブラリです。lxmlによるパースほど機能豊富でも効率的でもありませんが、PythonでXMLデータをパース・操作する簡便な手段を提供します。

DOMオブジェクトで利用可能な各種メソッドを使用して要素にアクセスできます。例えば、getElementsByTagName()メソッドを使用すると、タグ名を使用して要素の値を取得できます。

以下の例は、minidomライブラリを使用してXMLファイルをパースし、タグ名で要素を取得する方法を示しています:

import requests
import xml.dom.minidom

url = "https://brightdata.com/post-sitemap.xml"

response = requests.get(url)
if response.status_code == 200:
    dom = xml.dom.minidom.parseString(response.content)
    
    urlset = dom.getElementsByTagName("urlset")[0]
    for url in urlset.getElementsByTagName("url"):
        loc = url.getElementsByTagName("loc")[0].firstChild.nodeValue.strip()
        print(loc)
else:
    print("URLからXMLファイルを取得できませんでした。")

出力結果は次のようになります:

https://brightdata.com/case-studies/powerdrop-case-study
https://brightdata.com/case-studies/ブランド保護からあらゆる角度でアプローチ
https://brightdata.com/case-studies/taking-control-of-the-digital-shelf-with-public-online-data
https://brightdata.com/case-studies/the-seo-transformation
https://brightdata.com/case-studies/data-driven-automated-e-commerce-tools
https://brightdata.com/case-studies/highly-targeted-influencer-marketing
https://brightdata.com/case-studies/data-driven-products-for-smarter-shopping-solutions
https://brightdata.com/case-studies/workplace-diversity-facilitated-by-online-data
https://brightdata.com/case-studies/alternative-travel-solutions-enabled-by-online-data-railofy
https://brightdata.com/case-studies/data-intensive-analytical-solutions
https://brightdata.com/case-studies/canopy-advantage-solutions
https://brightdata.com/case-studies/seamless-digital-automations

minidomはXMLデータをDOMツリーとして表現することで処理します。このツリー構造によりデータのナビゲーションや操作が容易になり、シンプルなXML構造の読み取り、変更、構築といった基本的なタスクに最適です。

プログラムでXMLファイルからデフォルト設定を読み込む場合、minidomのDOMアプローチにより、子ノードや属性の検索といったメソッドを使ってXMLファイル内の特定設定に簡単にアクセスできます。minidomを使えば、font-sizeノードなどの特定設定をXMLファイルから容易に取得し、その値をアプリケーション内で利用できます。

SAXパーサー

SAXパーサーはPythonにおけるイベント駆動型のXMLパース手法であり、XML文書を順次処理し、文書の各部分に出会うたびにイベントを生成します。メモリ内にXML文書全体を表すツリー構造を構築するDOMベースのパーサーとは異なり、SAXパーサーは文書の完全な表現を構築しません。代わりに、文書を解析する過程で開始タグ、終了タグ、テキストコンテンツなどのイベントを発行します。

SAXパーサーは、メモリ効率が懸念される大規模なXMLファイルやストリームの処理に適しています。これは、文書全体をメモリに読み込まずにXMLデータを増分的に処理するためです。

SAXパーサーを使用する際には、パーサーが発行するstartElementやendElementといった特定のXMLイベントに対応するイベントハンドラを定義する必要があります。これらのイベントハンドラは、XML文書の構造や内容に基づいてアクションを実行するようカスタマイズ可能です。

以下の例は、startElementおよびendElementイベント 定義し、サイトマップファイルからURL情報を取得するSAXパーサーによるXMLファイルのパース方法を示しています:

import requests
import xml.sax.handler
from io import BytesIO

class MyContentHandler(xml.sax.handler.ContentHandler):
    def __init__(self):
        self.in_url = False
        self.in_loc = False
        self.url = ""

    def startElement(self, name, attrs):
        if name == "url":
            self.in_url = True
        elif name == "loc" and self.in_url:
            self.in_loc = True

    def characters(self, content):
        if self.in_loc:
            self.url += content

    def endElement(self, name):
        if name == "url":
            print(self.url.strip())
            self.url = ""
            self.in_url = False
        elif name == "loc":
            self.in_loc = False

url = "https://brightdata.com/post-sitemap.xml"

response = requests.get(url)
if response.status_code == 200:

    xml_content = BytesIO(response.content)
    
    content_handler = MyContentHandler()
    parser = xml.sax.make_parser()
    parser.setContentHandler(content_handler)
    parser.parse(xml_content)
else:
    print("URLからXMLファイルを取得できませんでした。")

出力は次のようになります:

https://brightdata.com/case-studies/powerdrop-case-study
https://brightdata.com/case-studies/ブランド保護からあらゆる角度でアプローチ
https://brightdata.com/case-studies/taking-control-of-the-digital-shelf-with-public-online-data
https://brightdata.com/case-studies/the-seo-transformation
https://brightdata.com/case-studies/data-driven-automated-e-commerce-tools
https://brightdata.com/case-studies/highly-targeted-influencer-marketing
https://brightdata.com/case-studies/data-driven-products-for-smarter-shopping-solutions
https://brightdata.com/case-studies/workplace-diversity-facilitated-by-online-data
https://brightdata.com/case-studies/alternative-travel-solutions-enabled-by-online-data-railofy
https://brightdata.com/case-studies/data-intensive-analytical-solutions
https://brightdata.com/case-studies/canopy-advantage-solutions
https://brightdata.com/case-studies/seamless-digital-automations

他のパーサーがファイル全体をメモリに読み込むのとは異なり、SAXはファイルをインクリメンタルに処理するため、メモリを節約しパフォーマンスを向上させます。ただし、SAXでは各データセグメントを動的に管理するため、より多くのコードを記述する必要があります。さらに、後からデータの特定部分を再訪して分析することはできません。

大規模なXMLファイル(例:様々なイベントを含むログファイル)をスキャンして特定の情報(例:エラーメッセージ)を抽出する必要がある場合、SAXはファイル内を効率的に探索するのに役立ちます。しかし、分析において異なるデータセグメント間の関係性を理解する必要がある場合、SAXは最適な選択肢ではない可能性があります。

untangle

untangleは、Python用の軽量XMLパースライブラリであり、XMLドキュメントからのデータ抽出プロセスを簡素化します。階層構造をナビゲートする必要がある従来のXMLパーサーとは異なり、untangleではXML要素や属性をPythonオブジェクトとして直接アクセスできます。

untangleを使用すると、XMLドキュメントをネストされたPython辞書に変換できます。XML要素は辞書のキーとして表現され、その属性とテキストコンテンツは対応する値として格納されます。このアプローチにより、Pythonデータ構造を用いてXMLデータに容易にアクセスし操作できます。

untangleはPythonのデフォルトでは利用できず、以下のPyPIコマンドでインストールする必要があります:

pip install untangle

以下の例は、untangleライブラリを使用してXMLファイルをパースし、XML要素にアクセスする方法を示しています:

import untangle
import requests

url = "https://brightdata.com/post-sitemap.xml"

response = requests.get(url)

if response.status_code == 200:
  
    obj = untangle.parse(response.text)
    
    for url in obj.urlset.url:
        print(url.loc.cdata.strip())
else:
    print("Failed to retrieve XML file from the URL.")

出力例:

https://brightdata.com/case-studies/powerdrop-case-study
https://brightdata.com/case-studies/ブランド保護からあらゆる角度でアプローチ
https://brightdata.com/case-studies/taking-control-of-the-digital-shelf-with-public-online-data
https://brightdata.com/case-studies/the-seo-transformation
https://brightdata.com/case-studies/data-driven-automated-e-commerce-tools
https://brightdata.com/case-studies/highly-targeted-influencer-marketing
https://brightdata.com/case-studies/data-driven-products-for-smarter-shopping-solutions
https://brightdata.com/case-studies/workplace-diversity-facilitated-by-online-data
https://brightdata.com/case-studies/alternative-travel-solutions-enabled-by-online-data-railofy
https://brightdata.com/case-studies/data-intensive-analytical-solutions
https://brightdata.com/case-studies/canopy-advantage-solutions
https://brightdata.com/case-studies/seamless-digital-automations

untangleはPythonでXMLデータを扱うためのユーザーフレンドリーなアプローチを提供します。明確な構文でパースプロセスを簡素化し、XML構造を自動的に使いやすいPythonオブジェクトに変換するため、複雑なナビゲーション技術が不要になります。ただし、untangleはPythonのコアパッケージに含まれていないため、別途インストールが必要である点に留意してください。

整ったXMLファイルを素早くPythonオブジェクトに変換し、さらに処理する必要がある場合にuntangleの使用を検討すべきです。例えば、XML形式で気象データをダウンロードするプログラムがある場合、untangleはXMLをパースし、現在の気温、湿度、予報を表すPythonオブジェクトを作成するのに適しています。これらのオブジェクトは、アプリケーション内で簡単に操作および表示できます。

まとめ

本記事では、XMLファイル全般とPythonにおけるXMLファイルのパースの様々な手法について解説しました。

小さな設定ファイルの処理、大規模なWebサービス応答のパース、膨大なサイトマップからのデータ抽出など、PythonはXMLパースタスクを自動化・効率化する多彩なライブラリを提供します。ただし、requestsライブラリでプロキシ管理なしにWebからファイルにアクセスする場合、クォータ例外やスロットリングの問題が発生する可能性があります。Bright Dataは受賞歴のあるプロキシネットワークであり、信頼性と効率性に優れたプロキシソリューションを提供し、シームレスなデータ取得とパースを保証します。 Bright Dataを利用すれば、制限や中断を気にせずXMLパースタスクに取り組めます。詳細については営業チームまでお問い合わせください。

スクレイピングやパースプロセス全体をスキップしたいですか?当社のデータセットマーケットプレイスを無料で試してみてください!