PythonでBeautifulSoupを使ったXMLのパース

XML(Extensible Markup Language)は、データの構造化方法として人気のあるマークアップ言語である。

データ転送(シリアル化されたオブジェクトを表す)や設定ファイルで使用されています。

JSONの人気が高まっているにもかかわらず、Android開発のマニフェストファイル、Java/Mavenのビルドツール、Web上のSOAP APIなどでXMLを見かけることがあります。

したがって、XMLの解析は開発者がやらなければならない一般的なタスクです。

Pythonでは、2つのライブラリを活用することで、XMLを読み、パースすることができます。

BeautifulSoupとLXMLです。

このガイドでは、XMLの読み方について説明します。

このガイドでは、BeautifulSoupとLXMLを使ってXMLファイルからデータを抽出・パースし、Pandasを使って結果を保存する方法を見ていきます。

LXMLとBeautifulSoupのセットアップ

まず、両ライブラリをインストールする必要があります。

ワークスペースに新しいフォルダを作成し、仮想環境を構築して、ライブラリをインストールします。

$ mkdir xml_parsing_tutorial
$ cd xml_parsing_tutorial
$ python3 -m venv env # Create a virtual environment for this project
$ . env/bin/activate # Activate the virtual environment
$ pip install lxml beautifulsoup4 # Install both Python packages


これで、すべてのセットアップが完了しましたので、パースを行ってみましょう。

lxml と BeautifulSoup による XML のパース

パースは常に基礎となるファイルとそれが使用する構造に依存するので、すべてのファイルに対する単一の銀の弾丸はありません。

BeautifulSoupはそれらを自動的に解析しますが、基礎となる要素はタスクに依存します。

したがって、実践的なアプローチでパージングを学ぶことが最善です。

<?xml version="1.0" encoding="UTF-8"?
<teachers
<teacher
<nameSam Davies</name
<age35</age
<subjectMaths</subject
</teacher
<teacher
<nameCassie Stone</name
<age24</age
<subjectScience</subject
</teacher
<teacher
<nameDerek Brandon</name
<age32</age
<subjectHistory</subject
</teacher
</teachers


タグはXML文書のルートを示し、タグはの子またはサブ要素で、一人の人に関する情報を持ちます。

name, <age, <subject<teacher タグの子であり、<teachers タグの孫にあたります。

上のサンプル文書の最初の行、<?xml version="1.0" encoding="UTF-8"? は、XML prolog と呼ばれるものです。

XML文書の中にXMLプロローグを含めることは完全に任意ですが、XMLファイルの先頭には必ずこの行が含まれます。

となります。

上のXMLプロローグは、使用するXMLのバージョンと文字エンコーディングの種類を示します。

この場合、XML文書内の文字は、UTF-8でエンコードされています。

XMLファイルの構造を理解したところで、それを解析してみましょう。

作業ディレクトリに teachers.py という新しいファイルを作成し、BeautifulSoup ライブラリをインポートしてください。

from bs4 import BeautifulSoup


注意: お気づきかもしれませんが、lxmlをインポートしていません! BeautifulSoupをインポートすることで、LXMLは自動的に統合されるので、別途インポートする必要はありませんが、BeautifulSoupの一部としてインストールされているわけではありません。

では、作成したXMLファイルの内容を読み込んで、soupという変数に格納し、パージングを始めてみましょう。

with open('teachers.xml', 'r') as f:
    file = f.read()


# 'xml' is the parser used. For html files, which BeautifulSoup is typically used for, it would be 'html.parser'.
soup = BeautifulSoup(file, 'xml')


変数 soup には XML ファイルをパースした内容が格納されています。

この変数とそれに付随するメソッドを使って、PythonのコードでXMLの情報を取得することができます。

例えば、XMLドキュメントから教師の名前だけを表示したいとします。

数行のコードでその情報を得ることができます。

names = soup.find_all('name')
for name in names:
    print(name.text)


