【Pythonスクレイピング入門】BeautifulSoupの使い方・メソッド一覧|HTML基本操作

Pythonを使ったWebスクレイピングの比較的メジャーなライブラリBeautifulSoupのメソッドを一挙紹介します。

このページを読めばBeautifulSoupのほとんどの動作、メソッドを確認することができます。

以下の目次は展開でき、逆引きリファレンスの形式になっていますので、調べたい操作がある方は、気になる箇所へすぐにジャンプできます。

BeautifulSoupとは

一言で言うと、HTMLをパースするPythonのライブラリです。

スクレイピングという処理は、HTMLの取得と解析の二段構成です。

僕はHTMLの取得にはrequestsというモジュールを使うことが多いです。
一応標準のライブラリでもあるにはあるんですが、Pythonのhttpアクセスのディファクトスタンダードはrequestsかなと個人的には思っています。

BeautifulSoupは解析の部分を担当するライブラリですね。
主にスクレイピングという作業では目的のHTMLタグや、テキストの抜き出しに使うことが多いです。

とりあえずこれを使えばHTML色々いじれるんだー、便利なんだーって思ってもらえれば。

BeautifulSoupインストール

  • bs4(BeautifulSoup)
  • lxml

Anacondaでパッケージを管理していれば基本的にどちらも初期状態から入っています。

必要なHTMLを抜き出すためにbs4というモジュールを使います。Python用のHTMLパーサもいくつか存在しますが、bs4とlxmlの組み合わせは割とメジャーなのでこれらを使っていきます。

lxmlというのはパーサと言って、解析方法の一つです。Python標準の"html.parser"というパーサよりも高速で動作可能なのが特徴です。

ない場合はどちらもpipでインストール可能です。適宜インストールしてください。

BeautifulSoupの使い方

こんな感じでインポートして使います。

第一引数のHTML_TEXTの部分は取ってきたHTMLをそのまま渡して、第二引数はパーサを指定します。

パーサっていうのは、"読み取り方式" と思ってもらえればOKです。省略可能な引数で、省略した場合は、標準の'html.parser'が使用されます。

つまり例に挙げている'html.parser'の部分は全く書く必要がありません。

僕は、より実践的にこんな形で書くことが多いです。

インポートの時点でas bs4 として、今後はBeautifulSoupなんてながったらしく書かねーからな、という宣言を行います。

何回も書く可能性がある時に長いメソッド名書いているとそれだけでスペルミスとか、余計な失敗するので・・・。

bs4に渡している res.content というのはrequestsで取ってきたHTMLのbytes形式のデータです。

res.text を渡してももちろん動作するのですが、 res.content を渡した方が「文字化け」する可能性を減らせますのでこちらで書く癖をつけましょう。

上で少し説明しましたが、'lxml'というのはCで書かれたパース方式。標準の"html.parser"よりも早いです。ただ、HTMLの状況によってはパース出来ないことがたまにあります。

基本は'lxml'を使いつつ、なんか知らんが上手いこと行かない時は'html.parser'を使っています。

BeautifulSoupオブジェクトの操作

上の例のコードで挙げたsoup変数にはBeautifulSoupのオブジェクトが格納されています。

このsoupオブジェクトを操作して、htmlの中身を取得したり、書き換えたり出来ます。

タグ名で検索

最初のaタグを取得

どちらも同じ動作をします。html上で”最初に出てくるaタグ”を返します。

最初のaタグに囲まれたテキストのみを取得

.textを付けるだけですね。<a></a>に囲まれた文字列だけを返します。

全てのaタグを取得

どちらも同じ動作です。find_allは単純に省略可能です。

全てのaタグのテキストのみを取得

soup使い始めて最初の頃、やりがちなんですが、.findの時とは違い、返ってくるのはリスト型(bs4.element.ResultSet)ですので、そのままテキストだけを抜き出すことはできません。

JQueryとか使い慣れてると何となく「いけんじゃねーかな」と思っちゃうんですよね。無理です!

これですべてのaタグのテキストのみをリスト型で取得できます。

リスト内包表記というPythonの特徴的な書き方ですが、簡単に説明すると一行でfor文を書けて、結果をリスト型で返します。

上記例ではtag変数がリスト内全ての要素にアクセスして tag.text でtag内のテキストのみを抜き出しています。

タグ内属性を取得

上記はgetメソッドを使うパターンですが、下記はブラケットでアクセスするパターンです。

基本的にこれらはget()でアクセスした時と同じ結果を返します。

何が違うかというと、ブラケットアクセスの場合、その属性がタグ内に存在しなかった場合KeyErrorの例外が発生します。

対してget()メソッドの場合は "none" が返却され例外は発生しません。

属性名で検索

最初にクラス属性に一致するタグを取得

class= ではなく class_= です。

Pythonではclassは予約語(別の意味のある)単語なのでそれと区別するためアンダースコアが必要です。

クラス属性に一致するタグを全て取得

どちらも同じ動作。

idに一致するタグを取得

id はそのまま id なんですね。ちなみにHTML構文の基本と矛盾しますが、

こんな書き方も通ります。どうせ id を find_all しても一個しか出てこないんですけどね。この場合返ってくるのはリスト型です。

複合的なタグ名または属性名で検索

複数の条件を組み合わせてタグを取得したい時があります。

全て同じ動作です。

attrsで指定するときはDict型(辞書型)で渡します。この場合classのアンダースコアはいりません。

CSSセレクタ型の検索

ここまで読んできて、JQueryを普段使い慣れている人からしたら、「BeautifulSoupめんどくさくね?」って思っているでしょう?

そんなあなたに、

こんな感じでCSSセレクタも使えます。返却型はリスト型になります。

その他のタグの取得検索方法

上記メソッドの他にもいくつかタグやタグ内テキスト、属性などを絞り込んで取得する方法が存在しますので紹介します。

タグ名を返却

ちなみにこの書き方だと当然 "a" がstring型で返却されてきますので、ほとんど意味がありませんね。メソッド書いてる時点で"a"だと確定しますからね。

直下子要素をリスト形式で返却

直下子要素をジェネレータ形式で返却

直下子要素をジェネレータ形式で再帰的に返却

子要素に含まれる文字列全てをジェネレータ形式で返却

親要素全体を取得

親要素を再帰的に取得

最初のa要素の次の兄弟要素を取得

最初のa要素の前の兄弟要素を取得

兄弟要素をジェネレータで一気に取得

上記で紹介したnext_sibling、previous_sibling はそれぞれ、"次"と"前"の兄弟要素を「ひとつだけ」取得する方法ですが、以下のようにすると、"以降の兄弟要素" と "以前の兄弟要素" を全て取得できるようになります

ちなみに返却の形式はジェネレータ形式です。
parent と parents みたいな感じですね。

最初のa要素の次の兄弟要素を取得(要素内も順次取得)

要素の書き換え

BeautiflSoupという名前だけあって、きれいにHTMLを整形したり、置き換えたりすることもできます。もしかしたら本来はこっちの使い方がメインなのかもしれませんが・・・。

純粋なスクレイピングを行うだけならあまり使う機会のないメソッドが多いかもしれませんが、機械的な引用転載なんかで活躍したりしますね。

要素の書き換えの基本事項

基本的には以下で紹介するメソッドは新規のsoupオブジェクトを返すワケではなく、現状のsoupオブジェクトを改変するものと思ってください。

Rubyでいう「破壊的メソッド」なイメージです。

書き換えだけではなく、以降に紹介する削除系のメソッドも同様に元本のオブジェクトを改変します。

また、オブジェクトが格納された変数などを操作して得られたオブジェクトは参照渡しとなります。
どういうことか、以下に例を挙げます。

