この記事で解決すること
この記事を最後まで読めば、競合サイトの価格や市場データを毎朝手でコピペする作業をPythonに置き換え、しかも止めずに動かし続ける手順がわかります。
- 01判定ページのソースに目的の文字があるか(静的/動的)
- 02収集requests + BeautifulSoup で表を取る
- 03保存日付つきCSVに utf-8-sig で書く
- 04検査0行・前回比で激減ならエラーにして通知● 人が確認
- 05定時タスクスケジューラ(スリープ解除の設定)
毎日のコピペ作業そのものに大きな時間がかかっているわけではなくても、同じ操作を繰り返す負担は積み重なります。ネット記事どおりにRequestsとBeautifulSoupとscheduleを書き写しても、実際に自分のサイトで動かすと「テーブルが見つからない」「数日後にHTML構造が変わって止まる」「PCをつけっぱなしにできず定時実行が維持できない」という壁にぶつかり、結局また手作業に戻ってしまう方が少なくありません。
この記事では、つまずきやすい箇所を次の3つに絞って順番に対処します。1つ目は対象サイトが静的か動的かの判定、2つ目はPCを常駐させずに定時実行を維持する方法、3つ目は通知を見落としても止まったことに気づける仕組みです。実装コードは実際に動くものだけを載せ、動かした後に何が起きるかまで書いています。
静的サイトか動的サイトかを1分で見分ける
結論として、対象ページの「ページのソースを表示」を開き、取得したい数値や見出しの文字列が含まれているかをCtrl+Fで検索すれば、静的か動的かは1分で判定できます。
この判定を飛ばしてコードを書き始めると、Requestsで取得したHTMLの中に目的のデータがそもそも存在せず、何を直してもテーブルが見つからないという無駄な試行錯誤に時間を使うことになります。先に判定しておくことで、どちらの道具を使うべきかを最初に決められます。
- 確認したい対象ページをブラウザ(Chromeなど)で開きます。ログインが必要なページは、この時点ではログイン後の画面で確認してください。
- ページの何もないところで右クリックし、「ページのソースを表示」を選びます(ショートカットはCtrl+U)。開発者ツールの「検証」ではなく、必ず「ページのソースを表示」を使います。検証で見える要素はブラウザがJavaScript実行後に書き換えた結果なので、Requestsが取得するHTMLとは一致しないことがあるためです。
- 表示されたソースの画面でCtrl+Fを押し、取得したい表の見出しや価格の数字の一部など、具体的な文字列を検索します。
- 文字列が見つかれば、そのページは静的です。Requests+BeautifulSoupでそのまま取得できます。
- 文字列が見つからなければ、そのデータはJavaScriptによって後から追加されています。Requestsでは取得できないため、Seleniumなどブラウザを操作する方法に切り替えるか、対象を変えるかを検討します。
ここで判断を誤ったまま実装を進めると、実行はエラーなく終わるのに保存されたCSVが0行、あるいはヘッダー行しかない状態になります。エラーメッセージが出ないため原因に気づきにくく、「コードは合っているはずなのにデータが空」という状態で時間を使ってしまいがちです。CSVの行数が想定どおりかを最初の実行で必ず目視確認する習慣をつけておくと、この種のつまずきを早い段階で防げます。基礎的な考え方の整理には10分で分かるPythonによるデータ収集の自動化も参考になります。
収集→整形→保存→通知の最小構成を実装する
結論として、収集・整形・保存までは1本のPythonファイルで完結させ、そこに定時実行を組み合わせるのが最小構成です。機能を盛り込みすぎず、まず動く状態を作ることを優先します。
以下はテーブルを取得してCSVに保存するコードです。静的サイトであることが前提になります。
import requests
from bs4 import BeautifulSoup
import pandas as pd
from datetime import datetime
TARGET_URL = "https://example.com/data-table" # 対象URLに変更
OUTPUT_FILE = f"data_{datetime.now().strftime('%Y%m%d')}.csv"
def scrape_table(url):
headers = {"User-Agent": "Mozilla/5.0"}
res = requests.get(url, headers=headers, timeout=10)
res.raise_for_status() # HTTPエラーをここで例外にする
soup = BeautifulSoup(res.text, "html.parser")
table = soup.find("table")
if table is None:
raise ValueError("テーブルが見つかりませんでした")
df = pd.read_html(str(table))[0]
df.to_csv(OUTPUT_FILE, index=False, encoding="utf-8-sig")
print(f"保存完了: {OUTPUT_FILE}({len(df)}行)")
return df
if __name__ == "__main__":
scrape_table(TARGET_URL)
実装の手順と、詰まりやすい箇所は次のとおりです。
- ターミナルで
pip install requests beautifulsoup4 pandas lxmlを実行します。pd.read_htmlは内部でlxmlなどの解析ライブラリを使うため、これを入れ忘れるとImportErrorが出ます。 - 上記のコードを
scraper.pyとして保存します。 TARGET_URLを前章で静的と確認したページのURLに書き換えます。- 一度だけ手動で
python scraper.pyを実行し、生成されたCSVをExcelなどで開いて行数と内容を目視確認します。この手動確認を飛ばして定時実行だけ組むと、空のCSVが毎朝無言で溜まっていくという事態に気づけません。 - CSVをExcelで開いたときに文字が崩れる場合は、
encoding="utf-8-sig"が効いているか確認します。対象サイトの文字コードがShift-JISの場合はres.encoding = "shift_jis"を明示的に指定する必要があります。 raise_for_status()を外さないようにします。これを外すと、サイト側が403や500のエラーページを返しているだけなのに、そのエラーページのHTMLをテーブル扱いしようとして別のわかりにくいエラーになります。
保存までできたら、次は定時実行です。Pythonのscheduleライブラリを使うと次のように書けます。
import schedule
import time
from scraper import scrape_table, TARGET_URL
def job():
try:
scrape_table(TARGET_URL)
except Exception as e:
print(f"エラー: {e}")
schedule.every().day.at("09:00").do(job)
while True:
schedule.run_pending()
time.sleep(30)
このコードは「PCの電源が入っていて、このPythonプロセスがずっと起動している」ことが前提です。この前提がどこで崩れるかを次の章で扱います。整形や実務での活用パターンをさらに体系的に学びたい場合はPython最速データ収集術|技術評論社も参考になります。
PCをつけっぱなしにできない人のための常駐の選択肢
結論として、scheduleとwhileループだけの構成は、PCがスリープしたり再起動したりすると止まります。常駐の方法は別に用意する必要があります。
前章のコードは、ノートPCを閉じてスリープさせた瞬間に時間が進まなくなり、9時になっても何も起きません。復帰後にまとめて実行されることもあれば、その日の分がそのまま飛ぶこともあり、挙動はOSの省電力設定に左右されます。さらにPCを再起動すると、このPythonプロセス自体が起動していないため、誰かが手動で再実行しない限り翌朝も動きません。これが「動かし続ける」段階で最も多いつまずきです。
私自身は、Webの収集やAIでの分類・下書き作成、LINEへのWebhook通知、Cloudflare Workerやデータベース(Supabase)を組み合わせた定時タスクを複数本、ローカルPCの定時実行の仕組みで無人運用しています。公開や送信の最終判断は人が確認する設計にしていますが、収集から通知までの流れ自体は無人で回っています。この規模でも、個人のPC1台とタスクスケジューラで成立しています。
個人がまず試せる選択肢を整理すると次のとおりです。
| 方式 | 向いている人 | 詰まりやすい点 |
|---|---|---|
| Windowsタスクスケジューラ | 普段使いのPCで試したい人 | 電源オプションでスリープを無効化しないと実行されない。スリープ解除の許可設定を入れ忘れやすい |
| cron(Mac・Linux) | Mac常用者、サーバー経験がある人 | Macはスリープで止まる点はWindowsと同じ。環境変数がcronの実行環境に引き継がれないことがある |
| VPS(常時起動のサーバー) | 複数のサイトを長期運用したい人 | 自分で契約・設定が必要。費用が発生し続ける |
まずはPCのタスクスケジューラで試す場合の手順です。
- Windowsの検索から「タスクスケジューラ」を開きます。
- 右側の「基本タスクの作成」を選び、名前を付けます。
- トリガーで「毎日」を選び、実行したい時刻(例えば9時)を指定します。
- 操作で「プログラムの開始」を選び、プログラムの欄にPythonの実行ファイルのフルパス、引数の欄にscraper.pyのフルパスを入力します。パスが通っていないと「ファイルが見つかりません」という分かりにくいエラーで止まります。
- 作成したタスクを右クリックして「プロパティ」を開き、「条件」タブにある「スリープを解除してタスクを実行する」にチェックを入れます。ここを見落とすと、PCがスリープしている時間帯は実行されずに終わります。
- 同じ「条件」タブの「コンピューターをAC電源で使用している場合のみ」のチェックを、ノートPCで使う場合は外しておきます。デフォルトのままだとバッテリー駆動時に実行されません。
- 設定後、タスクを右クリックして「実行」を選び、手動でも正しく動くかをその場で確認します。
HTML構造が変わって止まったときに気づく仕組み
結論として、通知メールが届いていることと、データが正しく取れていることは別の話なので、取得できた行数を毎回チェックするコードを入れておく必要があります。
サイトがリニューアルされてクラス名やタグの構造が変わると、soup.find("table")がNoneになって例外が発生し、エラー通知が届くケースはまだ気づきやすい方です。やっかいなのは、構造が一部だけ変わり、空のテーブルや1行だけのテーブルがエラーなく取得できてしまうケースです。この場合、プログラムは正常終了したと判断し、「収集完了」という通知が届いたまま、中身は空という状態になります。通知の有無だけを見ていると、この種の停止には気づけません。
対策として、取得した行数が0の場合や、前回保存したCSVと比べて大きく減っている場合はエラー扱いにするチェックを入れます。
import os
import pandas as pd
def validate(df, prev_file=None, min_ratio=0.5):
if len(df) == 0:
raise ValueError("取得件数が0件です。HTML構造が変わっている可能性があります")
if prev_file and os.path.exists(prev_file):
prev_df = pd.read_csv(prev_file)
if len(prev_df) > 0 and len(df) < len(prev_df) * min_ratio:
raise ValueError(
f"前回{len(prev_df)}行に対し今回{len(df)}行。"
"構造変更による取得漏れの可能性があります"
)
このチェックをscrape_tableの後に呼び出し、前日分のファイルパスを渡しておけば、件数が極端に減った日だけエラー通知が飛ぶようになります。しきい値(上の例では前回の50%未満)は、対象サイトのデータが日によってどの程度増減するかを見ながら、自分の業務に合わせて調整してください。
もう一つ大事なのは、通知そのものを見落とした場合にどう気づくかです。毎朝必ず確認する業務にこのデータを使っているなら、1日分の欠落でもその日の判断に影響します。一方で週に一度の集計にしか使わないデータであれば、数日分の欠落に気づくのが翌週になっても実害が出にくい、というように、許容できる見落とし期間は業務の使い方によって変わります。自分の運用では、どのくらい通知が来なかったら実害が出るかをあらかじめ決めておき、通知アプリの未読だけに頼らず、週に一度はCSVの最終更新日を自分の目で確認する習慣を入れておくと安全です。
自社で実際に動かしている無人運用の実測
結論として、私はこの種の仕組みを特別なサーバーではなく、普段使っているWindows PCのタスクスケジューラとPythonだけで、複数の無人運用を継続しています。
代表例の一つが、日本株の監視ボードです。寄り付き前の板情報を機械が読み、候補を選び、結果を毎日採点して公開するところまでを無人で回しています。収集・選定・通知・採点・公開までの一連の流れに人の手は入っていません(funnel.gpirot.comで公開しています)。
構成はシンプルで、サイトの公開先はCloudflare Pages、定時処理はWindowsのタスクスケジューラとPython、データの保存先はSupabaseです。この日本株の監視ボードに加えて、Webの収集やAIによる分類と下書き作成、LINEへの通知などを含めた定時タスクを複数本運用しており、公開や送信の最終確認だけは人が行う設計にしています。
個人のPC1台でも、ここまでの無人運用は成立します。
次の一歩:自分のデータで試すなら
まずやることは、この記事のscraper.pyをそのまま自分の対象サイトに差し替えて、手元で一度動かしてみることです。
scraper.pyをコピーし、作業用のフォルダに置きます。TARGET_URLを、1章の方法で静的と確認した自分の対象ページのURLに書き換えます。- 手動で一度実行し、CSVの行数と中身を自分の目で確認します。
- 問題がなければ、4章の手順でタスクスケジューラに登録します。
- 5章の行数チェックを組み込み、最初の数日は通知が正しく届くか、構造変更の検知が動くかを意識して見ておきます。
もし対象がWebページではなく、請求書や領収書、名簿などの紙やPDFで、そこから項目や明細を拾ってExcelに貼り直す作業が中心なのであれば、スクレイピングではなくOCRとAIによる転記の仕組みが向いています。その場合は、請求書・領収書・名簿から項目と明細を取り出してCSVやTSVにする公開ツールAI転記ツールを先に試してみることをおすすめします。

openpyxlでExcel自動化、学んだ後に詰まる運用を実例で埋める
CSVの文字化けと列ずれが止まる場所をPythonのコードで具体的に潰す手順
SEO記事の量産を自動化する前に工程のつなぎ目を見直す手順