PythonによるWebスクレイピングでは、requests と Beautiful Soup の2つのライブラリが定番です。requestsでHTMLをダウンロードし、Beautiful Soup で解析して情報を取り出します。
Beautiful Soup でHTMLの中からHTML要素を取得するには「find系」(find_all()、find())と「select系」(select()、select_one())という2タイプのメソッドを用います。
機能は2つとも同じであり、検索条件に合うHTML要素を返します。
異なるのは「検索条件の指定方法」です。例えば、href属性が”sample.pdf”のa要素を検索するには、それぞれ以下のような書き方になります。どちらも同じ要素を返します。
soup.find_all("a", href="sample.pdf")
soup.select("a[href='sample.pdf']")
このように、find_all()メソッドは「要素名」に続いて「属性」をキーワード引数で指定できます。一方、select()メソッドは、「CSSセレクタ」が使えます。
find_all()とselect()は要素をリストで返しますが、以下の表のようにマッチする要素をひとつだけ返すメソッドも用意されています。
| タイプ | すべての要素をリストで返す | ひとつだけ要素を返す | 引数(検索条件の指定) |
|---|---|---|---|
| find系 | find_all() |
find() |
要素名, 属性指定(キーワード引数) |
| select系 | select() |
select_one() |
CSSセレクタ |
find系とselect系のどちらを使うかは好みでいいと思いますが、select系でCSSセレクタを用いた方がスッキリ書けて臨機に対応できる場合があります。今回はいくつか例を挙げてみましたので、自分にはどちらが使いやすいかの選択材料にしていただけたらと思います。
サンプルコード
今回は以下の簡単なHTMLから要素を取得します。
>>> from bs4 import BeautifulSoup
>>> html = """
<h3 class="ramen title" summary="メニュー">ラーメンメニュー</h3>
<ul>
<li id="syouyu" class="ramen item">醤油ラーメン</li>
<li id="tonkotsu" class="ramen item favorite">とんこつラーメン</li>
<li id="miso" class="ramen item">味噌ラーメン</li>
</ul>
"""
>>> soup = BeautifulSoup(html, "html.parser")
すべての要素を返すメソッドの例
下表の find_all() と select() は同じ要素を返します。
| find_all() | select() | 実行結果 |
|---|---|---|
soup.find_all("li") |
soup.select("li") |
結果1 |
soup.find_all(class_="ramen") |
soup.select(".ramen") |
結果2 |
soup.find_all(class_="ramen item") |
soup.select("[class='ramen item']") |
結果3 |
| ー | soup.select(".ramen.item") |
結果4 |
soup.find_all("li", class_="favorite") |
soup.select("li.favorite") |
結果5 |
soup.find_all(id="miso") |
soup.select("#miso") |
結果6 |
soup.find_all(id=["miso", "tonkotsu"]) |
soup.select("#miso, #tonkotsu") |
結果7 |
classはPythonの予約語であるため使えません。class_を使います。結果は以下のようにすべてリストで返します。合致する要素がない場合は空リストを返します。
結果1
[<li class="ramen item" id="syouyu">醤油ラーメン</li>,
<li class="ramen item favorite" id="tonkotsu">とんこつラーメン</li>,
<li class="ramen item" id="miso">味噌ラーメン</li>]
結果2
[<h3 class="ramen title" summary="メニュー">ラーメンメニュー</h3>,
<li class="ramen item" id="syouyu">醤油ラーメン</li>,
<li class="ramen item favorite" id="tonkotsu">とんこつラーメン</li>,
<li class="ramen item" id="miso">味噌ラーメン</li>]
結果3
class属性の値が"ramen item"に「完全一致」する要素を検索します。"item ramen"のように順序を変えると完全一致しなくなるので「空リスト」を返します。
[<li class="ramen item" id="syouyu">醤油ラーメン</li>,
<li class="ramen item" id="miso">味噌ラーメン</li>]
結果4
class属性の値に"ramen"と"item"の両方を含む要素を検索します(「部分一致」)。find系でこの条件を検索するにはコードで対応する必要があります。
[<li class="ramen item" id="syouyu">醤油ラーメン</li>,
<li class="ramen item favorite" id="tonkotsu">とんこつラーメン</li>,
<li class="ramen item" id="miso">味噌ラーメン</li>]
結果5
[<li class="ramen item favorite" id="tonkotsu">とんこつラーメン</li>]
結果6
[<li class="ramen item" id="miso">味噌ラーメン</li>]
結果7
[<li class="ramen item favorite" id="tonkotsu">とんこつラーメン</li>,
<li class="ramen item" id="miso">味噌ラーメン</li>]
ひとつだけ要素を返すメソッドの例
下表の find() と select_one() は同じ要素を返します。
| find() | select_one() | 実行結果 |
|---|---|---|
soup.find("li") |
soup.select_one("li") |
結果1 |
soup.find(class_="ramen") |
soup.select_one(".ramen") |
結果2 |
soup.find(class_="ramen item") |
soup.select_one("[class='ramen item']") |
結果3 |
| ー | soup.select_one(".ramen.item") |
結果4 |
soup.find("li", class_="favorite") |
soup.select_one("li.favorite") |
結果5 |
soup.find(id="miso") |
soup.select_one("#miso") |
結果6 |
soup.find(id=["miso", "tonkotsu"]) |
soup.select_one("#miso, #tonkotsu") |
結果7 |
classはPythonの予約語であるため使えません。class_を使います。結果は以下のように単独の要素を返します。合致する要素がない場合はNoneを返します。
結果1
<li class="ramen item" id="syouyu">醤油ラーメン</li>
結果2
<h3 class="ramen title" summary="メニュー">ラーメンメニュー</h3>,
結果3
class属性の値が"ramen item"に「完全一致」する要素を検索します。"item ramen"のように順序を変えると完全一致しなくなるので「None」を返します。
<li class="ramen item" id="syouyu">醤油ラーメン</li>
結果4
class属性の値に"ramen"と"item"の両方を含む要素を検索します(「部分一致」)。find系でこの条件を検索するにはコードで対応する必要があります。
<li class="ramen item" id="syouyu">醤油ラーメン</li>
結果5
<li class="ramen item favorite" id="tonkotsu">とんこつラーメン</li>
結果6
<li class="ramen item" id="miso">味噌ラーメン</li>
結果7
<li class="ramen item favorite" id="tonkotsu">とんこつラーメン</li>
おすすめは
しいてどちらかをオススメするなら「select系」です。やはりスッキリ書けて、上記のclass属性の部分一致のようにフレキシブルに対応できます。
CSSセレクタを覚える必要がありますが、Webデザインを目的にするのとは違い、必要最小限をマスターすれば十分です。以下のページにまとめてありますので、ぜひ参考にしてください。
