PythonでWebsite Watcherを自作する:価格変動やニュースをTelegramで追跡

Python tutorial - IT technology blog
Python tutorial - IT technology blog

なぜWebサイト監視ツールを自作すべきなのか?

大事な会議の15分間に、iPhoneが300万ドンも安くなっていたのに、F5キーでページを更新できず買い逃してしまったことがあります。その時、手動で価格やニュースをチェックし続けるのは非常に労力がかかり、挫折しやすい作業だと痛感しました。

Distill.ioやVisualpingのようなツールは優秀ですが、無料版は月に30回程度のチェック制限があることが多いです。5分おきに継続してチェックしたい場合、月に15ドル〜25ドルほどかかることもあります。Pythonを使えば、広告フィルタリングをカスタマイズした高機能なクローンを自作でき、完全に無料でTelegramに通知を送ることができます。

多くの人はデータのスクレイピングBeautifulSoupを使っています。しかし、ReactやVueを使用している現代的なWebサイトでは、BeautifulSoupだと真っ白なHTMLフレームしか返ってこないことがよくあります。そこで今回はPlaywrightを採用します。このライブラリは本物のブラウザをシミュレートし、JavaScriptのレンダリングが完了してからデータを取得するため、難易度の高いWebサイトでも処理が可能です。

環境構築

まず、Python 3.8以上がインストールされていることを確認してください。ブラウジング用にPlaywright、Telegram APIとの連携用にrequestsライブラリを使用します。

# ライブラリの衝突を避けるために仮想環境を作成
python -m venv venv
source venv/bin/activate  # Windowsの場合: venv\Scripts\activate

# Playwrightとrequestsをインストール
pip install playwright requests

# Playwright用のChromiumブラウザをダウンロード(約100-150MB)
playwright install chromium

Telegramについては、@BotFatherとチャットし、/newbotと入力してAPI Tokenを取得してください。ボットに一度メッセージを送信してから、https://api.telegram.org/bot<YOUR_TOKEN>/getUpdatesにアクセスして、自分のchat_idを確認するのを忘れないでください。

コードの実装詳細

スクリプトは「アクセス -> 抽出 -> 比較 -> 通知」というモデルで動作します。

1. Playwrightによるデータ取得

HTML全体を取得するのではなく、特定のSelector(価格のクラスやタイトルのIDなど)を直接指定することをお勧めします。これにより、タイムスタンプや広告バナーが変わっただけで通知が飛んでしまうのを防げます。

import asyncio
from playwright.async_api import async_playwright

async def get_website_content(url, selector):
    async with async_playwright() as p:
        # ヘッドレズモードで実行(ブラウザウィンドウを表示しない)
        browser = await p.chromium.launch(headless=True)
        page = await browser.new_page()
        
        # アンチボットシステムによるブロックを避けるためにUser-Agentを偽装
        await page.set_extra_http_headers({"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0"})
        
        try:
            await page.goto(url, wait_until="networkidle", timeout=60000)
            await page.wait_for_selector(selector)
            content = await page.inner_text(selector)
            return content.strip()
        except Exception as e:
            print(f"アクセスエラー: {e}")
            return None
        finally:
            await browser.close()

2. コンテンツ変更のチェック

更新があったかどうかを判断する最も簡単な方法は、前回の実行結果を.txtファイルに保存しておくことです。新しい内容が古い内容と異なる場合、ファイルを更新して通知をトリガーします。

import os

def has_changed(new_content, filename="last_state.txt"):
    if not os.path.exists(filename):
        with open(filename, "w", encoding="utf-8") as f:
            f.write(new_content)
        return False
    
    with open(filename, "r", encoding="utf-8") as f:
        old_content = f.read()
    
    if new_content != old_content:
        with open(filename, "w", encoding="utf-8") as f:
            f.write(new_content)
        return True
    return False

3. Telegram経由で通知を送信

requestsライブラリを使えば、数行のコードでスマートフォンに即座に通知をプッシュできます。

import requests

def notify_telegram(message, token, chat_id):
    url = f"https://api.telegram.org/bot{token}/sendMessage"
    data = {"chat_id": chat_id, "text": message, "parse_mode": "HTML"}
    return requests.post(url, data=data)

各パーツの統合

以下はプロセス全体を動かすmain関数です。実際のURLとSelectorに書き換えるだけで、すぐに実行できます。

async def main():
    CONFIG = {
        "url": "https://tiki.vn/dien-thoai-iphone-15-pro-max-p251643444.html",
        "selector": ".price-and-icon",
        "token": "YOUR_TOKEN",
        "chat_id": "YOUR_ID"
    }

    print("Webサイトをチェック中...")
    current_data = await get_website_content(CONFIG["url"], CONFIG["selector"])

    if current_data and has_changed(current_data):
        msg = f"🚀 <b>新しい更新があります!</b>\n値: {current_data}\nリンク: {CONFIG['url']}"
        notify_telegram(msg, CONFIG["token"], CONFIG["chat_id"])
        print("通知を送信しました!")
    else:
        print("変更はありません。")

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

運用上のヒント

スクリプトを安定して動作させ、IP制限を避けるために、以下の3点に注意してください。

  • チェック頻度: 1〜2秒ごとのチェックは絶対に避けてください。ShopeeやAmazonのような大手サイトでは、すぐにIPがブロックされます。15〜30分周期が最も妥当な数値です。
  • 自動実行: VPSをお持ちの場合はcrontabを使用してください。そうでない場合は、GitHub Actionsを活用すれば、個人のPCをつけっぱなしにすることなく、24時間365日無料でスクリプトを実行できます。
  • 動的クラスの処理: 一部のWebサイトでは、ビルドのたびにクラス名が頻繁に変わります。その場合は、xpathdata-testidのような固定属性を使用して、要素をより正確に特定してください。

監視ボットを自作することは、お得な情報を手に入れるだけでなく、ブラウザの動作フローや実際のデータ処理を理解するための素晴らしい練習になります。ぜひ挑戦してみてください!

Share: