Pythonで最新の株価を返すLINE BOT構築ガイド: ngrok, flask, yfinance

この記事では、無料で登録できる LINE 公式アカウントを使い、LINE アプリ上で有価証券のティッカー(銘柄コード)を入力すると最新の終値を返すプログラムを実装します。ngrok というツールを使って、ローカルマシンで動作する Web アプリをインターネットに公開します。Web アプリは Flask で作成します。うまくいくとちょっと感動すると思います。

1. おおまかな実装の流れ

LINE Bot を Python で実装するおおまかな流れは以下のようになります。

  1. LINE 公式アカウントの作成と設定
  2. 開発環境構築
  3. オウム返し LINE Bot の作成とメッセージ処理ロジックの実装
  4. 入力したティッカーの最新終値を返すプログラムの作成

2. 技術スタックの大まかな解説

項目説明
Pythonプログラミング言語
LINE Messaging APILINE 上でチャットボットを作成し、ユーザーと双方向のコミュニケーションを実現するための API。
FlaskPython で作られた軽量な Web フレームワークです。最小限の機能で Web アプリケーションを構築できます。
LINE Bot SDK for PythonLINE API との複雑なやり取りを抽象化し、より簡単なコードで LINE ボットの機能を実装できるように設計されたライブラリ。
ngrokエングロック。あなたの PC で動作している Web サーバー(Flask アプリケーションなど)を Web 上に公開し、LINE プラットフォームと連携させてテストできるようにします。
yfinanceYahoo! Finance の API から金融データを手軽に取得できるライブラリ。
Webhook URLLINE プラットフォームがイベント(メッセージなど)をあなたのサーバーに通知するための URL(配達先住所のようなもの)です。通知を受け取ったらサーバー側で処理を行います。

3. 環境構築

ここでは、Windows と Mac の両方で Python の環境をセットアップし、LINE Bot 開発の準備を進める手順を解説します。Python 3.12 以上をインストール済みとします。

3-1. LINE 公式アカウントの作成と設定

3-1-1. LINE Business ID の登録

https://www.lycbiz.com/jp/service/line-official-account/ にアクセスし、LINE アカウントまたはメールアドレスで登録します。デフォルトでは LINE ビジネス ID と個人の LINE アカウントはひも付いていません。本解説ではひも付けません。

3-1-2. LINE 公式アカウントの作成

次に、作成した Business ID でログインし、アカウント名、企業名、業種などの必要情報を入力します。

公式アカウントが作成されたら、「LINE Official Account Manager へ」のボタンを押して LINE 公式アカウントマネージャーにログインしてください。

ウェルカムで QR コードが表示されますので友だち登録しておきましょう。

友だち追加すると下図のように表示されます。何かメッセージを送っても表示されるのはデフォルトのメッセージです。これから任意のメッセージを返すボットを実装していきます。

3-1-3. Messaging API の有効化

ユーザーと双方向のコミュニケーションを実現するための API を有効化します。API(Application Programming Interface)はプログラム同士が連携するための窓口です。ここでは私たちのアプリと LINE 社が提供しているプラットフォームとの窓口です。

LINE 公式アカウントマネージャーで画面右上にある「設定」をクリックし(①)、左側の「Messaging API」を選択して「Messaging API を利用する」をクリックします(②)。

次に表示される画面で名前とメールアドレスを入力してください。

続いてプロバイダーを作成します。

プライバシーポリシーと利用規約の入力画面になりますが、ここは何も入力せず OK ボタンを押します。

次のような画面が表示されます。これで Messaging API が有効になりました。

3-1-4. API キーの取得

「Channel Secret」と「Channel Access Token」を取得します。これらは後でプログラムで使用します。

Messaging API の画面では表示されている Channel Secret をコピーします。Channel Access Token は LINE Developers コンソールから発行します。下図の枠囲みからリンクを開いてください。

「LINE Developers コンソール」で表示されるプロバイダーから先ほど作成したものを選択します。

「Messaging API 設定」タブを選択し、

画面の下の方にあるチャンネルアクセストークン欄で発行します。下の図はすでに発行後のためボタンが表示されていませんが、発行前であればここに表示されます。発行されたらアクセストークンをコピーして保存しておいてください。

3-1-5. 応答設定

次は応答設定を行います。デフォルトではユーザーからのメッセージに対して毎回一律応答するメッセージが設定されています。不要なのでオフにしておきます。また、ユーザーからのメッセージに対して、これから実装するプログラムで応答するための設定も行います。

