Python | 自動化:データ収集 BOT

Python
スポンサーリンク

概要(データ収集BOTは「決まった場所から、決まった情報を黙々と集めてくれるロボット」)

データ収集 BOT は、
「毎日このサイトの価格をチェック」「このAPIから定期的にデータを取得」
といった“繰り返しの情報収集”を、Python に任せる仕組みです。

やっていることはシンプルで、

  • どこから(URL / API / ファイル)
  • 何を(どの情報・どの項目)
  • いつ(毎日 / 毎時 / 定期)

取ってくるかを決めて、それをコードとして固定するだけです。

ここでは、初心者がイメージしやすいように

  1. 「Webページから情報を取るBOT」
  2. 「APIから情報を取るBOT」

という2パターンを例に、設計の考え方からコード例、運用で大事なポイントまでかみ砕いて解説します。


基本の考え方(データ収集 BOT を分解すると何をしているのか)

データ収集 BOT の「頭の中」を分解する

どんな BOT も、やっていることはだいたい次の流れに分解できます。

  1. 行き先を決める
    どの URL / API / ファイル / DB から取るかを決める。
  2. 取り方を決める
    HTTP で GET するのか、POST するのか、ヘッダーやパラメータは何か。
  3. どこを抜くか決める
    HTML のどのタグ、JSON のどのキー、レスポンスのどの部分を使うか。
  4. どう保存するか決める
    CSV / Excel / DB / JSON …どの形式で残すか。
  5. いつ動かすか決める
    手動で python を叩くのか、cron/タスクスケジューラで自動実行するのか。

これを「人間の頭の中にあるやり方」から、「Python コード」に翻訳していくのが設計です。

まずは「1回分の収集」をきちんと作る

定期実行や自動化の前に、必ず
「1回分だけ、今からデータを取ってくる」スクリプトを先に作ります。

この 1回分を、

  • どこを叩く(URL)
  • 何をもらう(HTML / JSON)
  • どう取り出す(パース)
  • どう保存する(ファイル / DB)

まで含めて“1つの関数”に閉じ込める。
それができてから、cron やバッチに乗せます。


例1:Webページから情報を取るデータ収集 BOT(スクレイピング入門)

ライブラリとプロジェクトの準備

HTML ページからデータを取るときの定番は、

  • requests(HTTP通信)
  • BeautifulSoup(HTML解析)

の組み合わせです。

あらかじめインストールしておきます。

pip install requests beautifulsoup4

フォルダ構成はシンプルで構いませんが、パス管理のために Path を使う癖をつけておくと後が楽です。

from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent
DATA_DIR = BASE_DIR / "data"
DATA_DIR.mkdir(exist_ok=True)
Python

例:あるニュースサイトの「記事タイトル一覧」を取る

仮に、ニュースサイトのカテゴリページから「記事タイトルだけ」を毎日取得したいとします。
※ 実際にスクレイピングする場合は、対象サイトの利用規約・robots.txt を必ず確認してください。

HTML 構造(開発者ツールで確認)で、記事タイトルがこんな感じだとします。

<h2 class="article-title">
  <a href="/news/123">Python 自動化入門</a>
</h2>
HTML

この場合、クラス article-title の h2 タグを全部拾い、その中の a のテキストを抜けば良いです。

import requests
from bs4 import BeautifulSoup
from datetime import datetime
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent
DATA_DIR = BASE_DIR / "data"
DATA_DIR.mkdir(exist_ok=True)

TARGET_URL = "https://example.com/news/python"

def fetch_news_titles():
    resp = requests.get(TARGET_URL, timeout=10)
    resp.raise_for_status()

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

    titles = []
    for h2 in soup.select("h2.article-title a"):
        title = h2.get_text(strip=True)
        link = h2["href"]
        titles.append({"title": title, "link": link})

    return titles

