ななぶろ

-お役立ち情報を気楽に紹介するブログ-

PythonとXML:データ交換の基礎から実践まで-練習問題10問付き

www.amazon.co.jp

PythonとXML:データ交換の基礎から実践まで - 練習問題10問付き

Pythonは汎用性の高いプログラミング言語であり、様々なタスクに対応できます。その中でも、異なるシステム間でデータをやり取りする際に重要な役割を果たすのがXML(Extensible Markup Language)です。本記事では、XMLの基本からPythonでの扱い方までを解説し、理解度を確認するための練習問題10問を用意しました。

1. XMLとは? - データ構造とマークアップ言語

XMLは、データを構造化して表現するためのマークアップ言語です。HTMLと似ていますが、HTMLが主にWebページの表示に特化しているのに対し、XMLはデータの保存や転送を目的としています。

XMLの特徴:

  • 自己記述性: タグ名によってデータの内容が明確になるため、人間にも機械にも理解しやすい形式でデータを表現できます。
  • 拡張性: 独自のタグを作成できるため、様々な種類のデータを柔軟に表現できます。
  • プラットフォーム非依存性: テキストベースであるため、異なるOSや環境間でデータの互換性を保ちやすいです。

XMLの例:

<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
  <book category="cooking">
    <title lang="en">Everyday Italian</title>
    <author>Giada De Laurentiis</author>
    <year>2005</year>
    <price>30.00</price>
  </book>
  <book category="children">
    <title lang="de">Harry Potter und der Stein der Weisen</title>
    <author>J.K. Rowling</author>
    <year>2005</year>
    <price>29.99</price>
  </book>
</bookstore>

この例では、bookstoreがルート要素であり、その中に複数のbook要素が含まれています。各book要素は、title, author, year, priceといった子要素を持っています。属性(例:category="cooking")も使用されており、データの追加情報を提供しています。

Explanation in English:

XML (Extensible Markup Language) is a markup language designed for structuring and representing data. While HTML primarily focuses on displaying web pages, XML's purpose is to store and transport data.

Key Features of XML:

  • Self-Describing: The tag names clearly indicate the content they contain, making it easy for both humans and machines to understand the data structure.
  • Extensible: You can create custom tags, allowing you to represent a wide variety of data types flexibly.
  • Platform Independent: Being text-based, XML ensures compatibility across different operating systems and environments.

The example provided demonstrates a simple bookstore catalog with books categorized as "cooking" or "children." Each book has attributes like title (with language), author, year, and price. Attributes provide additional information about the data elements.

2. PythonとXML - 標準ライブラリと外部ライブラリ

PythonでXMLを扱うには、標準ライブラリのxml.etree.ElementTreeを使用する方法と、より高機能な外部ライブラリ(例:lxml)を使用する方法があります。

  • xml.etree.ElementTree: Pythonに標準で含まれており、基本的なXML操作が可能です。シンプルで使いやすいですが、大規模なXMLファイルの処理には向いていません。
  • lxml: C言語で実装されており、高速かつ高機能です。XPathによる要素の検索や、XML Schemaによる検証など、高度な機能をサポートしています。

Explanation in English:

Python offers two primary approaches for working with XML: using the built-in xml.etree.ElementTree library and leveraging external libraries like lxml.

  • xml.etree.ElementTree: This is a standard Python library providing basic XML manipulation capabilities. It's simple to use but may not be ideal for processing large XML files due to performance limitations.
  • lxml: Written in C, lxml offers significantly improved speed and functionality. It supports advanced features like XPath queries and XML Schema validation.

3. XMLの解析 (Parsing) - ElementTreeを使った例

XMLファイルをPythonで読み込む(解析する)には、ElementTreeparse()メソッドを使用します。

import xml.etree.ElementTree as ET

tree = ET.parse('books.xml')  # books.xmlはXMLファイル名
root = tree.getroot() # ルート要素を取得

for book in root.findall('book'):
    title = book.find('title').text
    author = book.find('author').text
    print(f"Title: {title}, Author: {author}")

このコードは、books.xmlファイルを解析し、各本のタイトルと著者名を表示します。 findall()メソッドで指定したタグを持つ要素をすべて検索し、find()メソッドで子要素を検索しています。 .text属性で要素のテキスト値を取得できます。

Explanation in English:

To read (parse) an XML file into Python, you can use the parse() method from the xml.etree.ElementTree library. The following code snippet demonstrates how to parse a 'books.xml' file and extract the title and author for each book:

import xml.etree.ElementTree as ET

tree = ET.parse('books.xml') # Parses the XML file
root = tree.getroot() # Gets the root element of the XML document

for book in root.findall('book'): # Finds all 'book' elements under the root
    title = book.find('title').text # Finds the 'title' element within each 'book' and extracts its text content
    author = book.find('author').text # Finds the 'author' element within each 'book' and extracts its text content
    print(f"Title: {title}, Author: {author}") # Prints the title and author for each book

findall() searches for all elements with a specified tag name, while find() locates the first child element with a given tag. The .text attribute retrieves the textual content of an element.

4. XMLの作成 (Building) - ElementTreeを使った例

XMLファイルをPythonから作成するには、Elementクラスを使って要素を作成し、それを組み立てていきます。

import xml.etree.ElementTree as ET

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

book1 = ET.SubElement(root, 'book', {'category': 'cooking'})
title1 = ET.SubElement(book1, 'title')
title1.text = 'Everyday Italian'
author1 = ET.SubElement(book1, 'author')
author1.text = 'Giada De Laurentiis'

book2 = ET.SubElement(root, 'book', {'category': 'children'})
title2 = ET.SubElement(book2, 'title')
title2.text = 'Harry Potter und der Stein der Weisen'
author2 = ET.SubElement(book2, 'author')
author2.text = 'J.K. Rowling'

tree = ET.ElementTree(root) # ElementTreeオブジェクトを作成
tree.write('new_books.xml', encoding='utf-8', xml_declaration=True) # XMLファイルに書き出し

このコードは、bookstoreというルート要素を持ち、2冊の本を含むXMLファイルを生成します。 ET.SubElement()メソッドで子要素を作成し、属性も設定できます。 tree.write()メソッドでXMLファイルに書き出します。 xml_declaration=Trueを指定すると、XML宣言 (<?xml version="1.0" encoding="UTF-8"?>) が付加されます。

Explanation in English:

Creating an XML file from Python involves constructing elements using the Element class and assembling them into a tree structure. Here's how you can create a new XML file with a 'bookstore' root element containing two books:

import xml.etree.ElementTree as ET

root = ET.Element('bookstore') # Creates the root element

book1 = ET.SubElement(root, 'book', {'category': 'cooking'}) # Creates a 'book' subelement with an attribute
title1 = ET.SubElement(book1, 'title') # Creates a 'title' subelement within 'book1'
title1.text = 'Everyday Italian' # Sets the text content of the 'title' element
author1 = ET.SubElement(book1, 'author') # Creates an 'author' subelement within 'book1'
author1.text = 'Giada De Laurentiis' # Sets the text content of the 'author' element

book2 = ET.SubElement(root, 'book', {'category': 'children'}) # Creates another 'book' subelement with an attribute
title2 = ET.SubElement(book2, 'title') # Creates a 'title' subelement within 'book2'
title2.text = 'Harry Potter und der Stein der Weisen' # Sets the text content of the 'title' element
author2 = ET.SubElement(book2, 'author') # Creates an 'author' subelement within 'book2'
author2.text = 'J.K. Rowling' # Sets the text content of the 'author' element

tree = ET.ElementTree(root) # Creates an ElementTree object from the root element
tree.write('new_books.xml', encoding='utf-8', xml_declaration=True) # Writes the XML tree to a file named 'new_books.xml'

ET.SubElement() creates child elements, and you can also set attributes directly during creation. The tree.write() method writes the XML structure to a file. Setting xml_declaration=True adds the XML declaration at the beginning of the file.

5. XPath - XML要素の効率的な検索

XPathは、XMLドキュメント内の要素を特定するための強力な言語です。 lxmlライブラリを使用すると、XPathによる要素の検索が容易になります。