LINE 公式アカウントマネージャーのチャット (①) タブで「Webhook」にチェックを入れ (②)「応答メッセージ」のチェックを外します (③)。

ここまでで LINE 側の設定が終わりましたので次はプログラム実装とサーバーの設定を行います。

3-2. LINE Bot SDK for Python とその他ライブラリのインストール

まずは仮想環境を有効にして、以下のライブラリをインストールします。

pip install line-bot-sdk flask yfinance python-dotenv

LINE Bot SDK for Python

  • Python で LINE Bot を作成するためのライブラリです。

Flask

  • Web サーバーを構築するために Flask をインストールします。

yfinance(株価ボットの場合):

  • 株価データを取得するために yfinance ライブラリをインストールします。

python-dotenv(環境変数管理):

  • Channel Access Token や Channel Secret などの機密情報を安全に管理するために使用します。

3-3. ngrok のインストールと設定

ngrok は、ローカルで開発した LINE ボットをインターネット上に公開するためのツールです。

  1. アカウントの登録 (無料)
  2. 登録後の Welcome 画面に OS ごとのインストール方法が表示されますので、案内に従ってダウンロード・インストールしてください。
  3. インストール後は ngrok config add-authtoken をターミナルで実行するのを忘れないでください。

4. オウム返し Bot の実装

これで環境が揃いましたので、Python と Flask を使って LINE Bot の基本的なメッセージ処理ロジックを実装する方法を解説します。まずはシンプルなオウム返し Bot です。

以下のような流れでオウム返しします。

flowchart TD
    A[ユーザーがメッセージ送信] --> B[LINEがcallback関数を呼び出し]
    B --> C{署名検証OK?}
    C -->|NG| D[エラー返却]
    C -->|OK| E[handle_message関数実行]
    E --> F[受信メッセージを取得]
    F --> G[同じメッセージで返信]
    G --> H[処理完了]

4-1. プロジェクト構造

まずは以下のディレクトリ構成を作成します。

project/
├── app.py
└── .env

4-2. 環境変数の設定

次はあなたの公式アカウントであることを証明するため、先ほどコピーしたチャンネルアクセストークンとチャンネルシークレットを使います。

セキュリティのため、チャンネルアクセストークンとチャンネルシークレットは直接コードに書き込まず、.env ファイルに記載し、python-dotenv ライブラリを使って読み込みます。

先ほどコピーしておいたチャンネルアクセストークンとチャンネルシークレットに置き換えてください。

CHANNEL_ACCESS_TOKEN='YOUR_CHANNEL_ACCESS_TOKEN'
CHANNEL_SECRET='YOUR_CHANNEL_SECRET'

4-3. app.py の実装

app.py に以下のコードを記述します。LINE アプリでユーザーがメッセージを送信したときに、このプログラムでボットが返事します。Flask という軽量 Web フレームワークを使います。

import os

from dotenv import load_dotenv
from flask import Flask, request, abort
from linebot.v3 import WebhookHandler
from linebot.v3.exceptions import InvalidSignatureError
from linebot.v3.messaging import Configuration, ApiClient, MessagingApi, ReplyMessageRequest, TextMessage
from linebot.v3.webhooks import MessageEvent, TextMessageContent

# 環境変数を読み込み
load_dotenv()

app = Flask(__name__)

# 環境変数の確認
channel_access_token = os.getenv('CHANNEL_ACCESS_TOKEN')
channel_secret = os.getenv('CHANNEL_SECRET')

# LINE Bot API の設定
configuration = Configuration(access_token=channel_access_token)
handler = WebhookHandler(channel_secret)


@app.route("/callback", methods=['POST'])
def callback():
    # リクエストヘッダーから署名検証のための値を取得
    signature = request.headers['X-Line-Signature']

    # リクエストボディを取得
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    # 署名を検証し、問題があれば例外を発生させる
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        print("Invalid signature. Please check your channel access token/channel secret.")
        abort(400)

    return 'OK'


@handler.add(MessageEvent, message=TextMessageContent)
def handle_message(event):
    # 受信したメッセージをそのまま返す(オウム返し)
    reply_text = event.message.text

    with ApiClient(configuration) as api_client:
        line_bot_api = MessagingApi(api_client)
        line_bot_api.reply_message_with_http_info(
            ReplyMessageRequest(
                reply_token=event.reply_token,
                messages=[TextMessage(text=reply_text)]
            )
        )


if __name__ == "__main__":
    port = int(os.environ.get("PORT", 8000))
    app.run(host="0.0.0.0", port=port, debug=True)

下から 2 行目の以下のコードはポートの設定です。ポートとは「家の玄関に新しい扉を作って、特定の人だけが入れるようにする」仕組みです。つまり 8000 番の玄関にお客さんが来たらこのプログラムが実行されます。この数値は次の ngrok で使う数値と合わせる必要があります。

port = int(os.environ.get("PORT", 8000))

実装したらターミナルで app.py を実行します。

python app.py

以下のような表示になります。

(.venv) h_ikuma:~/PycharmProjects/LINE BOT % python app.py
 * Serving Flask app 'app'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:8000
 * Running on http://192.168.31.209:8000
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 142-356-595

4-4. Webhook の設定

ボットを実際に稼働させるためには、LINE Developers で Webhook URL を設定します。Webhook とは「LINE から自動で連絡をもらうための、あなたのサーバーの連絡先」です。要するに「何かあったら、この住所(URL)に教えて」と登録するのが Webhook URL の役割です。

ngrok の起動

  • app.py実行しているターミナルとは別のターミナルを開いて ngrok http 8000 を実行します。app.py に記載したポート番号と合わせます。
  • 表示される https:// から始まる公開 URL(例: https://xxxx.ngrok-free.app)をコピーします。
  • 無料アカウントではこの URL は ngrok を起動するたびに変わりますので、その都度 LINE 側の Webhook URL 設定を修正する必要があります。

Messaging API の画面で Webhook URL を表示し、先ほどコピーした URL を貼り付け、末尾に /callback を付けて保存します。

callback とは「何かが起きたら、あらかじめ決めておいた処理を自動で実行する」仕組みです。LINE Bot の場合なら「メッセージが届いたら、私のサーバーに教えて(callback)」という設定をしているわけです。

ngrok でインターネット上に公開しているので、メッセージが届くと Messaging API から app.py に連絡が届き、オウム返しで返信します。

4-5. Bot のテスト

これで、あなたのローカルで実行されている LINE Bot は稼働しているはずです。実際に作成した公式アカウントに何かメッセージを入力してみてください。同じメッセージをオウム返しするはずです。

オウム返ししなかったら app.py を実行しているターミナルを確認してください。エラーが表示されていたらターミナルをクリックして Ctrl+C でアプリを停止させ、エラー箇所を修正したら再度 app.py を実行してください。

5. 株価を返すように修正

次はティッカーを入力したら最新の終値を返すようにしましょう。以下のような流れになります。

flowchart TD
    A["ユーザーがティッカーシンボル送信<br/>例: 'aapl'"] --> B["LINEがcallback関数を呼び出し"]
    B --> C{"署名検証OK?"}
    C -->|NG| D["エラー返却"]
    C -->|OK| E["handle_message関数実行"]
    E --> F["メッセージを正規化<br/>strip().upper()"]
    F --> G["get_stock_price関数呼び出し<br/>yfinanceで株価取得"]
    G --> H{"株価取得成功?"}
    H -->|成功| I["株価メッセージ作成<br/>'AAPL: $150.25'"]
    H -->|失敗| J["エラーメッセージ作成<br/>'ティッカーが見つかりません'"]
    I --> K["LINE Messaging APIで返信"]
    J --> K
    K --> L["処理完了"]

app.py と同じ階層に ticker.py を作成し、以下のコードを実装します。

import yfinance as yf


def get_stock_price(ticker_symbol):
    """
    指定されたティッカーシンボルの最新終値を取得する

    Args:
        ticker_symbol (str): ティッカーシンボル(例: 'AAPL', '^N225')

    Returns:
        tuple: (success: bool, result: float or str)
               成功時: (True, 株価)
               失敗時: (False, エラーメッセージ)
    """
    try:
        ticker = yf.Ticker(ticker_symbol)
        hist = ticker.history(period="1d")

        if hist.empty:
            return False, f"ティッカー '{ticker_symbol}' のデータが見つかりませんでした。"

        price = hist['Close'].iloc[-1]
        return True, price

    except Exception as e:
        return False, f"エラーが発生しました: {str(e)}"


if __name__ == "__main__":
    # 日経225と、存在しないティッカーを取得
    success, result = get_stock_price("^N225")
    if success:
        print(f'日経225: {result:.2f}')
    else:
        print(result)

    success, result = get_stock_price("hoge")
    if success:
        print(f'hoge: {result:.2f}')
    else:
        print(result)

