基礎から学ぶPython入門 90日コース | Web・API - Day 53:Webデータ取得

Python 90日で身につけるPython
スポンサーリンク
スポンサーリンク

Day 53:Webデータ取得 ― 「情報収集ツール」を作りながら、正しいマナーも身につける

Day 53では、Pythonで Web上の公開情報を集める「情報収集ツール」 を作りながら、 技術だけでなく、利用規約・robots.txt・アクセス制限を確認する習慣 もセットで身につけていきます。

ここまで学んできた「HTTP」「requests」「Beautiful Soup」を組み合わせて、 実際に「Webデータ取得の一連の流れ」を体験する回です。

Webデータ取得の全体像をイメージする

情報収集ツールの役割を言葉で整理する

まずは、今日作る 情報収集ツール の役割を、ざっくり言葉で整理してみます。

  • 公開されているWebページから情報を取得する
  • HTMLを解析して、欲しい部分(タイトル・本文・リンクなど)を抜き出す
  • 結果を画面に表示したり、ファイルに保存したりできるようにする
  • その前提として、利用規約・robots.txt・アクセス制限を確認する習慣を持つ

技術的には、

  • requests でHTMLを取得
  • BeautifulSoup でHTMLを解析
  • タグ・属性・CSSセレクタで要素を検索
  • テキストやリンクを抽出

という流れになります。

Webデータ取得のマナー:利用規約・robots.txt・アクセス制限

なぜ「マナー」が重要なのか

Webデータ取得は、技術的には「できてしまう」ことが多いですが、 だからこそ 「やっていいかどうか」 を常に意識する必要があります。

特に意識したいのは次の3つです。

  • 利用規約(Terms of Service)
  • robots.txt
  • アクセス制限(レート制限・認証など)

利用規約を確認する習慣

多くのサイトには「利用規約」や「ご利用条件」があり、 そこに「スクレイピング禁止」「自動取得禁止」などの記載がある場合があります。

情報収集ツールを作るときは、

「このサイトは、自動取得を許可しているか?」

を必ず確認する習慣を持つことが大切です。

robots.txtを確認する習慣

robots.txt は、サイトのトップに置かれる「クローラー向けのルール」です。

例:https://example.com/robots.txt

ここには、

  • どのパスへのアクセスを許可するか
  • どのパスへのアクセスを禁止するか

といった情報が書かれています。

情報収集ツールを作るときは、

「robots.txtで禁止されているパスにはアクセスしない」

というルールを守ることが基本になります。

アクセス制限・レート制限を意識する

APIと同様、Webサイトにも「短時間に大量アクセスすると制限される」仕組みがあることがあります。

  • 一定時間に何回まで
  • 一度に大量のページを取得しない
  • 適度な間隔を空ける

といった配慮をすることで、

「サイトに迷惑をかけない情報収集」

を心がけることができます。

ステップ1:HTMLを取得する小さな関数を作る

まずは、情報収集ツールの「入口」となる、 HTML取得関数 を作っていきます。

# day53_web_fetch.py
import requests


def fetch_html(url: str) -> str | None:
    """
    指定したURLからHTMLを取得する関数です。
    - 公開情報を対象にします。
    - タイムアウトやHTTPエラーを簡単に扱います。
    """

    print(f"[GET] {url} にアクセスします。")

    try:
        # timeout= を付けて、いつまでも待たないようにします。
        response = requests.get(url, timeout=5)
    except requests.exceptions.RequestException as e:
        # ネットワークエラーなどをまとめて扱います。
        print("HTML取得中にエラーが発生しました:", e)
        return None

    # ステータスコードをチェックします。
    if 200 <= response.status_code < 300:
        # 成功した場合は、レスポンス本文(HTML)を返します。
        return response.text
    else:
        print("HTMLの取得に失敗しました。ステータスコード:", response.status_code)
        return None


if __name__ == "__main__":
    # 練習用に httpbin のHTMLページを取得してみます。
    url = "https://httpbin.org/html"
    html = fetch_html(url)

    if html is not None:
        print("取得したHTMLの先頭100文字:")
        print(html[:100])
Python

ここではまだ「HTML文字列」を手に入れただけですが、 次のステップでこれを解析して「情報収集ツール」に近づけていきます。

ステップ2:Beautiful SoupでHTMLを解析し、情報を抜き出す

タイトル・本文・リンクを抽出する基本パターン

次に、Beautiful Soupを使って、 HTMLから「欲しい情報」を抜き出す処理を書いていきます。

# day53_info_extractor.py
import requests
from bs4 import BeautifulSoup