def save_titles_to_csv(titles):
    now = datetime.now().strftime("%Y%m%d_%H%M%S")
    out_path = DATA_DIR / f"news_titles_{now}.csv"

    import csv
    with out_path.open("w", encoding="utf-8", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=["title", "link"])
        writer.writeheader()
        writer.writerows(titles)

    print(f"保存しました: {out_path}")

def run_bot():
    titles = fetch_news_titles()
    print(f"取得件数: {len(titles)}")
    save_titles_to_csv(titles)

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

ここで重要なポイントを整理します。

  • requests.get でページを取得し、raise_for_status() でエラー応答を例外に変えている
  • BeautifulSoup の select("h2.article-title a") で、CSS セレクタを使って要素を絞り込んでいる
  • 必要な情報だけ(タイトルとリンク)を dict にしてリスト化し、それを CSV にしている

この「HTML から必要な部分だけ抜き、構造化されたデータ(リスト・dict)にする」感覚がとても大事です。


例2:API からデータを取る BOT(JSON ベース)

Webスクレイピングと API の違い

スクレイピングは「人間向けの HTML から機械的に情報を引き抜く」方法です。
一方、API は「最初から機械向け(JSONなど)にデータを返す窓口」です。

もし対象サービスに API があるなら、基本的にはスクレイピングより API を使う方が、

  • 安定している
  • 壊れにくい(HTML レイアウト変更に左右されにくい)
  • 利用規約的にも許可されているケースが多い

という点で好ましいです。

例:シンプルな JSON API からデータを取る

仮に、次のような API があるとします。

  • エンドポイント: https://api.example.com/v1/items
  • パラメータ: category=python
  • レスポンス(JSON)は、items の配列を返す

レスポンス例(ざっくり):

{
  "items": [
    {"id": 1, "name": "Python入門", "price": 1200},
    {"id": 2, "name": "自動化レシピ", "price": 1500}
  ]
}

これを毎日取得して CSV に保存する BOT は、こんな感じになります。

import requests
import csv
from datetime import datetime
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent
DATA_DIR = BASE_DIR / "data"
DATA_DIR.mkdir(exist_ok=True)

API_URL = "https://api.example.com/v1/items"

def fetch_items():
    params = {"category": "python"}
    resp = requests.get(API_URL, params=params, timeout=10)
    resp.raise_for_status()
    data = resp.json()
    return data["items"]

