GitHub Actions から日本株の終値を取るなら Yahoo Finance チャート API 直叩きが正解(stooq 終了をうけて)

プログラミング関連

以前このブログで「GitHub Actions から日本株の終値を取りたいなら stooq.com(apikey 経由)一択だった話」という記事を書きました。あれから少し経って、その結論が丸ごと使えなくなったので、続報として書き直します。結論から言うと、いまは Yahoo Finance のチャート API を直叩きするのが現実解です。

TL;DR

  • stooq.com は 2026 年に全エンドポイントへ JavaScript の Bot チャレンジを導入し、CSV が返らなくなりました。apikey の発行ページごと閉じたので、再取得もできません。旧記事の結論は失効です。
  • 代替を順に試した結果、Twelve Data 無料プランは東証(JPX)が有料限定Google スプレッドシートの GOOGLEFINANCE は東証銘柄が #N/A で、どちらも脱落しました。
  • 最終的に Yahoo Finance のチャート API(query1/query2.finance.yahoo.com/v8/finance/chart/<銘柄コード>.Tを、標準ライブラリだけで直叩きする形に落ち着きました。API キー不要で、GitHub Actions のクラウド IP からも 429 なしで取れています。
  • yfinance はクラウドだと 429 で死ぬ」というのは事実ですが、それはライブラリの重い挙動が原因で、単発の chart API を 1 回 GET するのとは別物です。ここを誤解して Yahoo 系を丸ごと避けていました。

背景:以前は stooq の apikey を勧めていた

作っているのは、自分が保有している日本株について「基準日(2026-02-08)の終値」と「本日の終値」の差分を、平日 17:00 JST に自分宛へメールする日次バッチです。実行基盤は GitHub Actions(schedule + 手動トリガ)、言語は Python、メール送信は Gmail の SMTP。要件で大事なのは次の点でした。

  • 比較基準日は固定(前営業日ではない)。基準価格は JSON で保持。
  • 土日は送らない。
  • 株価取得に失敗してもメールは送る。本文に銘柄ごとのエラー原因を書く。

株価データ取得は、前回の記事では「ローカルは yfinance、クラウド本番は stooq の apikey 経由 CSV に差し替える」のがいちばんラク、と結論づけていました。実際、しばらくはそれで動いていたんです。

異変:stooq が CSV を返さなくなった

ある週から、届くメールの全銘柄が「取得失敗」になりました。本文に残しておいたエラー原因を見ると、CSV ではなく JavaScript の Bot チャレンジページがそのまま返ってきていました。

<!DOCTYPE html><html><head><meta charset="utf-8">
<meta name="robots" content="noindex,nofollow"></head><body>
<noscript>This site requires JavaScript to verify your browser.
Please enable JavaScript and reload.</noscript>
<script nonce="...">(async()=>{ ... proof-of-work ... })()</script>

切り分けてみると、apikey を付けても・付けなくても同じチャレンジ HTMLが返り、さらに apikey 取得ページ(?get_apikey)自体もチャレンジの向こう側に入っていました。ブラウザ(JS が動く環境)で開いても、以前のように apikey 文字列が表示されません。

つまり「apikey が失効した」どころではなく、新しい apikey を取り直すこと自体ができない状態です。GitHub Actions の curl / urllib は JS を実行できないので、この proof-of-work チャレンジは越えられません。stooq は自動バッチ用途からは完全に脱落、と判断しました。

試行 1:Twelve Data 無料 — 東証(JPX)は有料限定だった

次に、API キーで認証するタイプなら IP では弾かれないはず、と考えて Twelve Data を試しました。銘柄検索では確かに「2593 = Ito En, Ltd. / 取引所 JPX / JPY」とヒットします。期待して無料キーを取り、実際に quote エンドポイントを叩いたところ——

{"code":404,
 "message":"This symbol is available starting with the Pro or Venture plan. Consider upgrading now at https://twelvedata.com/pricing",
 "status":"error"}

無料プランは東証銘柄を返してくれません。JPX は Pro / Venture プラン限定でした。検索でヒットするのにデータは有料、というのはありがちな罠です。push する前にローカルで実キーを叩いて確認していたので、無駄な本番デプロイは避けられました。

試行 2:Google スプレッドシート(GOOGLEFINANCE) — 東証は #N/A

「Google のサーバ側で取得させて、ウェブに公開した CSV を GitHub Actions から読む」という proxy 作戦も考えました。シートに次のように書きます。

A1: 2593   B1: =GOOGLEFINANCE("TYO:"&A1,"price")
A2: 6367   B2: =GOOGLEFINANCE("TYO:"&A2,"price")

ところが B 列は #N/A。数式ミスなら #ERROR!#NAME? になりますが、#N/A は「そのシンボルのデータを持っていない」の意味です。GOOGLEFINANCE は近年、海外取引所のカバレッジを縮小していて、東証銘柄が取れないのは既知の挙動でした。これも脱落です。

試行 3:Yahoo Finance チャート API 直叩き — これが現実解

ここまでで「無料で、東証の当日値を、クラウドのサーバから取れる」手段は実質ほぼ尽きました。残ったのが Yahoo Finance のチャート APIです。

https://query1.finance.yahoo.com/v8/finance/chart/2593.T?range=5d&interval=1d

返ってくる JSON のここに当日値(場が引けた後は終値)が入っています。

{"chart":{"result":[{"meta":{
  "currency":"JPY","symbol":"2593.T",
  "regularMarketPrice":2710,
  "chartPreviousClose":2684.5,
  ...
}}],"error":null}}

「でも Yahoo 系は yfinance がクラウドで 429 連発するから避けたんじゃ?」——自分もそう思って遠回りしました。けれど、あの 429 は yfinance ライブラリがクッキー/クラム取得などで何度も叩く挙動が主因で、軽量な chart エンドポイントを 1 回 GET するのとは別物です。実際、GitHub Actions(workflow_dispatch)で回しても 429 は出ず、伊藤園 2593.T も ダイキン工業 6367.T も普通に取得・送信できました。

実装:Python標準ライブラリだけで叩く

追加パッケージは不要です。urllibjson だけで完結します。query1 が落ちても query2 に切り替えるホストフォールバックを入れておくと安定します。

import json
from urllib.request import Request, urlopen

HOSTS = ("query1.finance.yahoo.com", "query2.finance.yahoo.com")
UA = ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
      "(KHTML, like Gecko) Chrome/124.0 Safari/537.36")

