7-4. 【自動化】競合ECサイト価格自動リサーチ&Discord通知Bot

第5章(実践開発:スクレイピング・データ分析・生成AI活用)で学んだWebスクレイピング技術を応用し、競合ECサイトやターゲット商品の価格変動・在庫状況を自動パトロールし、セール開始や価格改定を検知してDiscordへ自動通知するボットを構築します。

1. システムの設計と要件

本自動化ツールは、以下の実務的な技術要素を組み合わせて実現します。

  1. Playwright(ブラウザ自動化): JavaScriptによって動的に価格が書き換わるモダンなECサイトや、ログインが必要なページに対応。
  2. BeautifulSoup4(HTML解析): 抽出したHTMLから、商品名・価格・在庫ステータスを高速にパース。
  3. Discord Webhook(通知連携): 差分やターゲット価格を下回ったイベントを即座にチャットへプッシュ通知。
  4. cron(定期実行): サーバー上で定期的にスクリプトを自動実行し、24時間の監視体制を構築。

2. 実装コード(ec_price_watcher.py)

以下のコードを ec_price_watcher.py というファイル名で保存し、手元で動作させてみましょう(事前に pip install playwright beautifulsoup4 requests および playwright install が必要です)。

Python

import asyncio
import requests
from bs4 import BeautifulSoup
from playwright.async_api import async_playwright

# --- 設定値 ---
TARGET_URL = "https://example.com/sample-product"  # 監視対象のECサイトURL
DISCORD_WEBHOOK_URL = "https://discord.com/api/webhooks/your_webhook_id/your_token"
TARGET_PRICE_THRESHOLD = 5000  # この金額を下回ったら特別通知する閾値

async def fetch_dynamic_html(url: str) -> str:
    """Playwrightを用いてJavaScript描画後のHTMLを取得する (第5章)"""
    async with async_playwright() as p:
        # ヘッドレスモードでブラウザを起動
        browser = await p.chromium.launch(headless=True)
        page = await browser.new_page()
        
        # ページにアクセスしてネットワークが落ち着くまで待機
        await page.goto(url, wait_until="networkidle")
        html_content = await page.content()
        await browser.close()
        return html_content

def parse_product_info(html: str):
    """BeautifulSoupで商品名と価格を抽出する (第5章)"""
    soup = BeautifulSoup(html, "html.parser")
    
    # サイトのHTML構造に合わせてセレクタを調整
    title_elem = soup.select_one(".product-title")
    price_elem = soup.select_one(".product-price")
    stock_elem = soup.select_one(".stock-status")
    
    title = title_elem.get_text(strip=True) if title_elem else "商品名不明"
    price_text = price_elem.get_text(strip=True) if price_elem else "0"
    
    # 価格文字列から数値のみを抽出 (例: "¥4,800(税込)" -> 4800)
    import re
    numeric_price = int("".join(re.findall(r'\d+', price_text)))
    
    stock_status = stock_elem.get_text(strip=True) if stock_elem else "在庫あり"
    
    return {
        "title": title,
        "price": numeric_price,
        "stock": stock_status
    }

def send_discord_notification(message: str):
    """Discord Webhook経由でメッセージをプッシュ通知する"""
    payload = {"content": message}
    try:
        response = requests.post(DISCORD_WEBHOOK_URL, json=payload)
        response.raise_for_status()
        print("📢 Discord通知を送信しました。")
    except requests.exceptions.RequestException as e:
        print(f"❌ Discord通知の送信に失敗しました: {e}")

async def main():
    print(f"🔍 競合ECサイトのリサーチを開始します: {TARGET_URL}")
    
    try:
        # 1. 動的HTMLの取得
        html = await fetch_dynamic_html(TARGET_URL)
        
        # 2. データの解析
        product = parse_product_info(html)
        print(f"取得結果: {product['title']} / ¥{product['price']:,} / {product['stock']}")
        
        # 3. 条件判定と通知
        if product["price"] <= TARGET_PRICE_THRESHOLD:
            msg = f"🔥 【価格下落アラート】\n監視対象商品が目標価格を下回りました!\n商品名: {product['title']}\n価格: ¥{product['price']:,}\nURL: {TARGET_URL}"
            send_discord_notification(msg)
        else:
            print(f"現在の価格(¥{product['price']:,})は目標値(¥{TARGET_PRICE_THRESHOLD:,})の範囲内です。")
            
    except Exception as e:
        print(f"❌ スクレイピング処理中にエラーが発生しました: {e}")

if __name__ == "__main__":
    asyncio.run(main())

3. サーバーでの定期実行(cron)設定

第8章(付録2)で解説したお名前.comサーバーやLinux環境のcronを使い、このスクリプトを1日数回自動実行させます。

Bash

# crontab -e で編集画面を開き、以下を追加(例: 3時間おきに実行)
0 */3 * * * /home/username/public_html/python-lab.net/venv/bin/python3 /home/username/public_html/python-lab.net/ec_price_watcher.py > /dev/null 2>&1

まとめ

本記事では、Playwrightによる動的ブラウザ操作とBeautifulSoupによるHTML解析を組み合わせ、競合ECサイトの価格変動を自動検知してDiscordへ通知する実用的なBotの構築を学びました。

コメント

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