PythonのBeautifulSoupとは?HTMLの取得・解析の使い方を解説

PR Python

PythonのBeautifulSoupとは?HTMLの取得・解析の使い方を解説

💡 この記事でわかること

  • BeautifulSoupとは何か、Webスクレイピングの基本的な考え方
  • インストール方法と、HTMLを解析する基本の書き方
  • find・find_all・select で、目的のタグを取り出す方法
  • requests と組み合わせて、Webページを取得・解析する流れ
  • スクレイピングを行う前に確認すべきルールとマナー

1. BeautifulSoupとは?

BeautifulSoupとは、
👉 HTMLの文章を解析して、必要なタグや文字だけを取り出しやすくするPythonの外部ライブラリです。

Webページから情報を取り出すことを「スクレイピング」と呼びます。ページのHTMLを取得するのは requests、取得したHTMLの中から「見出しだけ」「リンクだけ」を取り出すのが BeautifulSoup、という役割分担です。

👉 Pythonのrequestsについての記事はこちら

💡 スクレイピングは、相手のサイトに負担をかけたり、規約に反したりする可能性がある操作です。取得する前に、サイトの利用規約や robots.txt を確認し、APIが用意されているならAPIを使うのが基本です。詳しくは、この記事の後半の「スクレイピングのルールとマナー」で解説します。

この記事のコード例は、練習用に書いたHTMLの文字列を使って動かします。実際のWebサイトにアクセスする例は、動作確認用として公開されている example.com だけにしています。


2. BeautifulSoupをインストールする

BeautifulSoupは標準ライブラリではないので、pip でインストールします。ターミナルで次のコマンドを実行してください。

python3 -m pip install beautifulsoup4 requests

👉 インストールするときの名前は beautifulsoup4、読み込むときの名前は bs4 です。名前が違うので、間違えやすい点です。requests をまだ入れていない場合は、あわせてインストールします。仮想環境を使っていない場合にエラーが出るときは、仮想環境の記事も参考にしてください。

👉 Pythonの仮想環境(venv)についての記事はこちら


3. HTMLを解析する基本

まず、練習用のHTMLを文字列として用意して、解析してみます。BeautifulSoup(HTML, "html.parser") で、HTMLを扱えるオブジェクトにします。

from bs4 import BeautifulSoup

html = """
<html>
  <head><title>ゼロスタ サンプル</title></head>
  <body>
    <h1 id="main-title">プログラミング学習ブログ</h1>
    <p class="lead">初心者向けの記事を書いています。</p>
    <ul class="article-list">
      <li><a href="/css-flexbox/">CSSのflexbox</a></li>
      <li><a href="/python-json/">PythonのJSON</a></li>
      <li><a href="https://example.com/">外部サイト</a></li>
    </ul>
    <p class="note">更新日:<span class="date">2026-09-20</span></p>
  </body>
</html>
"""

soup = BeautifulSoup(html, "html.parser")

print(soup.title.text)   # ゼロスタ サンプル
print(soup.h1.text)      # プログラミング学習ブログ
  • "html.parser" は、HTMLを解析する方法(パーサー)の指定。Pythonに標準で付いているので、追加のインストールは不要
  • soup.title・soup.h1 のように、soup.タグ名 で、最初に見つかったそのタグを取り出せる
  • .text(または .get_text())で、タグの中の文字だけを取得できる

👉 HTMLのタグについての記事はこちら


4. タグを探す(find・find_all)

soup.タグ名 では最初の1つしか取れません。条件を指定して探すときは find()(最初の1つ)と find_all()(すべて)を使います。

# 最初の <p> タグ
print(soup.find("p"))
# <p class="lead">初心者向けの記事を書いています。</p>

# class や id で絞り込む(classは、Pythonの予約語と重なるため class_ と書く)
print(soup.find("p", class_="note").text)   # 更新日:2026-09-20
print(soup.find(id="main-title").text)      # プログラミング学習ブログ

# すべての <a> タグ
links = soup.find_all("a")
print(len(links))   # 3

for a in links:
    print(a.get_text(strip=True), a["href"])
# CSSのflexbox /css-flexbox/
# PythonのJSON /python-json/
# 外部サイト https://example.com/

👉 class で絞り込むときは、class_ と最後にアンダースコアを付けます。class はPythonの予約語で、そのままでは書けないためです。find_all() の結果はリストのように扱えるので、for 文で1つずつ取り出せます。

👉 タグの属性(リンク先など)は、辞書と同じように a["href"] で取り出します。属性がないかもしれないときは、a.get("href") を使うと、なくてもエラーにならず None が返ります(a["target"] のように存在しない属性を [ ] で指定すると KeyError になります)。

👉 Pythonのリストと辞書についての記事はこちら


5. CSSセレクタで探す(select・select_one)

HTMLやCSSを学んだ人にとっては、CSSセレクタで指定できる select()(すべて)と select_one()(最初の1つ)のほうが、書きやすいことも多くあります。

print(soup.select_one("p.lead").text)
# 初心者向けの記事を書いています。

print([a.text for a in soup.select("ul.article-list a")])
# ['CSSのflexbox', 'PythonのJSON', '外部サイト']

print(soup.select_one(".date").text)
# 2026-09-20

👉 ul.article-list a は「クラスが article-list の ul の中にある a」という意味のセレクタです。select() の結果はリストで返ってきます。ブラウザの開発者ツールでコピーしたセレクタも、そのまま使えることが多いです。

👉 CSSのセレクタについての記事はこちら


6. requestsと組み合わせてWebページを解析する

実際のWebページを解析するときは、requests で取得したHTMLを BeautifulSoup に渡します。ここでは、動作確認用に公開されている example.com を例にします。

import requests
from bs4 import BeautifulSoup

response = requests.get("https://example.com/", timeout=10)
response.raise_for_status()

page = BeautifulSoup(response.text, "html.parser")

print(page.title.text)        # Example Domain
print(page.h1.text)           # Example Domain
print(page.find("a")["href"]) # https://iana.org/domains/example

👉 表示される内容は、サイト側が変更すると変わることがあります。timeout の指定や raise_for_status() での確認は、requests の記事で解説した基本と同じです。

日本語のページで文字化けするときは、response.text の代わりに response.content を渡すと、直ることがあります。サーバーが文字コードを指定していない場合、response.text は、正しくない文字コードで読み込んでしまうことがあるためです。

page = BeautifulSoup(response.content, "html.parser")   # バイト列のまま渡す

👉 response.content をそのまま渡すと、BeautifulSoupが、HTML内の <meta charset="utf-8"> などを手がかりに文字コードを判断してくれます。それでも直らないときは、ページごとの文字コードを調べて対応します。


7. 実践例:リンクの一覧を取り出してJSONに保存する

これまでの内容を組み合わせて、記事一覧のリンクから、タイトルとURLを取り出す例です。相対パスのリンクは、urljoin() で完全なURLに直せます。解析するHTMLは、3章で用意した練習用の soup を使います。

import json
from urllib.parse import urljoin

base_url = "https://example.com"
articles = []

for a in soup.select("ul.article-list a"):
    articles.append({
        "title": a.get_text(strip=True),
        "url": urljoin(base_url, a["href"]),
    })

print(json.dumps(articles, ensure_ascii=False, indent=2))
[
  {
    "title": "CSSのflexbox",
    "url": "https://example.com/css-flexbox/"
  },
  {
    "title": "PythonのJSON",
    "url": "https://example.com/python-json/"
  },
  {
    "title": "外部サイト",
    "url": "https://example.com/"
  }
]

👉 /css-flexbox/ のような相対パスは、urljoin(基準のURL, リンク) で完全なURLになり、https://example.com/ のような完全なURLは、そのまま残ります。json.dump() を使えば、この結果をファイルに保存することもできます。

👉 PythonのJSONについての記事はこちら


8. スクレイピングのルールとマナー