def fetch_html(url: str) -> str | None:
    """Day 53用のHTML取得関数です。"""
    print(f"[GET] {url} にアクセスします。")
    try:
        response = requests.get(url, timeout=5)
    except requests.exceptions.RequestException as e:
        print("HTML取得中にエラーが発生しました:", e)
        return None

    if 200 <= response.status_code < 300:
        return response.text
    else:
        print("HTMLの取得に失敗しました。ステータスコード:", response.status_code)
        return None


def extract_info(html: str):
    """
    HTMLから情報を抽出する関数です。
    - ページタイトル(<h1>)を取得します。
    - 段落(<p>)のテキストを一覧表示します。
    - ページ内のリンク(<a>)を一覧表示します。
    """

    # Beautiful Soup でHTMLを解析します。
    soup = BeautifulSoup(html, "html.parser")

    # 1. ページタイトル(<h1>)を取得
    h1_tag = soup.find("h1")
    if h1_tag is not None:
        print("ページタイトル:")
        print(h1_tag.get_text(strip=True))
    else:
        print("ページタイトル(<h1>)は見つかりませんでした。")

    # 2. 段落(<p>)のテキストを一覧表示
    p_tags = soup.find_all("p")
    print("\n本文(<p>)一覧:")
    for i, p in enumerate(p_tags, start=1):
        text = p.get_text(strip=True)
        print(f"[{i}] {text}")

    # 3. ページ内のリンク(<a>)を一覧表示
    a_tags = soup.find_all("a")
    print("\nリンク一覧:")
    for i, a in enumerate(a_tags, start=1):
        href = a.get("href")  # href属性からリンク先URLを取得します。
        text = a.get_text(strip=True)
        print(f"[{i}] テキスト='{text}', URL='{href}'")


def main():
    url = "https://httpbin.org/html"
    html = fetch_html(url)

    if html is not None:
        extract_info(html)
    else:
        print("HTMLを取得できなかったため、情報抽出を中止します。")


if __name__ == "__main__":
    main()
Python

このコードは、 「1ページ分の情報収集ツール」 の基本形になっています。

ステップ3:情報収集ツールとしての「流れ」を整える

ツールの入口と出口を意識する

情報収集ツールとして考えると、

  • 入口:URLを指定する
  • 中身:HTML取得 → 解析 → 情報抽出
  • 出口:結果を表示する/保存する

という流れになります。

ここでは、入口と出口を少し整えて、 「ツールらしい形」にしてみます。

# day53_info_tool.py
import requests
from bs4 import BeautifulSoup


def fetch_html(url: str) -> str | None:
    """指定URLからHTMLを取得する関数です。"""
    print(f"[GET] {url} にアクセスします。")
    try:
        response = requests.get(url, timeout=5)
    except requests.exceptions.RequestException as e:
        print("HTML取得中にエラーが発生しました:", e)
        return None

    if 200 <= response.status_code < 300:
        return response.text
    else:
        print("HTMLの取得に失敗しました。ステータスコード:", response.status_code)
        return None


def extract_info(html: str) -> dict:
    """
    HTMLから情報を抽出して、辞書としてまとめて返す関数です。
    - title: ページタイトル
    - paragraphs: 段落テキストのリスト
    - links: (テキスト, URL) のタプルのリスト
    """

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

    # ページタイトル
    h1_tag = soup.find("h1")
    title = h1_tag.get_text(strip=True) if h1_tag is not None else None

    # 段落テキスト
    paragraphs = [
        p.get_text(strip=True)
        for p in soup.find_all("p")
    ]

    # リンク一覧
    links = []
    for a in soup.find_all("a"):
        text = a.get_text(strip=True)
        href = a.get("href")
        links.append((text, href))

    return {
        "title": title,
        "paragraphs": paragraphs,
        "links": links,
    }


def print_info(info: dict):
    """抽出した情報を見やすく表示する関数です。"""

    print("=== 情報収集結果 ===")

    # タイトル
    title = info.get("title")
    if title:
        print("\n[タイトル]")
        print(title)
    else:
        print("\n[タイトル]")
        print("(見つかりませんでした)")

    # 段落
    paragraphs = info.get("paragraphs", [])
    print("\n[本文]")
    if paragraphs:
        for i, text in enumerate(paragraphs, start=1):
            print(f"[{i}] {text}")
    else:
        print("(本文が見つかりませんでした)")

    # リンク
    links = info.get("links", [])
    print("\n[リンク]")
    if links:
        for i, (text, href) in enumerate(links, start=1):
            print(f"[{i}] テキスト='{text}', URL='{href}'")
    else:
        print("(リンクが見つかりませんでした)")


def main():
    # 実際に使うときは、ここに「情報収集したいURL」を指定します。
    url = "https://httpbin.org/html"

    html = fetch_html(url)
    if html is None:
        print("HTMLを取得できなかったため、終了します。")
        return

    info = extract_info(html)
    print_info(info)


