このページで解説している内容は、以下の YouTube 動画の解説で見ることができます。
【Python入門】スクレイピングを定期的に実行する(schedule)

スクレイピングを定期的に実行する(schedule)
Webページから特定の情報を収集するスクレイピング処理は、時間経過とともに更新されるデータ(天気・株価・ニュースなど)では定期的に実行したいケースが多くあります。Pythonでは、スクレイピング自体をモジュール化しておき、scheduleライブラリを用いることで、指定した時間や間隔ごとに自動でスクレイピングを実行する仕組みを簡単に構築できます。
ここでは、scraping2.html を対象としたスクレイピングを、毎日や一定時間おきに実行する方法を例示し、JupyterLab 上での利用や無限ループによるジョブ管理の手法を解説します。

プログラムのダウンロード
「ダウンロード」から、JupyterLab で実行できるサンプルプログラムがダウンロードできます。ファイルは、ESET Endpoint Securityでウイルスチェックをしておりますが、ダウンロードとプログラムの実行は自己責任でお願いいたします。
1.スクレイピング対象のページ
今回のサンプルでは、前回のコンテンツで使用した「scraping2.html」を引き続き利用します。このHTMLファイルはURL「https://www.study-pg.com/scraping/scraping2.html」に配置しています。HTMLの構造は以下のとおりです。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Scraping Demo Page 2</title>
</head>
<body>
<h1>Scraping Demo Page 2</h1>
<p>This page is intended for scraping demonstration.</p>
<ul>
<li>
<span class="release-number"><a href="release01.html">Demo Release 1.0</a></span>
<span class="release-date">January 10, 2022</span>
</li>
<li>
<span class="release-number"><a href="release02.html">Demo Release 2.0</a></span>
<span class="release-date">March 15, 2022</span>
</li>
<li>
<span class="release-number"><a href="release03.html">Demo Release 3.0</a></span>
<span class="release-date">July 22, 2022</span>
</li>
</ul>
<p>
For further details, visit <a href="https://www.example.com">Example Site</a>.
</p>
</body>
</html>
このHTMLファイルを開くと下図のように表示されます。