python teachers.py` を実行すると、次のようになります。

Sam Davis 
Cassie Stone 
Derek Brandon


find_all()メソッドは、引数として渡されたタグにマッチするすべてのリストを返します。

上のコードに示すように、soup.find_all(‘name’)は XML ファイルに含まれるすべてのタグを返します。

そして、これらのタグを繰り返し処理し、タグの値を含むtext` プロパティを表示します。

パースされたデータをテーブルに表示する

もう一歩進めて、XMLファイルの内容をすべてパースして表形式で表示することにしましょう。

teachers.py` ファイルを次のように書き換えてみましょう。

from bs4 import BeautifulSoup


# Opens and reads the xml file we saved earlier
with open('teachers.xml', 'r') as f:
    file = f.read()


# Initializing soup variable
soup = BeautifulSoup(file, 'xml')


# Storing <name tags and elements in names variable
names = soup.find_all('name')


# Storing <age tags and elements in 'ages' variable
ages = soup.find_all('age')


# Storing <subject tags and elements in 'subjects' variable
subjects = soup.find_all('subject')


# Displaying data in tabular format
print('-'.center(35, '-'))
print('|' + 'Name'.center(15) + '|' + ' Age ' + '|' + 'Subject'.center(11) + '|')
for i in range(0, len(names)):
    print('-'.center(35, '-'))
    print(
        f'|{names[i].text.center(15)}|{ages[i].text.center(5)}|{subjects[i].text.center(11)}|')
print('-'.center(35, '-'))


上のコードの出力は次のようになります。

-----------------------------------
|      Name     | Age |  Subject  |
-----------------------------------
|   Sam Davies  |  35 |   Maths   |
-----------------------------------
|  Cassie Stone |  24 |  Science  |
-----------------------------------
| Derek Brandon |  32 |  History  |
-----------------------------------


おめでとうございます。

おめでとうございます! BeautifulSoupとLXMLを使って、最初のXMLファイルをパースしましたね。

さて、理論やプロセスに慣れてきたところで、もっと実戦的な例を試してみましょう。

ここでは、汎用的なデータ構造にデータを格納する前段階として、データを表としてフォーマットしました。

つまり、今度のミニプロジェクトでは、Pandasの DataFrame にデータを格納することにします。

もしあなたがまだDataFrameについてよく知らないのであれば、Python with Pandasを読んでください。

DataFrameについてまだよく知らない方は、Python with Pandas: Guide to DataFrameを参照してください。

をご覧ください。

RSSフィードのパースとCSVへのデータ格納

ここでは、The New York Times NewsのRSSフィードをパースし、そのデータをCSVファイルに格納します。

RSSとは、Really Simple Syndicationの略です。

RSSフィードとは、ウェブサイトの更新情報をまとめたファイルのことで、XMLで記述されています。

ここでは、The New York TimesのRSSフィードは、同ウェブサイトで毎日更新されるニュースの要約を含んでいます。

この要約には、ニュースリリースへのリンク、記事画像へのリンク、ニュース項目の説明などが含まれています。

RSSフィードは、ウェブサイトの所有者による素敵なトークンとして、人々がウェブサイトをスクレイピングせずにデータを取得できるようにするためにも使用されています。

以下は、The New York TimesのRSSフィードのスナップショットです。

このリンクから、さまざまな大陸、国、地域、トピック、その他の基準のNew York Times RSSフィードにアクセスすることができます。

データの解析を始める前に、その構造を見て理解することが重要です。

各ニュース記事に関する RSS フィードから抽出したいデータは、次のとおりです。

  • グローバル一意識別子 (GUID)
  • タイトル
  • 発行日
  • 説明

さて、構造に慣れ、明確な目標を持ったところで、プログラムを開始しましょう! データを取得し、簡単にCSVファイルに変換するために、requestsライブラリとpandasライブラリが必要です。

まだ requests を扱ったことがない方は、 Python のリクエストモジュールへのガイド を読んでください。

を読んでください。

requestsを使うと、Web サイトに HTTP リクエストを送り、そのレスポンスを解析することができる。

今回の例では、RSSフィード(XML)を取得し、BeautifulSoupがそれをパースできるようにするために使用できます。

pandasを使用すると、パースされたデータをテーブルでフォーマットし、最終的にテーブルの内容をCSVファイルに格納することができるようになります。

同じ作業ディレクトリに、 requestspandas をインストールします (仮想環境はまだ有効である必要があります)。

$ pip install requests pandas


新しいファイル nyt_rss_feed.py で、ライブラリをインポートします。

import requests
from bs4 import BeautifulSoup
import pandas as pd


そして、The New York TimesのサーバーにHTTPリクエストをして、RSSフィードを取得し、その内容を取得しましょう。

url = 'https://rss.nytimes.com/services/xml/rss/nyt/US.xml'
xml_data = requests.get(url).content


上記のコードで、HTTP リクエストからレスポンスを取得し、その内容を xml_data 変数に格納することができました。

requestsライブラリはデータをbytes` として返す。

