2011年4月12日火曜日

解析木の修正

今や、あなたは解析木内を、どのように検索すれば良いか知っています。しかし、解析木を修正し、再出力したいことがあるかもしれません。
要素を親の contents の外側に切り取ることが出来ます、しかし、ドキュメントの残りの部分は、切り取った要素へのリファレンスを持ち続けます。
Beautiful Soup は、解析木の内部的な一貫性を維持する間に、解析木を修正するための様々な方法を提供します。

属性値を変更する


タグオブジェクトの属性値を修正するために、ディクショナリの代入を使用出来ます。
from BeautifulSoup import BeautifulSoup
soup = BeautifulSoup("<b id="2">Argh!</b>")
print soup
# <b id="2">Argh!</b>
b = soup.b

b['id'] = 10
print soup
# <b id="10">Argh!</b>

b['id'] = "ten"
print soup
# <b id="ten">Argh!</b>

b['id'] = 'one "million"'
print soup
# <b id='one "million"'>Argh!</b>

また、属性値を削除したり、新しい属性値を追加したり出来ます:
del(b['id'])
print soup
# <b>Argh!</b>

b['class'] = "extra bold and brassy!"
print soup
# <b class="extra bold and brassy!">Argh!</b>

要素の除去


要素への参照を持つと、 extract を使用して、要素を解析木の外側に切り取ることが出来ます。
このコードは、ドキュメントから、全てのコメントを除去します:
from BeautifulSoup import BeautifulSoup, Comment
soup = BeautifulSoup("""1<!--The loneliest number-->
                        <a>2<!--Can be as bad as one--><b>3""")
comments = soup.findAll(text=lambda text:isinstance(text, Comment))
[comment.extract() for comment in comments]
print soup
# 1
# <a>2<b>3</b></a>

このコードはドキュメントの部分木全体を刈り取ります:
from BeautifulSoup import BeautifulSoup
soup = BeautifulSoup("<a1></a1><a><b>Amazing content<c><d></a><a2></a2>")
soup.a1.nextSibling
# <a><b>Amazing content<c><d></d></c></b></a>
soup.a2.previousSibling
# <a><b>Amazing content<c><d></d></c></b></a>

subtree = soup.a
subtree.extract()

print soup
# <a1></a1><a2></a2>
soup.a1.nextSibling
# <a2></a2>
soup.a2.previousSibling
# <a1></a1>

extract メソッドは、1つの解析木を2つの互いに素な解析木に変換します。
航行メソッドが変更され、そのため、木は決して元に戻らないように見えます:
soup.a1.nextSibling
# <a2></a2>
soup.a2.previousSibling
# <a1></a1>
subtree.previousSibling == None
# True
subtree.parent == None
# True

1つの要素を別の要素に置換する


replaceWith メソッドは、1ページの要素を抽出し、異なる要素と置換します。
新しい要素は、タグや、可航オブジェクトで構いません。
The new element can be a Tag (possibly with a
whole parse tree beneath it) or a NavigableString.
もし、変更前のプレーン文字を replaceWith に渡せば、文字は、NavigableString に変わります。
航行メンバーは、ドキュメントが最初の場所で、上記方法で解析されたかように変更されます。

ここに、シンプルな例を示します:
from BeautifulSoup import BeautifulSoup
soup = BeautifulSoup("<b>Argh!</b>")
soup.find(text="Argh!").replaceWith("Hooray!")
print soup
# <b>Hooray!</b>

newText = soup.find(text="Hooray!")
newText.previous
# <b>Hooray!</b>
newText.previous.next
# u'Hooray!'
newText.parent
# <b>Hooray!</b>
soup.b.contents
# [u'Hooray!']

あるタグを別のタグで置換する、より複雑な例を示します:
from BeautifulSoup import BeautifulSoup, Tag
soup = BeautifulSoup("<b>Argh!<a>Foo</a></b><i>Blah!</i>")
tag = Tag(soup, "newTag", [("id", 1)])
tag.insert(0, "Hooray!")
soup.a.replaceWith(tag)
print soup
# <b>Argh!<newTag id="1">Hooray!</newTag></b><i>Blah!</i>

ドキュメントの一部から要素を切り取り、他の場所に組み込むことさえ出来ます:
from BeautifulSoup import BeautifulSoup
text = "<html>There's <b>no</b> business like <b>show</b> business</html>"
soup = BeautifulSoup(text)

no, show = soup.findAll('b')
show.replaceWith(no)
print soup
# <html>There's  business like <b>no</b> business</html>

新しい要素の追加


タグクラスと、解析クラスは insert と呼ばれるメソッドをサポートします。
これは、ちょうど、Pythonのリストの insert メソッドのように動作します:
インデックスをタグの contents メンバーに渡し、新しい要素をその箇所に組み込みます。

これは、前のセクションで、ドキュメント中のタグを新しいタグで置換する時、実演済みです
スクラッチから、解析木全体を構築するために、 insert を使うことが出来ます:
from BeautifulSoup import BeautifulSoup, Tag, NavigableString
soup = BeautifulSoup()
tag1 = Tag(soup, "mytag")
tag2 = Tag(soup, "myOtherTag")
tag3 = Tag(soup, "myThirdTag")
soup.insert(0, tag1)
tag1.insert(0, tag2)
tag1.insert(1, tag3)
print soup
# <mytag><myOtherTag></myOtherTag><myThirdTag></myThirdTag></mytag>

text = NavigableString("Hello!")
tag3.insert(0, text)
print soup
# <mytag><myOtherTag></myOtherTag><myThirdTag>Hello!</myThirdTag></mytag>

1つの解析木中、一箇所だけで、要素が生じます。
もし、
If you
give insert an element that's already connected to a soup object, it
gets disconnected (with extract) before it gets connected
elsewhere.
この例の中で、 NavigableString をsoupの2番目の場所に挿入しようとしましたが、 NavigableString を再び挿入することは出来ませんでした。
NavigableString は移動してしました:
tag2.insert(0, text)
print soup
# <mytag><myOtherTag>Hello!</myOtherTag><myThirdTag></myThirdTag></mytag>

この現象は、要素が前もって、全く異なるスープオブジェクトに属していたとしても起きます。
要素は1つの parent や、1つの nextSibling などしか持っておらず、要素は同時に1箇所にしか存在出来ないためです。

0 件のコメント:

コメントを投稿

【和訳】Django Rest Framework 目次

目次 【和訳】Django Rest Framework クイックスタート 【和訳】Django Rest Framework チュートリアル1:シリアル化 【和訳】Django Rest Framework チュートリアル2:リクエストとレスポンス 【和訳】Django Res...