Beautiful Soup の replaceWith で string が更新されない

# -*- coding: utf-8 -*-

from BeautifulSoup import BeautifulSoup

soup = BeautifulSoup("<b>git</b>")
print soup                                # => <b>git</b>
print soup.b                              # => <b>git</b>
print soup.b.string                       # => git
soup.find(text='git').replaceWith('Git')
print soup                                # => <b>Git</b>
print soup.b                              # => <b>Git</b>
print soup.b.string                       # => git


上記のように replaceWith を実行すると、
要素の innerText が更新されているように見えるけれど、
string で参照すると、更新されていない。


また同じように新しくタグの要素を作成し置き換えた場合も
一見うまくいっているように見えるけれど、string で参照すると
None が返ってくる。

# -*- coding: utf-8 -*-

from BeautifulSoup import BeautifulSoup, Tag

soup = BeautifulSoup("<dc:subject>git</dc:subject>")
print soup.find('dc:subject')                  # => <dc:subject>git</dc:subject>
print soup.find('dc:subject').string           # => git

new_tag = Tag(soup, "dc:subject")
new_tag.insert(0, 'Git')
soup.find('dc:subject').replaceWith(new_tag)

print soup.find('dc:subject')                  # => <dc:subject>Git</dc:subject>
print soup.find('dc:subject').string           # => None


これは何だろう…と思って、わかんないかもしれないけれど
ソースでも見てみるか、と思いつつちょっと検索したら全く同じ話がでてきた。


Google グループ



That is indeed a bug in all versions before the just released
Beautiful Soup 3.0.8. Before that, Tag.string wasn't updated when the
tree was manipulated. Beautiful Soup 3.0.8 calculates Tag.string when
it is accessed using a property, so that bug is entirely fixed.


3.0.8 is available on the Beautiful Soup home page, but hasn't yet
been added to any distro packages. There hasn't been a 3.1.x release
based on the 3.0.8 changes, so that bug is still in 3.1.x.


使っていた Beautiful Soup は 3.0.8 だったけれど、
easy_install でいれたのでよく分からないが、
全く同じ現象が起こっているからまだバグが直っていないのを使っているようだ。


とりあえず renderContents で対応することにした。

print soup.b.renderContents()                  # => Git
print soup.find('dc:subject').renderContents() # => Git