次は app.py を以下のように get_stock_price をインポートし、handle_message を修正します。

from ticker import get_stock_price


@handler.add(MessageEvent, message=TextMessageContent)
def handle_message(event):
    # 受信したメッセージをそのまま返す(オウム返し)
    # reply_text = event.message.text

    # ユーザーからのメッセージを取得
    user_message = event.message.text.strip().upper()  # 大文字に変換してスペースを削除
    # ティッカーシンボルとして株価を取得
    success, result = get_stock_price(user_message)
    if success:
        # 株価を取得できた場合
        reply_text = f"{user_message}: ${result:.2f}"
    else:
        # エラーが発生した場合
        reply_text = result

    with ApiClient(configuration) as api_client:
        line_bot_api = MessagingApi(api_client)
        line_bot_api.reply_message_with_http_info(
            ReplyMessageRequest(
                reply_token=event.reply_token,
                messages=[TextMessage(text=reply_text)]
            )
        )

修正したら LINE アプリで試してください。例えば Apple 社の株価を取得するには aapl と入力します。

その他にもいろいろなティッカーで遊んでみてください。

ティッカー企業名
AAPLApple Inc.
MSFTMicrosoft Corporation
NVDANVIDIA Corporation
AMZNAmazon.com Inc.
GOOGLAlphabet Inc.
TSLATesla Inc.
METAMeta Platforms Inc.
NFLXNetflix Inc.
AMDAdvanced Micro Devices Inc.
LLYEli Lilly and Company

うまくいきましたか? 期待したとおり株価が返ってきたら嬉しかったのではないでしょうか? うまくいかなかったら次のトラブルシューティングを確認してください。

6. トラブルシューティング

6-1. まずはここを確認(クイックチェックリスト)

  • [ ] Flask アプリ(app.py)が起動している(コンソールに Running on http://127.0.0.1:8000 が表示)
  • [ ] ngrok http 8000 を別ターミナルで実行中(https の公開 URL が表示)
  • [ ] LINE Developers の Webhook URL が https://<ngrokのURL>/callback になっている
  • [ ] LINE Official Account Manager の「Webhook」が有効
  • [ ] .envCHANNEL_ACCESS_TOKENCHANNEL_SECRET が正しい(余計な空白・改行なし)
  • [ ] 端末がスリープしていない

6-2. 症状別の対処

A. 返信が一切返ってこない

  • Webhook の疎通確認

    • LINE Developers → Messaging API → Webhook 設定 → 「接続確認」を押す
    • 成功しない場合は URL の末尾に /callback が付いているか、ngrok が起動中か確認
  • ログの確認

    • app.py 側のコンソールにリクエストログ(Request body)が出ているか
    • 何も来ない場合、ngrok の URL が変わっていないか再確認
  • ヘルスチェック

    • 公開 URL 直下 / は 200 を返すはず
    curl -i https://<ngrokのURL>/

B. 400/403(Invalid signature)になる

  • CHANNEL_SECRET が間違っている可能性
    • WebhookHandler(channel_secret) に設定している値が最新か確認
  • Webhook を自分で curl POST しても X-Line-Signature がないため 400 が正常
    • Webhook の実テストは必ず LINE 側の「接続確認」か、実メッセージで行う

C. 401 Unauthorized になる

  • CHANNEL_ACCESS_TOKEN が無効 or 期限切れ
    • LINE Developers でトークンを再発行し、.env を更新 → アプリ再起動

D. 404 や 405 Method Not Allowed

  • Webhook パスのミス
    • @app.route("/callback", methods=['POST']) と LINE 側 URL /callback の一致を確認
  • メソッド不一致
    • Webhook は POST のみ。GET では 405 になる

E. 500 Internal Server Error(Python 例外)

  • 例外メッセージを確認して修正
    • 例えば event.message.text を前提にして画像やスタンプが来ると例外に
    • 本ガイドのコードは TextMessageContent のみをハンドルしていることを再確認

F. たまに動くが、しばらくすると動かない

  • ngrok の URL が起動の度に変わる(無料プラン)
    • 変わったら Webhook URL を更新
  • PC/Mac のスリープ・ネットワーク切断
    • スリープ設定を見直す or こまめに再接続

G. yfinance で株価が取れない/遅い

  • ティッカーの打ち間違い、マーケット休場、ネットワーク不安定