2013/06/13

ライブラリ:BeautifulSoup

Pythonの「BeautifulSoup」というライブラリについてご紹介します。

import BeautifulSoup

BeautifulSoupは、バージョン3までと現在最新のバージョン4とでパッケージ名が異なります。バージョン4ではBeautifulSoup4もしくはbs4という名前になっています。
  • バージョン3: BeautifulSoup
  • バージョン4: bs4

今回はバージョン3を対象に述べていきます。ちなみに、バージョン3はPyton 3には対応していないため、Python 3で使うならバージョン4の方を使うことになります。

BeautifulSoup(バージョン3まで)には大きく分けて2つのクラスが入っています。BeautifulSoupとBeautifulStoneSoupです。

ちがいは次の点です。
  • BeautifulSoup: HTML用
  • BeautifulStoneSoup: XML用
(ちなみに、BeautifulSoupのバージョン4では、BeautifulStoneSoupはBeautifulSoupに吸収され存在しません)。

基本的な考え方はHTMLでもXMLでも同じなので、今回はElementTreeと対比させる意味も含めて、後者のBeautifulStoneSoupの方を見ていきます。

XMLファイルの読み込み

from BeautifulSoup import BeautifulStoneSoup

XMLFILE = 'sample.xml'

f = open(XMLFILE, 'r')
soup = BeautifulStoneSoup(f.read())
f.close()
XMLファイルを読み込むには、通常のファイルオープンをしてテキストを抽出し、そこからBeautifulStoneSoupインスタンスを生成します。

以下、samle.xmlには以下の内容が入っていると想定してみていきます。
<post attrA="value A" attrB="value B">
    <title>This is a title.</title>
    <categories>
        <!-- This is a comment. -->
        <category term="Asia"/>
        <category term="South America"/>
        <category term="Europe"/>
    </categories>
</post>

データの読み方

上のコードのとおりXMLのおおもととなるインスタンスsoupを生成したら、あとは木を辿ってデータを見ていきます。木を辿るのに使うのは、主に次のふたつのメソッドです。
  • soup.find()
  • soup.findall()

BeutifulStoneSoupでは、各ノードを「Tag」インスタンスとして保持します。各ノードの情報にアクセスするには、主に次のアトリビュート・メソッドを用います。
  • tag.name
  • tag.string
  • tag.attrs
  • tag.get()

以下、いろいろ見ていきます。

print soup.__class__.__name__
# BeautifulStoneSoupと出力
上のコードで生成したsoupはBeautifulStoneSoupのインスタンスです。

print soup.find('category').__class__.__name__
print soup.find('category').name
print soup.find('category').get('term')
print soup.find('category').attrs
# 以下のとおり出力
# Tag
# category
# Asia
# [(u'term', u'Asia')]
tag.find(タグ名)で子孫のノードにアクセスすることができます。各タグはTagのインスタンスです。nameにはそのタグ名が入っています。get()あるいはattrsで、タグのアトリビュート情報にアクセスできます。BeautifulStoneSoupでは、parse読み出した文字列をすべてユニコードに変換して扱います。

print soup.find('title').string
# 以下のとおり出力
# This is a title.
tag.stringにはそのタグ内の文字列が入っています。

es = soup.findAll('category')
for e in es:
    print e.name, e.get('term')
# 以下のとおり出力
# category Asia
# category South America
# category Europe
tag.find(タグ名)が最初に引っかかった要素を1つだけ返すのに対し、tag.findAll(タグ名)は引っかかった要素をすべてリストにして返してくれます。

es = soup.findAll('category', term='Europe')
for e in es:
    print e.name, e.get('term')
# 以下のとおり出力
# category Europe
find()、findAll()を使うときは、タグ名だけでなくアトリビュートも手がかりに検索することができます。ここでは、termの値が「Europe」となる要素を検索しています。

print soup.post.title.string
# 以下のとおり出力
# This is a title.
タグの親子関係をもとにデータを取得することも可能です。この場合は、soup以下にある「post」タグの下にある一番最初の「title」タグを取得しています。

その他の機能

# 整形表示
print soup.prettify()
取得したXMLを整形して表示するために、prettify()というメソッドが用意されています。

# 元データのエンコード
print soup.originalEncoding
元データの文字コードは、originalEncodingで確認することができます。

# 子要素を取得
print soup.post.categories.contents
# 以下のとおり出力
# [u'\n', u' This is a comment. ', u'\n', <category term="Asia"></category>, <category term="South America"></category>, <category term="Europe"></category>]
contentsを使えば、タグだけでなく、コメントなどの文字列も含めて子要素を取得することができます。ただ、元のXMLデータに空白や改行が入っていると、思わぬところでインデックスが変わってしまったりしてハマりがちですので、タグ要素だけに着目するのであれば、find()、findAll()などを使うのがよいかと思います。


以上です。

今回は取り上げませんでしたが、このほかにもBeautifulSoupには
  • あるノードの隣のノードや親ノードを取得する機能
  • XMLを編集する機能
なんかも備わっています。さらに詳しくは参考ページなどをご参照いただければと思います。

また、BeautifulSoupのバージョン3までとバージョン4とでは互換性の無い部分がところどころあるので、その点にも注意が必要かと思います。今回のBeautifulStoneSoupは、バージョン3までの機能です。


インストール
「pip」が入っていれば、コマンドラインから「pip install beautifulsoup」でインストールできます(BeautifulSoup4は「pip install beautifulsoup4」)。


参考
BeautifulSoup - PyPI
Beautiful Soup: We called him Tortoise because he taught us.
Beautiful Soupドキュメント — BeautifulSoup Document 0.1 documentation
BeautifulSoupでスクレイピングのまとめ « taichino.com
BeautifulSoupでHTML解析 - Perl使いのPythonちゃん

Beautifulsoup4 - PyPI
Beautiful Soup 4 Documentation
※最後の2つは BeautifulSoup4 関連です

0 件のコメント: