【Python】BeautifulSoupでHTMLを解析する

Python

こんにちは、かつコーチです。

requestsでHTMLを取得できても、そこから必要な情報だけを取り出すのは意外と大変ですよね。

「見出しだけ抜き出したい」「特定のクラスの要素だけ集めたい」

そんなときに使うのがBeautifulSoup(HTMLやXMLを解析するためのPythonライブラリ)です。

この記事では、BeautifulSoupのインストールから、findfind_allselectを使った要素取得までを解説します。

読み終える頃には、取得したHTMLから狙った情報だけを抽出できるようになります。

基本の書き方・実装手順

BeautifulSoupのインストール

BeautifulSoupと、HTML解析エンジンのlxmlをあわせてインストールします。

pip install beautifulsoup4 lxml requests

手順1:HTMLをBeautifulSoupオブジェクトに変換する

まずは、サンプルのHTML文字列をBeautifulSoupオブジェクトに変換します。

from bs4 import BeautifulSoup

html = """
<html>
<body>
    <h1 class="title">お知らせ一覧</h1>
    <ul class="news-list">
        <li class="news-item">セール開始</li>
        <li class="news-item">新商品入荷</li>
        <li class="news-item">メンテナンス予定</li>
    </ul>
</body>
</html>
"""

soup = BeautifulSoup(html, "lxml")
print(soup.prettify()[:200])

BeautifulSoup(html, "lxml")の第2引数には、解析エンジン(パーサー)を指定します。

lxmlは処理速度が速く、実務でよく使われるパーサーです。

手順2:find/find_allで要素を取得する

findは条件に合う最初の1要素、find_allは条件に合う全要素をリストで返すメソッドです。

from bs4 import BeautifulSoup

html = """
<html>
<body>
    <h1 class="title">お知らせ一覧</h1>
    <ul class="news-list">
        <li class="news-item">セール開始</li>
        <li class="news-item">新商品入荷</li>
        <li class="news-item">メンテナンス予定</li>
    </ul>
</body>
</html>
"""

soup = BeautifulSoup(html, "lxml")

title = soup.find("h1", class_="title")
print(title.text)

items = soup.find_all("li", class_="news-item")
for item in items:
    print(item.text)

実行結果は次の通りです。

お知らせ一覧
セール開始
新商品入荷
メンテナンス予定

class_="title"のようにアンダースコアを付けるのは、Pythonの予約語classと区別するためです。

手順3:select/select_oneでCSSセレクタを使う

selectは、CSSセレクタ(.クラス名#ID名などの記法)で要素を取得できるメソッドです。

普段CSSを書いている方には、こちらの書き方の方が馴染みやすいかもしれません。

from bs4 import BeautifulSoup

html = """
<html>
<body>
    <h1 class="title">お知らせ一覧</h1>
    <ul class="news-list">
        <li class="news-item">セール開始</li>
        <li class="news-item">新商品入荷</li>
        <li class="news-item">メンテナンス予定</li>
    </ul>
</body>
</html>
"""

soup = BeautifulSoup(html, "lxml")

title = soup.select_one("h1.title")
print(title.text)

items = soup.select("ul.news-list > li")
for item in items:
    print(item.text)

select_one()は最初の1要素、select()は全要素のリストを返します。

ul.news-list > liのように、子要素の指定もCSSセレクタと同じ記法で書けます。

つまずきやすい設定・注意点

findselect_oneは、該当する要素がない場合にNoneを返します。

Noneに対して.textを呼び出すとAttributeErrorになるため、要素が見つかったかどうかのチェックを忘れないようにしましょう。

よくあるつまずきポイント・エラー対処

lxmlパーサーが見つからずエラーになる

これは筆者が実際に新しいPCで環境構築をしたときに遭遇したつまずきです。

beautifulsoup4だけをインストールした状態でlxmlパーサーを指定したところ、エラーが発生しました。

❌ Before(lxmlをインストールせずに使おうとする書き方)

from bs4 import BeautifulSoup

html = "<p>テスト</p>"
soup = BeautifulSoup(html, "lxml")
print(soup.p.text)

このとき、次のエラーメッセージが表示されました。

bs4.FeatureNotFound: Couldn't find a tree builder with the features you requested: lxml. Do you need to install a parser library?

原因は単純で、pip install beautifulsoup4だけではlxmlパーサー自体はインストールされないためです。

BeautifulSoup本体と解析エンジンは別パッケージという点を、筆者は見落としていました。

✅ After(lxmlを別途インストールする、または標準パーサーを使う書き方)

pip install lxml
from bs4 import BeautifulSoup

html = "<p>テスト</p>"
soup = BeautifulSoup(html, "lxml")
print(soup.p.text)

pip install lxmlを実行してlxml本体をインストールすれば、エラーは解消します。

すぐにインストールできない環境であれば、Python標準のhtml.parserを使う方法もあります。

from bs4 import BeautifulSoup

html = "<p>テスト</p>"
soup = BeautifulSoup(html, "html.parser")
print(soup.p.text)

html.parserは標準ライブラリに含まれているため追加インストールは不要ですが、lxmlに比べて解析速度がやや遅い点は覚えておきましょう。

まとめ

この記事のポイント

  • BeautifulSoup(html, "lxml")でHTML文字列を解析対象に変換する
  • findは最初の1要素、find_allは全要素をリストで取得する
  • selectselect_oneはCSSセレクタの記法で要素を取得できる
  • 要素が見つからない場合はNoneが返るため、存在チェックを忘れない
  • lxmlパーサーはBeautifulSoup本体とは別パッケージなので、個別インストールが必要

次に読むべき記事

  • requestsライブラリでWebスクレイピングの基本を学ぶ
  • reモジュールで正規表現を使う
  • CSVファイルを一括処理する自動化スクリプトを書く

タグ: Python, 中級者向け, 自動化

タイトルとURLをコピーしました