from lxml import etree

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

# XPathでtitleタグを持つ要素をすべて検索
titles = root.xpath('//title') # //はドキュメント内のどこでも検索

for title in titles:
    print(title.text)

# 特定の属性を持つbook要素を検索
cooking_books = root.xpath('//book[@category="cooking"]')

for book in cooking_books:
    print(book.find('title').text)

この例では、//titleというXPath式を使って、ドキュメント内のすべてのtitle要素を検索しています。 [@category="cooking"]は、category属性が"cooking"であるbook要素を検索します。

Explanation in English:

XPath is a powerful language for navigating and selecting elements within an XML document. The lxml library makes XPath queries much easier to implement.

from lxml import etree

tree = etree.parse('books.xml') # Parses the XML file
root = tree.getroot() # Gets the root element of the XML document

# Uses XPath to find all 'title' elements in the document
titles = root.xpath('//title') # '//' searches anywhere in the document for a 'title' element

for title in titles:
    print(title.text) # Prints the text content of each 'title' element

# Uses XPath to find all 'book' elements with the attribute 'category' set to 'cooking'
cooking_books = root.xpath('//book[@category="cooking"]') # '[@category="cooking"]' filters for books where category is "cooking"

for book in cooking_books:
    print(book.find('title').text) # Prints the title of each cooking book

The // prefix in XPath indicates a descendant search, meaning it searches anywhere within the document. The predicate [@category="cooking"] filters elements based on their attribute values.

6. XML Schema (XSD) - XMLデータの検証

XML Schema(XSD)は、XMLドキュメントの構造とデータ型を定義するためのスキーマ言語です。 XSDファイルを使ってXMLドキュメントを検証することで、データの整合性を保つことができます。 lxmlライブラリを使用すると、XSDによる検証が可能です。

from lxml import etree

xsd_doc = etree.parse('books.xsd') # XSDファイルを解析
schema = etree.XMLSchema(xsd_doc)

xml_doc = etree.parse('books.xml') # XMLファイルを解析

try:
    schema.assertValid(xml_doc)
    print("XML document is valid against the schema.")
except etree.DocumentInvalid as e:
    print(f"XML document is invalid: {e}")

このコードは、books.xsdファイルで定義されたスキーマに基づいて、books.xmlファイルを検証します。 assertValid()メソッドを使って検証を行い、エラーが発生した場合は例外をキャッチしてメッセージを表示します。

Explanation in English:

XML Schema (XSD) is a schema language used to define the structure and data types of an XML document. By validating an XML document against an XSD file, you can ensure data consistency and integrity. The lxml library provides tools for performing XSD validation.

from lxml import etree

xsd_doc = etree.parse('books.xsd') # Parses the XSD file
schema = etree.XMLSchema(xsd_doc) # Creates an XML Schema object from the parsed XSD document

xml_doc = etree.parse('books.xml') # Parses the XML file

try:
    schema.assertValid(xml_doc) # Validates the XML document against the schema
    print("XML document is valid against the schema.")
except etree.DocumentInvalid as e:
    print(f"XML document is invalid: {e}") # Prints an error message if validation fails

The assertValid() method performs the validation, and a DocumentInvalid exception is raised if any errors are found during the process.

7. 名前空間 (Namespaces) - XMLの曖昧性解消

XMLの名前空間は、異なるXMLドキュメント間で同じタグ名が衝突するのを防ぐための仕組みです。 名前空間を使用すると、タグ名を一意に識別できます。

from lxml import etree

# 名前空間を定義
ns = {'bookstore': 'http://example.com/books', 'b': 'http://www.w3.org/2005/Atom'}

tree = etree.parse('books_with_namespaces.xml') # XMLファイルを解析
root = tree.getroot()

# 名前空間を指定して要素を検索
titles = root.xpath('//b:title', namespaces=ns) # b:は名前空間のプレフィックス

for title in titles:
    print(title.text)

この例では、books_with_namespaces.xmlファイルに名前空間が定義されています。 XPath式で名前空間を指定することで、特定の名前空間内の要素を正確に検索できます。