if __name__ == "__main__":
    main()
Python

この形になると、

  • URLを変えるだけで、別のページの情報収集ができる
  • 抽出した情報が辞書にまとまっているので、後でファイル保存などに発展させやすい

という「ツールらしさ」が出てきます。

ステップ4:マナーをコードの外側で意識する

ここまでのコードは「技術的にできること」を示していますが、 実際に運用するときには、次のような習慣をセットで持つことが大切です。

  • 利用規約を読む
    • 「自動取得」「スクレイピング」に関する記載がないか確認する
  • robots.txtを確認する
    • 禁止されているパスにはアクセスしない
  • アクセス頻度を抑える
    • time.sleep() などで間隔を空ける
  • 認証が必要なページには勝手にアクセスしない
    • ログインが必要なページは、特に慎重に扱う

情報収集ツールは、 「技術的にできること」と「倫理的にやっていいこと」の両方を考えながら育てていくものです。

Day 53ミニテンプレート:情報収集ツールの基本形

最後に、Day 53の内容をコンパクトにまとめた 「情報収集ツール」テンプレート を載せておきます。

# day53_info_collect_tool_template.py
import time
import requests
from bs4 import BeautifulSoup


def fetch_html(url: str) -> str | None:
    """
    指定URLからHTMLを取得する関数です。
    - 公開情報を対象にします。
    - タイムアウトと簡単なエラー処理を行います。
    """

    print(f"[GET] {url} にアクセスします。")

    try:
        response = requests.get(url, timeout=5)
    except requests.exceptions.RequestException as e:
        print("HTML取得中にエラーが発生しました:", e)
        return None

    if 200 <= response.status_code < 300:
        return response.text
    else:
        print("HTMLの取得に失敗しました。ステータスコード:", response.status_code)
        return None


def extract_info(html: str) -> dict:
    """
    HTMLから基本的な情報を抽出します。
    - タイトル(<h1>)
    - 段落(<p>)
    - リンク(<a>)
    """

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

    # タイトル
    h1_tag = soup.find("h1")
    title = h1_tag.get_text(strip=True) if h1_tag is not None else None

    # 段落
    paragraphs = [
        p.get_text(strip=True)
        for p in soup.find_all("p")
    ]

    # リンク
    links = []
    for a in soup.find_all("a"):
        text = a.get_text(strip=True)
        href = a.get("href")
        links.append((text, href))

    return {
        "title": title,
        "paragraphs": paragraphs,
        "links": links,
    }


def print_info(info: dict):
    """抽出した情報を見やすく表示します。"""

    print("=== 情報収集結果 ===")

    title = info.get("title")
    print("\n[タイトル]")
    print(title if title else "(見つかりませんでした)")

    print("\n[本文]")
    paragraphs = info.get("paragraphs", [])
    if paragraphs:
        for i, text in enumerate(paragraphs, start=1):
            print(f"[{i}] {text}")
    else:
        print("(本文が見つかりませんでした)")

    print("\n[リンク]")
    links = info.get("links", [])
    if links:
        for i, (text, href) in enumerate(links, start=1):
            print(f"[{i}] テキスト='{text}', URL='{href}'")
    else:
        print("(リンクが見つかりませんでした)")


def main():
    # 実際に使うときは、ここに「情報収集したいURL」を指定します。
    url = "https://httpbin.org/html"

    # ※実運用では、この前に利用規約・robots.txtの確認を行う習慣を持つことが大切です。

    html = fetch_html(url)
    if html is None:
        print("HTMLを取得できなかったため、終了します。")
        return

    info = extract_info(html)
    print_info(info)

    # アクセス間隔を空ける例(複数ページを巡回する場合)
    time.sleep(1.0)


if __name__ == "__main__":
    main()
Python

Day 53のまとめ

Day 53では、Webデータ取得と情報収集ツールの制作を通して、

  • 公開情報を対象に、WebページからHTMLを取得する基本パターン
  • Beautiful SoupでHTMLを解析し、タイトル・本文・リンクなどを抽出する流れ
  • 抽出した情報を辞書にまとめて、ツールとして扱いやすくする考え方
  • 利用規約・robots.txt・アクセス制限を確認する習慣の重要性
  • 「技術的にできること」と「倫理的にやっていいこと」をセットで考える姿勢

を整理しました。

この土台があると、

  • ニュースサイトから見出しだけを集める
  • 公開されている統計ページから数値をまとめる
  • 社内の公開レポートから必要な情報だけを抽出する

といった「情報収集ツール」を、 安全かつ丁寧な形で育てていくことができるようになります。

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