欲しい商品の入荷情報をずっと待ち続けた経験、ありませんか?公式サイトを何度も開いては「まだ入荷してないか…」を繰り返すやつです。
エンジニアではない友人が、ずっと品切れ続きのバッグを追いかけていて「入荷したら即買いたいんだけど、毎日確認するの疲れた…」とこぼしていました。
あまりに不憫だったのでLINEで通知がくるシステムを友人用作ったので、その備忘録を書かせていただきます。
作ったシステムはLINEにこんな感じです。
スクリーンショットは商品名など一部を伏せていますが、商品が追加されるたびに通知が届き、いまのところ安定して動いています。
実際に作ってみると、WebスクレイピングとLINE APIを組み合わせることで、新商品が追加された瞬間に通知を送るシステムが無料で意外と簡単に作れました。今回はその実装をまとめます。同じようなものを作りたい方の参考になれば幸いです。
結論(先出し)
以下の構成で、監視したいECサイトの新着商品をLINEへ自動通知するシステムを無料で構築できます。
- スクレイピング:requests + BeautifulSoup4 で商品データを取得
- 差分検出:JSONファイルに前回の商品一覧を保存し、新着を検知
- 通知:LINE Messaging APIのMulticast送信で複数人に一斉通知
- 運用:Railwayにデプロイして24時間監視を自動化
ソースコードはGitHubで公開しています:konPlantPG/bag_reserch
システム全体の仕組み
作ったシステムはシンプルに4つのステップで動きます。
- 定期監視:商品ページを一定間隔でチェック
- 新商品検出:前回保存した商品リストと比較して差分を検出
- LINE通知:新商品が見つかったらLINEに即時通知
- クラウド運用:Railwayで24時間稼働させる
使った技術スタックはこちらです。
| カテゴリ | 技術 |
|---|---|
| 言語 | Python 3.13 |
| HTTPクライアント | requests |
| HTMLパーサー | BeautifulSoup4 |
| Webhookサーバー | Flask |
| 通知 | LINE Messaging API |
| ホスティング | Railway(無料枠) |
| データ永続化 | JSON ファイル |
商品データの取得(スクレイピング)
最初に悩んだのが、JavaScriptで動的にコンテンツが生成されているサイトからのデータ取得です。単純にHTMLを取得しても、肝心の商品一覧が空のことがあります。
最初は普通に requests.get() でHTMLを取得しようとしたのですが、何も商品データが返ってきませんでした。原因はUser-Agentの設定漏れで、デフォルトのrequestsはBotだと判定されて弾かれていました。ヘッダーを追加したら無事通るようになりました。
さらに、このサイトはJavaScriptで動的にコンテンツを生成しているため、HTMLを取得しただけでは商品一覧が空のことがあります。調べてみると、ページ内の <script> タグの中にJSON形式で商品データが埋め込まれていることがわかりました。このJSONを解析することで、商品名・価格などの情報を一括取得できます。
import requests
import json
from bs4 import BeautifulSoup
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
def get_current_products(url):
"""指定されたURLから商品データを取得します。"""
try:
response = requests.get(url, headers=HEADERS, timeout=30)
response.raise_for_status()
soup = BeautifulSoup(response.content, 'html.parser')
# ページ内の埋め込みJSONから商品データを取得
script_tag = soup.find('script', id='product-data')
if script_tag:
data = json.loads(script_tag.string)
# 商品データをdict形式({商品ID: 商品情報})に整形して返す
return {item["id"]: item for item in data.get("products", [])}
# フォールバック: HTMLの要素から直接抽出
return extract_products_from_html(soup, url)
except Exception as e:
print(f"商品データ取得エラー: {e}")
return {}
埋め込みJSONが見つからなかった場合のフォールバックも用意しています。サイトのDOM構造はいつ変わるかわからないので、二段階にしておくと少し安心です。詳細な実装はGitHubのコードを参照してください。
新着商品の差分検出
新商品かどうかを判定するのに使ったのは、シンプルなJSONファイルによる永続化です。前回チェック時の商品IDをJSONに保存しておき、今回取得した商品IDと比較します。
import json
import os
DATA_FILE = "known_products.json"
def load_known_products():
if os.path.exists(DATA_FILE):
with open(DATA_FILE, "r") as f:
return json.load(f)
return {}
def save_known_products(products):
with open(DATA_FILE, "w") as f:
json.dump(products, f, ensure_ascii=False, indent=2)
def detect_new_products(current_products, known_products):
"""現在の商品リストから新着だけを返す"""
return {
pid: product
for pid, product in current_products.items()
if pid not in known_products
}
DBを使わずJSONファイルで済ませたのは、個人利用のシンプルなBotだったので過剰設計を避けたかったからです。
LINE通知機能
LINE Messaging APIのMulticastを使うと、複数のユーザーIDに一斉にメッセージを送れます。グループで使いたいときに便利でした。
import requests
import os
CHANNEL_ACCESS_TOKEN = os.getenv('LINE_CHANNEL_ACCESS_TOKEN')
RECIPIENT_USER_IDS = [
uid.strip()
for uid in os.getenv('LINE_USER_IDS', '').split(',')
if uid.strip()
]
def send_line_multicast_message(message):
"""複数のユーザーに一斉にメッセージを送信"""
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {CHANNEL_ACCESS_TOKEN}'
}
payload = {
'to': RECIPIENT_USER_IDS,
'messages': [{'type': 'text', 'text': message}]
}
response = requests.post(
'https://api.line.me/v2/bot/message/multicast',
headers=headers,
json=payload
)
return response.status_code == 200
友人のスマホに届く通知はこんな感じです。
🆕 新商品が追加されました!
📂 カテゴリ: バッグ
🏷️ 商品名: ○○バッグ
💰 価格: ¥10,000
📍 監視URL: https://example.com/products
「めちゃくちゃわかりやすい!」と喜ばれました(笑)。絵文字を入れると視認性が上がるのでおすすめです。通知文はこう組み立てています。
def build_notification_message(product: dict) -> str:
name = product.get("name", "商品")
category = product.get("category", "カテゴリ")
price = product.get("price", "")
url = product.get("url", "https://example.com")
return (
"🆕 新商品が追加されました!\n"
f"📂 カテゴリ: {category}\n"
f"🏷️ 商品名: {name}\n"
f"💰 価格: {price}\n"
f"📍 監視URL: {url}"
)
LINE Messaging APIの設定手順
LINE Messaging APIの初期設定は以下の流れです。
- LINE Developers Console にアクセスし、プロバイダーを作成
- Messaging APIチャンネルを新規作成
- チャンネルアクセストークン(長期)を発行
- 友人(通知受け取り側)に公式アカウントを友だち追加してもらい、Webhook経由でユーザーIDを取得
- 取得したユーザーIDを環境変数
LINE_USER_IDSにカンマ区切りで設定
Webhookサーバー(Flask)でフォローイベントを受け取り、ユーザーIDをログに出力する方法でIDを収集しました。
Railwayへのデプロイ
クラウド運用には Railway を選びました。無料枠があり、個人の小規模Botなら実質タダで動かせます(料金プランは変わることがあるのでRailway公式で確認してください)。
デプロイの手順はシンプルです。
- GitHubリポジトリとRailwayを連携(mainブランチへのpushで自動デプロイ)
- Railway管理画面で環境変数を設定:
LINE_CHANNEL_ACCESS_TOKEN:LINEのアクセストークンLINE_USER_IDS:通知先のユーザーID(カンマ区切り)MONITOR_URLS:監視対象URL(カンマ区切り)CHECK_INTERVAL:チェック間隔(秒)
Procfileに起動コマンドを記述:worker: python main_cloud.py
機密情報はすべて環境変数で管理し、コードには一切書いていません。os.getenv() で取得するパターンは環境が変わっても同じですね。
まとめ
この記事では、PythonでWebスクレイピング + LINE通知ボットを作った実装をまとめました。
- requests + BeautifulSoup4 で商品データを取得
- JSONファイルで前回状態を保存し、差分で新着を検出
- LINE Messaging APIのMulticastで複数人に一斉通知
- RailwayにデプロイしてGitHub連携で自動運用
デプロイしてから友人は今も使い続けていて、入荷があるたびLINEに通知が来るそうです。「もうサイト確認しなくていいの最高」と言っていました。身近な人の課題をコードで解決できたのは、純粋に嬉しかったです。同じようなものを作りたい方は、ぜひGitHubのコードも参考にしてみてください。
ご質問があればコメントまでどうぞ。参考になれば幸いです。
