BeautifulSoupでstringとtextの挙動の明確な違い – Python



スクレイピングなどで、最終的に文字列を取得したい場合は、soupオブジェクトに対して".string"や".text" で文字列を抽出することが出来ますが、両者の明確な挙動の違いを例を挙げて紹介します。

例えば上記のようなHTMLソースがあった場合を例に見ていきます。

ここまでが前提のソース。

soup.p.text の場合

".text" の方は非常に単純で、そのタグ内に含まれるすべての文字列をつなぎ合わせて返却します。

.stringに対して、予想外の挙動になることが少ないので、「文字列の取得は.textを使っている」という人も少なくないはず。

soup.p.text

ここまでは普通ですね。pタグ内のタグ<br/>と<b></b>タグがしっかり消えています。

ちなみに返却の型はstr型です。

このように".text"は子孫要素の最下部に至るまで文字列を再帰的に取得してつなぎ合わせて返却します。

もちろん、最上位の親タグ<div>を指定しても、全ての文字列のみが返却されます。

ちょっとうっとおしい"\n"も文字列としてしっかり帰ってきています。

soup.p.string の場合

何となくどこで使えばいいのか分からない".string"ですが、こちらの挙動を見ていきます。

soup.p.string

???
なにも帰って来ません。

返却はNoneですね。

soup.b.string

ターゲットをpタグからbタグに変更してみるとしっかり取れます。

ちなみに返却型は通常の文字列型(str)とは違いBeautifulSoup専用の特殊文字列クラス"NavigableString"です。

soup.stringが返却される条件

".string"は直下にタグ要素を持っているとNoneとなり、返却されません。

と、今まで思っていたのですが、どうやら違うようです。
その様に解説しているブログとかも結構あったりします。
でも、違います

以下で少し、実験してみます。
元のhtmlをbs4を使って加工、pタグ内に子要素として他のタグが存在しない状態を作ります。

pタグ内のbタグを取り除いたこの状態でもNoneで、何も返って来ません。
まだ、pタグの中にbrタグがいるからですね。

???

何も返却されません。pタグ内に他のタグは存在しないのに変ですね。

soup.stringが返却される本当の条件

結論から言うと、".string"は指定した要素の、子孫要素に渡って"NavigableString"クラスが一つしか存在しない時にstringとして返却されます。

上の実験で何故stringが返ってこないかの答えを解説します。

答えは".strings"という似たようなメソッド(アトリビュート?)を見てみると分かります。
このメソッドは子孫要素に渡って"NavigableString"クラスを再帰的に取得し、ジェネレータ型で返却します。

結果はご覧の通り、返ってきたNavigableStringをリスト化してみると、4つ存在します。

よく見ていただくと分かるのですが、実験内で消したタグが存在していた位置で文字列が途切れています。

BeautifulSoupのメソッドでタグを取り除いても厳密には文字列が繋がるワケではないという事です。

NavigableStringクラスというのは各種タグで分断されるようですね。

「タグで分断されるなら、子孫要素にタグがある時点で、.stringは返却されないんじゃ?」
と思いました?

このように指定要素内に別のタグが存在していても、テキストを分断しない配置になっていれば.stringは返却されるのです。

BeautifulSoupにおけるstringとtextの違い まとめ

どちらもテキストを取得するために用いるメソッドだけど、何が何でも根こそぎ取得してくる".text"に対して、割と繊細で使いどころが難しい".string"という印象は変わらないですね。

".string"のメリットは何かというと、一般的なstr型文字列ではなく、bs4派生クラスの文字列なので、bs4各種メソッドが使える点が大きいですね。
.textで返却されるのは標準のstr型なので、もちろんBeatifulSoupのメソッドを使うことはできません。

単純な文字列取得だけなら、あまり".string"を使うことはないかもしれませんが、HTML自体を加工して流用する場合なんかだと .extract() とか、親まで返って .decompose() とか、使えないと困りますので使いどころはあるにはあると思います。

今回の記事で、両者の明確な違いを少し頭に入れていただいて、実際使ったときに挙動が変で混乱することがなくなれば幸いです。