パースツリー
ツリーのナビゲート
parent
contents
string
nextSiblingとpreviousSibling
nextとprevious
タグの繰り返し処理
タグ名をメンバーとして使う
パースツリーの検索
パーサーオブジェクト(BeautifulSoup やBeautifulStoneSoupのインスタンス)は、HTMLやXMLの構造に対応するように接続された深いネストのデータ構造を持ちます。これをこのドキュメントでは「パースツリー」と呼んでいます。パーサーオブジェクトには2つのタイプのオブジェクトがあります。一つはTagオブジェクトで<TITLE>のようなHTMLやXMLのタグに対応します。もう一つはNavigableStringオブジェクトで"Page title"や"This is paragraph"のような文字列に対応します。NavigableStringオブジェクトには、特別なXML構文に対応する、CData, Comment, Declaration, ProcessingInstructionなどのサブクラスもあります。これらのサブクラスはサブクラスの持つエクストラデータを出力する場合を除いて、NavigableStringオブジェクトと同じように振舞います。
Tagオブジェクトは以下のメンバーを持ちます。NavigableStringオブジェクトもcontentsとstring以外のすべてのメンバーを持ちます。
以下の構造のHTMLドキュメントで、<HEAD>に対応するTagオブジェクトのparentは<HTML>に対応するTagオブジェクトとなります。<HTML>に対応するTagオブジェクトのparentはBeautifulSoup(またはBeautifulStoneSoup)パーサーオブジェクトになります。このパーサーオブジェクトのparentはNoneとなります。
<html>
<head>
<title>
...
以下はサンプルコードです。
#coding: UTF-8
from BeautifulSoup import BeautifulSoup
doc = ['<HTML><head><TITLE>Page title</title></head>',
'<body><p id="para1" align="left">paragraph-1',
'<p id="para2" align="center">paragraph-2',
'<div class = cls1>cls1<b>test</b><div class=cls2>class2 div</div></div>'
'</html>']
soup = BeautifulSoup(''.join(doc))
# <head>Tagのparentは<html>Tag
print soup.head.parent.name
# <head>Tagのparentのparentは<html>BeautifulSoup
print soup.head.parent.parent.__class__.__name__
# BeautifulSoupのparentはNone
print soup.parent
上記のコードを実行すると、以下が出力されます。
html
BeautifulSoup
None
parentによってツリーを上に移動することができますが、contentsを使うとツリーを下に移動することができます。contentsはページ要素のTagやobjects オブジェクトの順番のリストです。トップレベルのパーサーオブジェクトとTagオブジェクトのみがcontentsを持ちます。NavigableStringは文字列なのでサブ要素を持つことができないのでcontentsを持ちません。
#coding: UTF-8
from BeautifulSoup import BeautifulSoup
doc = ['<HTML><head><TITLE>Page title</title></head>',
'<body><p id="para1" align="left">paragraph-1',
'<p id="para2" align="center">paragraph-2',
'<div class = cls1>cls1<b>test</b><div class=cls2>class2 div</div></div>'
'</html>']
soup = BeautifulSoup(''.join(doc))
# <head>Tagのparentは<html>Tag
pBody = soup.body
print pBody.contents
上記のスクリプトを実行すると、以下のように<body>の下のサブ要素が表示されます。
[<p id="para1" align="left">paragraph-1</p>, <p id="para2" align="center">paragraph-2<div class="cls1">cls1<b>test</b><div class="cls2">class2 div</div>
</div></p>]
タグが1つだけのチャイルドノードを持ち、そのチャイルドノードが文字列の場合、それはtag.stringとして参照することができます。
#coding: UTF-8
from BeautifulSoup import BeautifulSoup
doc = "<html><head><title>Page title</title></head><body><p>paragraph-1</html>"
soup = BeautifulSoup( doc )
pBody = soup.body
上記のスクリプトを実行すると、<title>タグの内容(文字列)"Page Title"が表示されます。
nextSiblingとpreviousSiblingはパースツリーの同じ階層の前後のノードへのリンクです。以下の構造のHTMLドキュメントの場合、&lh;head>タグのnextSiblingは&lh;body>タグ、&lh;body>タグのpreviousSiblingは&lh;head>タグとなります。
<html>
<head>
<title>
Page title
</title>
</head>
<body>
..
以下のスクリプトを実行すると、<head>タグと同じ階層の次のノード<body>タグの名前"body"が表示されます。
#coding: UTF-8
from BeautifulSoup import BeautifulSoup
doc = "<html><head><title>Page title</title></head><body>body</body></html>"
soup = BeautifulSoup( doc )
print soup.head.nextSibling.name
nextとpreviusもnextSiblingとpreviousSiblingと同じようにノード間のリンクのためのメンバーですが、nextとpreviusはノードがパーサーのよって処理された順番でリンクされます(おそらくドキュメント上でのタグの出現順)。
タグをリストとして扱うことで、タグの内容を繰り返し処理することができます。たとえば、次のような書き方ができます。
from BeautifulSoup import BeautifulSoup
doc = \"<html><head><title>Page title</title></head><body><div>__div1__</div><div>__div2__</div></body></html>\"
soup = BeautifulSoup( doc )
#<body>のサブノードの数を表示
print len( soup.body )
#<body>のサブノードを表示
for subNode in soup.body:
print subNode
上記のスクリプトを実行すると、以下が出力されます。
2
<div>__div1__</div>
<div>__div2__</div>
パーサーやTagオブジェクトのメンバーとしてタグ名を使うこともできます。これはあらかじめドキュメントの構造が分かっている場合に便利な方法です。
#coding: UTF-8
from BeautifulSoup import BeautifulSoup
doc = \"<html><head><title>Page title</title></head><body><div>__div1__</div><div>__div2__</div></body></html>\"
soup = BeautifulSoup( doc )
print \"head =\", soup.head
print \"title=\", soup.head.title
上記のスクリプトを実行すると、以下が出力されます。
head = <head><title>Page title</title></head>
title= <title>Page title</title>
この方法では、同じ親ノードに同じ名前のタグが複数ある場合、最初に出現するノードだけが参照されます。たとえば、以下のコードでは<body>タグには二つの<div>タグがありますがbodyのdivメンバーとして参照できるのは、最初の<div>となります。タグ名で参照できるメンバーはリストではないので5行目のような書き方はできません。二番目の<div>タグを参照するには7行目のような書き方をします。このような書き方(タグ名で表現されるメンバー)は実際にはfirstメソッドのエイリアスです。
#coding: UTF-8
from BeautifulSoup import BeautifulSoup
doc = "<html><head><title>Page title</title></head><body><div>__div1__</div><div>__div2__</div></body></html>"
soup = BeautifulSoup( doc )
#print soup.body.div[1]
print soup.body.div
print soup.body.contents[1]
タグ名の最後に"Tag"をつけた名前のメンバーも使うことができます。たとえば、object.bodyで参照されるタグはobject.bodyTagという名前でも参照することができます。
#coding: UTF-8
from BeautifulSoup import BeautifulSoup
doc = \"<html><head><title>Page title</title></head><body><div>__div1__</div><div>__div2__</div></body></html>\"
soup = BeautifulSoup( doc )
#soup.headと同じ意味
print \"head =\", soup.headtag
#soup.head.titleと同じ意味
print \"title=\", soup.headTag.titleTag
Pythonの識別子として無効なタグ名(たとえばハイフン付きの名前)を使う場合、ここで説明した方法は使えません。この場合は、findメソッドを使います。
Beautiful Soupは構文解析ツリーをたどったり、指定した条件に一致するTagオブジェクトやNavigableStringオブジェクトを取得するためのメソッドを持っています。
findAll
条件にマッチするすべてのTagオブジェクトとNavigableStringオブジェクトを検索する