Python講座 / Beautiful SoupでHTMLから値を抽出する
HTMLから値を取り出すときは、パーサーで要素を選び、テキストや属性を読みます。想定件数と必須値を検証し、HTMLやセレクターの変化を、正しい空データとして受け入れないようにします。
管理できる静的HTMLから始める
この実習は手元のHTML文字列を解析し、外部サイトへリクエストを送りません。HTML解析と外部収集は別の段階です。外部収集ではアクセス権、利用条件、頻度、個人情報の扱いも確認します。
仮想環境へ導入します。
python -m pip install beautifulsoup4
配布名はbeautifulsoup4、import名はbs4です。バックエンドには標準のhtml.parserを使います。
選択子やサーバー接続の準備はCSS講座 / 結合子と擬似クラス:親子・兄弟・状態・順番とCSS講座 / 基本セレクター:要素・クラス・ID・属性の該当実習で確認できます。
抽出の全体を実行する
parse_html.pyへ保存し、python parse_html.pyを実行します。
from bs4 import BeautifulSoup
html = """<ul id="expenses">
<li data-amount="3500"><span class="name">coffee</span></li>
<li data-amount="8000"><span class="name">lunch</span></li>
</ul>"""
soup = BeautifulSoup(html, "html.parser")
rows = soup.select("#expenses > li")
if len(rows) != 2:
raise ValueError("unexpected row count")
total = 0
for row in rows:
name = row.select_one(".name")
amount_text = row.get("data-amount")
if name is None or not amount_text or not amount_text.isascii() or not amount_text.isdecimal():
raise ValueError("missing or invalid field")
amount = int(amount_text)
print(name.get_text(" ", strip=True), amount)
total += amount
print("total:", total)
予想結果です。負数や数字以外の金額はこの例の入力規則では受け入れません。
coffee 3500 lunch 8000 total: 11500
セレクターと不足する要素を扱う
#expenses > liは指定IDの直下にあるli、.nameはクラスを選びます。select_oneは一致がないとNoneを返すため、テキストを読む前に確認します。
get_textの区切りとstripは、入れ子のテキストの間や前後の空白を制御します。ブラウザーに見える内容が、ダウンロードしたHTMLにあるとは限りません。JavaScriptが後から作る場合もあるので、実際の応答とデータ源を確認します。
空結果で故障を隠さない
一致が0件のときは、本当に空なのかセレクターが壊れたのかを区別します。この例は2行を要求しますが、実際の収集では対象に応じた件数、必須値と文脈の方針が必要です。
nameクラスを変えて必須項目の失敗を確認し、data-amountを-1やabcへ変えて拒否を確かめます。名前へ入れ子のタグを追加して文字列の結合も観察し、最後に元のHTMLへ戻します。正規表現によるパターン検索と、構造化されたHTMLの解析は目的が異なります。
Beautiful Soupが扱うのは渡されたHTMLです。HTTP応答にない要素をJavaScriptが後から作る場合、その要素はselectで見つかりません。応答原文とブラウザーの表示を比較してから判断します。クラス選択子と子孫・子の関係を区別すると、似た要素が複数あっても対象範囲を絞れます。