def get_price(symbol: str) -> float:
    last_err = None
    for host in HOSTS:                       # query1 がダメなら query2
        url = f"https://{host}/v8/finance/chart/{symbol}?range=5d&interval=1d"
        try:
            req = Request(url, headers={"User-Agent": UA})
            with urlopen(req, timeout=15) as resp:
                data = json.loads(resp.read().decode("utf-8"))
        except Exception as e:               # 429 等はここで握って次のホストへ
            last_err = e
            continue
        result = (data.get("chart") or {}).get("result") or []
        if result:
            return float(result[0]["meta"]["regularMarketPrice"])
    raise RuntimeError(f"取得失敗: {last_err}")

print(get_price("2593.T"))   # 伊藤園 -> 2710.0

銘柄リストは差し替え前提なので、コードに直書きせず JSON に外出ししています。Yahoo のティッカーは東証なら <銘柄コード>.T です。

[
  { "name": "伊藤園",       "code": "2593", "yahooSymbol": "2593.T", "basePrice": 3054 },
  { "name": "ダイキン工業", "code": "6367", "yahooSymbol": "6367.T", "basePrice": 18010 }
]

そして何より気持ちがいいのが、GitHub Actions 側から株価用のシークレットが消えたことです。Yahoo は無キーなので、渡すのはメール関連だけになりました。

      - name: Run kabumail
        env:
          SMTP_USER: ${{ secrets.SMTP_USER }}
          SMTP_PASS: ${{ secrets.SMTP_PASS }}
          MAIL_TO:   ${{ secrets.MAIL_TO }}
          # STOOQ_APIKEY / TWELVEDATA_APIKEY はもう不要
        run: python src/kabumail.py

比較表

取得元認証東証カバークラウドIP(GHA)で動く料金現状
yfinance(ライブラリ)不要△(429 多発)無料ローカル試作向き
stooq 素叩き CSV不要✕(Bot チャレンジ)無料不可
stooq apikey 経由apikey✕(発行停止・チャレンジ)無料不可(再取得もできない)
Twelve Data 無料API キー✕(Pro/Venture 限定)無料東証は取れない
GOOGLEFINANCE不要✕(#N/A)無料東証は取れない
Yahoo チャート API 直叩き不要○(実測 OK)無料◎ 現状の現実解

Yahoo チャート API を使うときの注意点

1. 非公式エンドポイントである

/v8/finance/chart/ は Yahoo Finance のフロントが使う非公開 API です。利用は自己責任で、節度を持って(1 日数回・少数銘柄程度に)。仕様やレート制限が予告なく変わる可能性は常にあります。

2. シンボルは <銘柄コード>.T、価格は meta.regularMarketPrice

東証は 2593.T のように .T を付けます。場が引けた後(自分のバッチは 17:00 JST 実行)なら regularMarketPrice がその日の終値になります。保険として indicators.quote[0].close の末尾値を見るようにしておくと、稀に meta 側が欠けても拾えます。

3. query1 がダメでも query2 がある

同じパスで query1query2 の 2 ホストが使えます。片方が一時的に不調でももう片方で取れることがあるので、ホストフォールバックを入れておくと安定します。

4. クラウド IP の 429 に備える

いまは GitHub Actions の IP でも通っていますが、データセンター IP が将来弾かれる可能性はゼロではありません。軽いリトライを入れつつ、ダメになったときの退避先(Google Apps Script で Yahoo を取得して CSV 公開する proxy 方式、または Twelve Data Pro 等の有料 API)も頭の片隅に置いておくと安心です。

5. 取得に失敗してもメールは止めない

これは取得元に関係なく大事な設計です。価格が取れなかった銘柄は「取得失敗」とその原因(HTTP エラーや JSON 構造の異常など)を本文に書いて、メール送信自体は必ず実行します。こうしておくと、今回のように取得元が静かに壊れたときも「届いたメールのエラー文」が一次情報になり、切り分けが一気に速くなります。実際、今回も本文に残していたチャレンジ HTML が原因特定の決め手でした。

結論:用途別の使い分け

  • ローカルでの試作yfinance が手軽。書き味がよく、すぐ動きます。
  • クラウド(GitHub Actions 等)での本番Yahoo チャート API を標準ライブラリで直叩き。無キーで、IP も今のところ問題なし。これが現状いちばんラクです。
  • 業務でカッチリ・SLA が要る:素直に有料 API(Twelve Data Pro など)。非公式エンドポイント依存のリスクをお金で消せます。

前回は「stooq apikey 一択」と書きましたが、無料で登録不要な株価ソースはある日いきなり閉じるものだと、今回あらためて思い知りました。だからこそ「取得失敗してもバッチは止めない・原因をメールに残す」という土台が効いてきます。同じように GitHub Actions から日本株を取りたい人の遠回りを少しでも減らせれば幸いです。

コメント

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