では、BeautifulSoupの助けを借りて、PandasでXMLデータをパースしてテーブルに格納する以下の関数を作成します。

def parse_xml(xml_data):
  # Initializing soup variable
    soup = BeautifulSoup(xml_data, 'xml')


# Creating column for table
    df = pd.DataFrame(columns=['guid', 'title', 'pubDate', 'description'])


# Iterating through item tag and extracting elements
    all_items = soup.find_all('item')
    items_length = len(all_items)

    for index, item in enumerate(all_items):
        guid = item.find('guid').text
        title = item.find('title').text
        pub_date = item.find('pubDate').text
        description = item.find('description').text


# Adding extracted elements to rows in table
        row = {
            'guid': guid,
            'title': title,
            'pubDate': pub_date,
            'description': description
        }


df = df.append(row, ignore_index=True)
        print(f'Appending row %s of %s' % (index+1, items_length))


return df


上記の関数は、BeautifulSoupを使ってHTTPリクエストからXMLデータをパースし、その内容をsoup変数に格納します。

パースしたいデータの行と列を持つ Pandas DataFrame は df 変数を通して参照されます。

次に、XML ファイルを繰り返し処理し、<item を含むすべてのタグを探します。

タグを繰り返し処理することで、その子タグを抽出することができます。

guid, <title, <pubdate, そして <description です。

find()` メソッドを使用して、1つのオブジェクトだけを取得していることに注意してください。

各子タグの値をPandasのテーブルに追加していきます。

さて、関数の後のファイルの最後に、関数を呼び出してCSVファイルを作成するために、次の2行のコードを追加します。

df = parse_xml(xml_data)
df.to_csv('news.csv')


python nyt_rss_feed.py` を実行し、現在の作業ディレクトリに新しい CSV ファイルを作成します。

Appending row 1 of 24
Appending row 2 of 24
...
Appending row 24 of 24


CSV ファイルの中身は以下のようになります。

データのダウンロードは、インターネットの接続状況やRSSフィードの種類によって、多少時間がかかる場合があります。

また、データの解析には、CPUやメモリなどのリソースが必要です。

今回使用したフィードはかなり小さいので、すぐに処理できるはずです。

すぐに結果が表示されない場合は、しばらくお待ちください。

おめでとうございます!The New York Times NewsのRSSフィードを解析してCSVファイルに変換できました。

結論

このガイドでは、BeautifulSoup と LXML をセットアップして XML ファイルをパースする方法について学びました。

まず、教師データのある単純な XML ファイルを解析して練習し、次に、The New York Times の RSS フィードを解析して、そのデータを CSV ファイルに変換してみました。

これらのテクニックを使用して、遭遇する可能性のある他の XML を解析し、必要なさまざまな形式に変換することができます。

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