def save_items_csv(items):
    now = datetime.now().strftime("%Y%m%d_%H%M%S")
    out_path = DATA_DIR / f"items_{now}.csv"

    if not items:
        print("データがありません")
        return

    fieldnames = list(items[0].keys())
    with out_path.open("w", encoding="utf-8-sig", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(items)

    print(f"保存しました: {out_path}")

def run_bot():
    items = fetch_items()
    print(f"取得件数: {len(items)}")
    save_items_csv(items)

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

ここでのキモは、

  • resp.json() で JSON を Python の dict / list に変換している
  • items はただの「リスト of dict」なので、そのまま CSV に流せる

という点です。


BOT 設計で本当に大事なところを深掘りする

1. パス設計(BASE_DIR / DATA_DIR をちゃんと決める)

データ収集 BOT は、定期実行(cron / タスクスケジューラ)と組み合わせることが多いです。
このとき「カレントディレクトリがどこか分からない」問題がよく起きます。

そのため、必ず

BASE_DIR = Path(__file__).resolve().parent
DATA_DIR = BASE_DIR / "data"
Python

のように、「スクリプトが置いてある場所」を基準にフォルダを作っていきます。
こうすると、どこから python bot.py を叩いても、data フォルダの場所がブレません。

2. 例外処理とログ(失敗したことに気づけるようにする)

ネット越しの BOT は、失敗する前提で作ります。
回線落ち、API 側エラー、HTML 構造変更、などなど。

最低限、「失敗時に何が起きたか」をわかるようにしておきます。

簡易的には print でも構いませんが、運用を意識するなら logging がおすすめです。

import logging

LOG_FILE = BASE_DIR / "data_bot.log"

def setup_logging():
    logging.basicConfig(
        filename=LOG_FILE,
        level=logging.INFO,
        format="%(asctime)s [%(levelname)s] %(message)s"
    )

def run_bot():
    logging.info("BOT開始")
    try:
        items = fetch_items()
        logging.info(f"取得件数: {len(items)}")
        save_items_csv(items)
        logging.info("BOT正常終了")
    except Exception as e:
        logging.exception(f"BOT異常終了: {e}")
        raise
Python

こうしておくと、「昨日の夜の取得が失敗していたのに誰も気づかない」という事故を減らせます。

3. 冪等性(同じ日付のデータをもう一度取り直しても壊れない)

BOT 運用で地味に効いてくるのが“再実行しても壊れないかどうか”です。

例えば、日次データを取る BOT の場合、

  • YYYYMMDD.csv のように日付別ファイルにする
  • 同じ日付のファイルが既にある場合の扱いを決める(上書き / スキップ / バックアップ)

などのルールを先に決めておきます。

上書きを許容するなら、こういう感じです。

def get_output_path(prefix: str, date_str: str):
    return DATA_DIR / f"{prefix}_{date_str}.csv"

def save_items_csv_for_date(items, date_str):
    out_path = get_output_path("items", date_str)
    # すでにある場合は上書き(=取り直し)
    ...
Python

逆に「一度取ったら変わらない」系のデータなら、
既にファイルがあればスキップする、という設計もありです。

4. 制約・マナー(アクセス頻度・利用規約)

データ収集 BOT を作ると、つい「たくさん回したくなる」気持ちになりますが、
対象サイトや API にとっては負荷になる場合があります。

意識しておきたいのは、

  • robots.txt をチェックして、スクレイピングが許可されているか確認する
  • 連続アクセスする場合は time.sleep で間隔を空ける
  • API の rate limit(最大リクエスト数/秒や/日)を守る

といった点です。

「人間がブラウザでアクセスする以上の頻度で連打しない」くらいの感覚を持っておくと、
トラブルになりにくくなります。


定期実行と組み合わせて「BOTらしく」していく

1回分ができたら、あとは時間を OS に任せる

ここまでで「run_bot() を呼べば1回分が動く」という状態が作れたら、
いよいよ「BOT化」ができます。

考え方としては、

  • Python 側:1回分の処理(今日のデータを取る)
  • OS 側:毎日○時に python bot.py を実行

と役割を分けます。

Linux / Mac なら cron、Windows ならタスクスケジューラです。
(あなたはすでに cron / タスクスケジューラの話も読んでくれているので、その知識がそのまま活きます。)

実運用イメージ

例えば、「毎日 9:00 にニュースタイトルを収集する BOT」は、

  • news_bot.pyrun_bot() を定義
  • crontab0 9 * * * /usr/bin/python3 /path/to/news_bot.py を登録

という形になります。

ログを見れば「今日何件取れたか」が分かり、
data フォルダには news_titles_YYYYMMDD_...csv が日々増えていく。

ここまで行くと、もう立派な「データ収集 BOT」です。


まとめ(「1回分の収集を丁寧に作る」がすべての出発点)

データ収集 BOT の核心は、豪華な仕組みではなく、

  • どこから何を取ってくるかを明確に決める
  • 1回分の収集処理(fetch → parse → save)を関数にまとめる
  • パス・ログ・エラー・再実行時の挙動を最初に設計する
  • 定期実行は OS(cron / タスクスケジューラ)に任せる

という「地味だけど堅い型」です。

この型を一度自分の手で作ってみると、

  • 価格情報の定期取得
  • ニュース・ブログ記事タイトルの収集
  • 公開APIからの統計データ取り込み

などに、どんどん横展開していけるようになります。

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