Explanation in English:

XML namespaces are a mechanism to prevent tag name collisions when different XML documents use the same tag names. Namespaces provide a way to uniquely identify tags.

from lxml import etree

# Defines the namespaces
ns = {'bookstore': 'http://example.com/books', 'b': 'http://www.w3.org/2005/Atom'}

tree = etree.parse('books_with_namespaces.xml') # Parses the XML file
root = tree.getroot()

# Uses XPath to find all 'title' elements within the 'b' namespace
titles = root.xpath('//b:title', namespaces=ns) # 'b:' specifies the prefix for the 'b' namespace

for title in titles:
    print(title.text) # Prints the text content of each 'title' element

The namespaces argument in the XPath expression allows you to specify which namespace a tag belongs to, ensuring accurate selection even when multiple namespaces are present.

練習問題 - PythonとXMLの理解度チェック

以下の練習問題を解くことで、PythonとXMLに関する知識を深めましょう。

  1. XMLファイルの読み込み: books.xmlファイルからすべての本のタイトルと著者名を抽出し、リストとして表示するプログラムを作成してください。

    • Solution: Use ET.parse() to parse the XML file, then iterate through each 'book' element and extract the 'title' and 'author' text values.
  2. XMLファイルの作成: ユーザーに本のタイトル、著者名、価格を入力させ、それらの情報を含む新しいXMLファイルを生成するプログラムを作成してください。

    • Solution: Use ET.Element() to create elements, ET.SubElement() to add child elements, and tree.write() to save the XML file.
  3. XPathによる検索: books.xmlファイルから、価格が30ドル以上の本をすべて検索し、そのタイトルと価格を表示するプログラムを作成してください。

    • Solution: Use XPath with a predicate like '//book[@price > 30]'.
  4. XML Schemaの検証: books.xsdファイルで定義されたスキーマに基づいて、books.xmlファイルを検証するプログラムを作成してください。

    • Solution: Parse both the XSD and XML files, then use schema.assertValid() to validate the XML against the schema.
  5. 名前空間の使用: 名前空間が定義されているXMLファイルから、特定の名前空間内の要素をXPathを使って検索するプログラムを作成してください。

    • Solution: Define a namespace dictionary and pass it as the namespaces argument in the XPath expression.
  6. 属性の追加: 既存のXMLファイルに新しい本を追加し、その本のカテゴリー属性を設定するプログラムを作成してください。

    • Solution: Parse the XML file, add a new 'book' element with the desired category attribute using ET.SubElement().
  7. 要素の削除: XMLファイルから指定された本の著者名を削除するプログラムを作成してください。

    • Solution: Parse the XML file, locate the specific book by title or other criteria, and remove the 'author' element.
  8. XMLデータの変換: XMLファイルを読み込み、データをJSON形式に変換して出力するプログラムを作成してください。(jsonライブラリを使用)

    • Solution: Parse the XML file using ElementTree, then convert the resulting tree structure into a Python dictionary, and finally use json.dumps() to serialize the dictionary into JSON format.
  9. XMLファイルの結合: 複数のXMLファイルを読み込み、それらを1つのXMLファイルに結合するプログラムを作成してください。

    • Solution: Parse each XML file, extract its root element, and append those elements to a new root element in a single ElementTree. Then write the combined tree to a new XML file.
  10. エラーハンドリング: XMLファイルの解析中に発生する可能性のあるエラー(例:ファイルが存在しない、XMLの形式が不正)を適切に処理するプログラムを作成してください。

    • Solution: Wrap the parsing and validation code in try...except blocks to catch exceptions like FileNotFoundError or xml.etree.ElementTree.ParseError. Provide informative error messages to the user.

まとめ

本記事では、PythonとXMLの関係について、基本的な概念から実践的な応用まで幅広く解説しました。 XMLはデータ交換において重要な役割を果たしており、PythonでXMLを扱うことで、様々なシステムとの連携が可能になります。 練習問題を解くことで、より深く理解を深め、実用的なスキルを習得してください。

参照先:

このブログ記事が、PythonとXMLの学習の一助となれば幸いです。