上記のリリース番号や日付を定期的に取得したいというシナリオを考え、scheduleライブラリを用いた自動実行を行います。
2.scheduleライブラリの概要
schedule
は、あらかじめ設定した時間や曜日、あるいは間隔に応じて指定した関数を呼び出してくれるライブラリです。たとえば以下のように設定できます。
記述例 | 動作 |
---|---|
schedule.every().minute.do(func) | 毎分 func を実行する。 |
schedule.every().hour.do(func) | 毎時 func を実行する。 |
schedule.every(3).hours.do(func) | 3時間おきに func を実行する。 |
schedule.every().day.at("10:00").do(func) | 毎日 10:00に func を実行する。 |
schedule.every().monday.at("09:00").do(func) | 毎週月曜9:00に func を実行する。 |
設定したスケジュールは schedule.run_pending()
を呼ぶたびに「実行すべきタイミング」かどうかを判定し、該当するならその関数を自動で呼び出します。実際のプログラムでは、無限ループにより定期的に run_pending()
を呼び出し、間隔を空けるために time.sleep()
を用いることが一般的です。
インストール方法
pip install schedule
あるいは、Anaconda/Miniconda環境であれば
conda install -y -c conda-forge schedule
3.スクレイピング処理
以下のプログラムでは、Requests+BeautifulSoupでscraping2.htmlを取得し、「release-number」クラスのspanと「release-date」クラスのspanを探して抽出します。最後に取得日時を出力する機能を追加し、定期実行時にログを兼ねられるようにします。
import requests
from bs4 import BeautifulSoup
import datetime
def scrape_releases():
"""
scraping2.htmlを取得し、release-numberとrelease-dateの情報を抽出して表示。
最後に現在時刻を表示する。
"""
url = "https://www.study-pg.com/scraping/scraping2.html"
resp = requests.get(url)
soup = BeautifulSoup(resp.text, 'html.parser')
# 抽出結果をリストに格納
releases = []
# <li>タグを順に処理し、内部のspanを探す
for li in soup.find_all('li'):
span_num = li.find('span', class_='release-number')
span_date = li.find('span', class_='release-date')
if span_num and span_date:
a_tag = span_num.find('a')
if a_tag:
name_text = a_tag.get_text(strip=True)
date_text = span_date.get_text(strip=True)
releases.append((name_text, date_text))
# 取得データを表示
print("=== Scraping Results ===")
for name, date in releases:
print(f"{name:20} {date}")
# 現在時刻を出力
now = datetime.datetime.now()
print(f"Scraped at: {now}\n")
scrape_releases()
実行結果
=== Scraping Results ===
Demo Release 1.0 January 10, 2022
Demo Release 2.0 March 15, 2022
Demo Release 3.0 July 22, 2022
Scraped at: 2025-03-09 15:43:48.911345
解説
scrape_releases
関数 : スクレイピング本体。RequestsでHTML取得 → BeautifulSoupで解析 → <li>
タグ内のrelease-number
, release-date
を取り出す。
4.scheduleで定期的に実行
次に、scheduleライブラリを使って上記のscrape_releases()
を定期的に呼び出すファイルを作りましょう。以下は毎分1回スクレイピングする例です。
import requests
from bs4 import BeautifulSoup
import datetime
import schedule
import time
def scrape_releases():
"""
scraping2.htmlを取得し、release-numberとrelease-dateの情報を抽出して表示。
最後に現在時刻を表示する。
"""
url = "https://www.study-pg.com/scraping/scraping2.html"
resp = requests.get(url)
soup = BeautifulSoup(resp.text, 'html.parser')
# 抽出結果をリストに格納
releases = []
# <li>タグを順に処理し、内部のspanを探す
for li in soup.find_all('li'):
span_num = li.find('span', class_='release-number')
span_date = li.find('span', class_='release-date')
if span_num and span_date:
a_tag = span_num.find('a')
if a_tag:
name_text = a_tag.get_text(strip=True)
date_text = span_date.get_text(strip=True)
releases.append((name_text, date_text))
# 取得データを表示
print("=== Scraping Results ===")
for name, date in releases:
print(f"{name:20} {date}")
# 現在時刻を出力
now = datetime.datetime.now()
print(f"Scraped at: {now}\n")
def run_scheduler():
"""
scheduleライブラリを使い、scrape_releasesを毎分1回実行するサンプル関数。
Ctrl+Cで終了するまでループする。
"""
# 毎分呼び出し
schedule.every().minute.do(scrape_releases)
print("Press Ctrl+C or ■ to exit. Starting scheduled tasks...")
while True:
schedule.run_pending() # スケジュールされたタスクをチェック
time.sleep(1) # 1秒インターバルでループ
run_scheduler()
実行結果
Press Ctrl+C or ■ to exit. Starting scheduled tasks...
=== Scraping Results ===
Demo Release 1.0 January 10, 2022
Demo Release 2.0 March 15, 2022
Demo Release 3.0 July 22, 2022
Scraped at: 2025-03-09 15:55:54.353820
=== Scraping Results ===
Demo Release 1.0 January 10, 2022
Demo Release 2.0 March 15, 2022
Demo Release 3.0 July 22, 2022
Scraped at: 2025-03-09 15:56:20.443381
・・・(続く)
解説
schedule.every().minute.do(scrape_releases)
:scrape_releases
を毎分呼び出す設定。while True:
: 常時ループを走らせ、schedule.run_pending()
を毎秒実行。Ctrl+C
: ターミナルで強制終了。JyupyterLabの場合は「■」ボタンで強制終了させます。
スケジュールのバリエーション
schedule.every().day.at("10:00").do(scrape_releases) # 毎日10:00に
schedule.every(3).hours.do(scrape_releases) # 3時間おきに
schedule.every().monday.at("09:30").do(scrape_releases)# 毎週月曜9:30に
実運用では間隔を十分あけて相手サーバへの負荷を抑える、あるいは夜間のみ実行、などの工夫が必要です。
5.注意点と応用
- 終了方法 : ターミナル上で動かす場合、
Ctrl + C
でプログラムを強制終了が一般的。JyupyterLabの場合は「■」ボタンで強制終了させます。 - ログ出力・例外処理 : 定期ジョブを安定稼働させるために、スクレイピング失敗時のリトライやログ保存が望ましい。
- サーバへの負荷 : 頻繁すぎるアクセスは禁止事項に抵触する可能性があるため、スクレイピング対象サイトの利用規約を必ずチェックし、短い間隔で連続アクセスしないように注意。
- ターミナル・サーバ上で動かす : JupyterLab上では無限ループが使いにくいので、通常はスクリプトをターミナルから直接実行し、デーモンとして動かす、あるいはcronなど他のジョブ管理システムと連携する場合も多い。
まとめ
scheduleライブラリを使えば、scraping2.htmlのようなWebページを定期的にスクレイピングし、情報を自動で収集できます。たとえばニュースサイトの最新記事や天気予報、株価といった更新頻度の高いデータも、スケジュール設定だけで拡張可能です。
今後、ログの保管やDBへの格納、可視化との連携なども盛り込みながら本格的なアプリケーションに成長させることができます。スクレイピング対象サイトの利用規約や負荷対策に十分配慮しながら、自動収集の仕組みを構築していきましょう。