スクレイピングは便利な反面、やり方によっては、相手のサイトに迷惑をかけたり、規約違反になったりします。実際のサイトに使う前に、次の点を必ず確認してください。

  • 利用規約を確認する:サイトによっては、自動でのデータ取得を禁止しています。禁止されている場合は、行わないでください
  • robots.txt を確認する:サイトの /robots.txt には、自動アクセスして良い範囲・いけない範囲が書かれています
  • APIがあれば、APIを使う:公式にAPIが提供されているなら、スクレイピングよりもAPIのほうが確実で、相手にも負担をかけません
  • アクセスの間隔をあける:連続でアクセスするときは、time.sleep(1) などで、1秒以上あけます。短時間の大量アクセスは、サーバーの負担になります
  • 取得したデータの扱いに気をつける:文章や画像には著作権があります。個人情報を含むデータの収集や公開は、してはいけません

robots.txt は、Pythonの標準ライブラリ urllib.robotparser で、「このURLにアクセスしてよいか」を確認できます。

from urllib.robotparser import RobotFileParser

rp = RobotFileParser()
rp.set_url("https://example.com/robots.txt")
rp.read()

# "*" は、すべてのロボット(自動アクセス)を表す
print(rp.can_fetch("*", "https://example.com/some-page"))   # True なら許可されている

👉 robots.txt や利用規約は、あくまで確認の目安であり、守っていれば何をしてもよいという意味ではありません。判断に迷うときは、取得しないという選択が安全です。

なお、requests はJavaScriptを実行しないため、JavaScriptで後から表示される内容は、取得できないことがあります。その場合は、別の方法が必要になります。


9. つまずきやすいポイント

① 見つからないときは None が返り、その先でエラーになる

find() は、条件に合うタグがないと None を返します。そのまま .text を続けると、エラーになります。

print(soup.find("table"))        # None(tableタグはないため)

print(soup.find("table").text)
# AttributeError: 'NoneType' object has no attribute 'text'

次のように、None かどうかを先に確認する、または select()(見つからなければ空のリスト)を使うと、エラーを避けられます。

table = soup.find("table")
if table is not None:
    print(table.text)
else:
    print("tableが見つかりませんでした")

print(soup.select("table"))   # [](エラーにならない)

👉 Pythonの例外処理についての記事はこちら

② 取得したHTMLとブラウザの表示が違う

ブラウザで見えている内容が、response.text の中に含まれていないときは、JavaScriptで後から作られている可能性があります。まず print(response.text) で、実際に何が返ってきているかを確認しましょう。

③ サイトのHTMLが変わると、動かなくなる

スクレイピングは、サイトのHTMLの構造に依存しています。サイト側が構造を変えると、これまで動いていたコードが取得できなくなります。セレクタは、できるだけ変わりにくいもの(idなど)を選び、定期的に動作を確認しましょう。


10. BeautifulSoup 早見表

書き方できること
BeautifulSoup(html, "html.parser")HTMLを解析して、扱えるオブジェクトにする
soup.タグ名最初に見つかったそのタグを取り出す
soup.find("p", class_="note")条件に合う最初のタグを取り出す(なければ None)
soup.find_all("a")条件に合うタグをすべて取り出す(リスト)
soup.select_one("p.lead")CSSセレクタで最初の1つを取り出す
soup.select("ul li a")CSSセレクタですべて取り出す(なければ空のリスト)
tag.text / tag.get_text(strip=True)タグの中の文字を取得する(前後の空白を除く)
tag["href"] / tag.get("href")属性の値を取得する(getは、なくても None)
urljoin(基準URL, リンク)相対パスを、完全なURLにする

11. まとめ

  • BeautifulSoupは、HTMLを解析して必要な部分を取り出すためのライブラリ(インストール名は beautifulsoup4、読み込みは bs4)
  • タグの取り出しは find()・find_all()、CSSセレクタなら select()・select_one()
  • 文字は .text、属性は ["href"] で取得し、Webページの取得は requests と組み合わせる
  • find() は見つからないと None を返すので、その先の .text でエラーにならないように確認する
  • 利用規約・robots.txt の確認、アクセス間隔、データの扱いなど、スクレイピングのルールとマナーを必ず守る

👉 まずは、この記事の練習用HTMLで、タグの取り出し方に慣れてみてください。実際のサイトに使うのは、ルールを確認し、相手に負担をかけない書き方ができるようになってからにしましょう。

-Python
-, , , ,