後に紹介しますが、decompose()はタグを削除するメソッドです。

この場合、変数"a_tags"に格納されたaタグが全て削除されるのは理解できると思いますが、実は、aタグの削除は元の"soup"という変数にも及びます

意外と見落としがちで、挙動が変なことに後から気づくケースが多いので、念頭に入れてください。

最初のdivタグをpタグに変更

divタグをpタグに変更します。この置き換えでは文字列で直接指定でき、new_tag()メソッドでの指定は必要ありません。

最初のpタグのタグ内テキストをreplace_textに変更

タグ内テキストを変更可能です。
ちなみに以下の方法では書き換えが失敗します。

最初のp要素内にappend_textを追加

最初のa要素のhref属性を変更

属性には[]ブラケットでアクセスできます。
その場合、オブジェクトtypeはbs4.element.Tagである必要があります。

最初のpタグを新規divタグで囲う

指定した要素を覆うように新規タグを配置します。

soup.new_tag()メソッドでタグを作る必要があるのがミソです。

要素の削除

soupオブジェクトから要素を削除することが可能です。

要素の削除の基本事項

基本的には以下で紹介するメソッドは新規のsoupオブジェクトを返すワケではなく、現状のsoupオブジェクトを改変するものと思ってください。

Rubyでいう「破壊的メソッド」なイメージです。

削除だけではなく、上で紹介している書き換え系のメソッドも同様に元本のオブジェクトを改変します。

また、オブジェクトが格納された変数などを操作して得られたオブジェクトは参照渡しとなります。
どういうことか、以下に例を挙げます。

後に紹介しますが、decompose()はタグを削除するメソッドです。

この場合、変数"a_tags"に格納されたaタグが全て削除されるのは理解できると思いますが、実は、aタグの削除は元の"soup"という変数にも及びます

意外と見落としがちで、挙動が変なことに後から気づくケースが多いので、念頭に入れてください。

最初のdivタグからクラス属性を削除

属性を削除したい場合はdelステートメントを使います。

最初のスクリプトタグを削除

decompose()はタグを丸ごと取り除きます。

最初のpタグを取り出し、かつ、元の変数からその要素を削除

decompose()と何が違うのかというと、extract()は結果を返すという点が異なります。

pという変数には最初のpタグが代入された状態になり、元のsoupオブジェクトからはそのタグが削除されます。

jQueryとか使ってる人なら「pop」がイメージしやすいですかね。

最初のpタグの中身を削除する

decompose()との違いは中身だけを消去する点です。
つまり、この場合<p>~</p>の枠は残ります。

最初のaタグを囲う親要素を削除

直上の親要素を削除します。wrap()の反対ですね。

BeautifulSoupでXMLをパースする

BeautifulSoupはHTMLのパースというイメージがあるかと思いますが、実はXMLの解析にも使えます。

pythonのXML解析用のライブラリには他にもElementTreeなどがありますが、BeautifulSoupを使い慣れているならこちらの方が直感的に操作しやすいかもしれません。

メソッドなどの取り扱いはHTMLをパースする時とほとんど同じです。

ただ、XMLのパース時は少しだけ、オブジェクトの作り方が異なります。

基本的にはパーサに'lxml-xml'を指定するのがいいと思います。

'lxml'のままでも動くには動きますが、キャメルバックのタグが全て小文字になったり、少し挙動が変化します。

特に理由がないのであれば専用のパーサを使っておくのが無難です。

BeautifulSoupを用いたXMLの解析は以下の記事で、例として、AmazonのMWSから取得できるXMLデータを用いて詳細に解説しています。

BeautifulSoup 総括

スクレイピングでは主に要素の取得が目的になることが多いかと思いますが、実際のBeautifulSoupはjQueryライクな、総合的にHTMLの加工をすることを得意としています。

CSSセレクタ型の操作にも対応していて、Pythonによるスクレイピングでは手放せない便利なライブラリです。