【Excel Power Query】JRA 3連単オッズ「人気順」が取れない!隠しパラメータを暴いてPOST送信で取得する方法
こんにちは、三流プログラマーのKen3です。
今回はYouTubeの視聴者さんからいただいた、「JRA(日本中央競馬会)の3連単オッズを『人気順』でPower Queryを使って取得したいが、うまくいかない」という質問への回答を備忘録としてまとめます。
通常、WebスクレイピングはURLを指定してデータを取得しますが、JRAのサイトのようにJavaScriptで動的にページ遷移するサイトでは一筋縄ではいきません。今回はその解決策として、裏側で動いている隠しパラメータを探し出し、M言語でPOST送信する方法を解説します。

いただいた質問:URLが変わらない問題
視聴者さんからの質問は以下の通りです。
3連単の「人気順」を取得しようとすると、URLが変わらず、クエリにどう記述していいかわかりません。
strPARA = Text.ToBinary(...)の部分でパラメータを指定してもうまくいきません。
JRAのオッズページは、表示ボタンを押してもURL(accessO.html)が変わらず、裏でJavaScript(setParameter関数)が動いてデータを書き換えています。
解決へのアプローチ:ソースコードの裏側を暴く
ブラウザの「検証」機能や「ページのソースを表示」を使って、ボタンを押したときに「どんなデータがサーバーに送られているか」を調査しました。
■動画の概要 先週の配信で音声トラブルがあった「JRA3連単人気順オッズ」の取得に再チャレンジします。URLが変わらない動的ページのため、HTMLソースとJavaScript(setParameter関数など)を解析し、隠しパラメータを特定してPower Query(M言語)でデータを取得する手順を実演解説します。
[00:00] オープニング・再挑戦の経緯 先週の「音声なし事故」のリベンジです。JRA公式サイトの複雑なパラメータ(人気順)をどのようにクエリに渡せばよいか、改めて調査・解説します。
[00:36] 開催地パラメータの確認 まずは基本となる「開催地(例:阪神4日)」のパラメータ取得について。既存のソースコードを流用しながら簡単に手順を振り返ります。
[01:38] 取得の戦略と現状の壁 「馬番順」から「人気順」へ画面遷移してもURLが変わらない問題点を解説。直接リンクがないため、一旦馬番順を経由してソースコードを探る必要があります。
[05:06] ソース解析:基本パラメータの特定 ブラウザの「ページのソースを表示」から検索機能を使い、人気順リンクのベースとなるパラメータ(pw.../AE)を特定します。
[07:36] Excelパワークエリでのテスト準備 新規Excelブックを開き、「空のクエリ」を作成。特定したパラメータを固定値として使い、まずはHTMLソースが取れるかテストする準備を行います。
[11:57] 詳細解析:HTMLフォームとJavaScript 「表示」ボタンを押した際に実行される処理を追跡。順位範囲(iv_ninki)やソート順のvalue値をHTMLソースから読み解きます。
[16:35] 重要:setParameter関数の解読 JavaScriptのsetParameter関数を解析。ラジオボタンの値や入力フォームの値が変数(para1~para4)にどう格納されるかを確認します。
[20:26] 隠し項目「xW15」の正体 パラメータの4番目に渡されているxW15を検索し、それがHTML上の隠し項目(hidden input)であることを突き止めます。
[22:30] POST送信の仕組み(calldoAction) 最終的に実行されるcalldoAction関数を確認。4つのパラメータ(cname, xMode, xRange, xSort)が必要であることを特定します。
[25:22] M言語でのクエリ作成 Power Queryのエディターで、解析した各パラメータを &(アンパサンド)で結合し、POST送信用のバイナリデータを作成する方法を解説します。
[27:57] データ取得テスト(1001~1500位) 作成したクエリを実行し、実際に3連単オッズ(1001位~)がExcelに取り込めるか確認します。
[31:21] 応用テスト(2001~2500位) パラメータの一部(xRange)を書き換え、別の順位範囲(2001位~)も正しく取得できるか検証します。
[35:02] 今後の課題とまとめ オッズ範囲指定(100倍~など)や、レース番号を可変にする方法など、実運用に向けた積み残し課題と今回のまとめ。
1. JavaScriptの解析
「表示」ボタンの onclick イベントを追うと、setParameter() という関数が実行されていることがわかります。
この関数の中で、以下の要素から値を取得していました。
iv_mode(ラジオボタン): 人気順かオッズ順かを判定iv_ninki(セレクトボックス): 「1~500位」などの範囲指定iv_sort(セレクトボックス): 昇順・降順xW15(隠しフィールド): レースを特定するID
2. 送信パラメータの特定
調査の結果、サーバー(accessO.html)に対して、以下のパラメータをPOST送信(データを裏で送る)する必要があることがわかりました。
| パラメータ名 | 役割 | 設定例 (1~500位の場合) |
|---|---|---|
cname |
レースID (隠し項目 xW15 の値) |
pw158op.../AE (レース毎に異なる) |
xMode |
モード (人気順) | 1/0E |
xRange |
順位範囲 (1001~1500位など) | 03/81 (※範囲によって変わる) |
xSort |
ソート順 (昇順) | 1/0E |
解決策:M言語での記述コード
Power Queryの「詳細エディター」に貼り付けて使えるコードを作成しました。
Web.Contents 関数の Content オプションを使うことで、GETリクエストではなくPOSTリクエストとしてデータを送信できます。
以下は、阪神ジュベナイルフィリーズ(11R)の3連単人気順(1001位~1500位) を取得するサンプルコードです。
let
// 1. JRAホームページのアクセス先URL
strURL = "https://www.jra.go.jp/JRADB/accessO.html",
// 2. 送信するパラメータの設定(ここを書き換えて調整します)
// cname: レースごとの固有ID(ソースの隠し項目 xW15 から取得)
strCNAME = "cname=pw158opS309202505041120251214Z/AE",
// xMode: 1/0E = 人気順
strxMode = "xMode=1/0E",
// xRange: 03/81 = 1001位~1500位 (ここを変更して範囲を変える)
strxRange = "xRange=03/81",
// xSort: 1/0E = 昇順
strxSort = "xSort=1/0E",
// 3. パラメータを結合してバイナリ化 (POST送信の肝)
// 文字列を結合: "cname=...&xMode=...&xRange=...&xSort=..."
strPARA = Text.ToBinary(strCNAME & "&" & strxMode & "&" & strxRange & "&" & strxSort),
// 4. ヘッダー指定
strHEAD = [#"Content-Type"="application/x-www-form-urlencoded"],
// 5. データの取得 (Web.ContentsでPOST送信)
ソース = Web.Contents(strURL, [Headers = strHEAD, Content = strPARA]),
// 6. 文字コード変換 (Shift-JIS = 932)
文字列 = Text.FromBinary(ソース, 932),
// 7. 必要な情報を抽出 (レース名や時刻など)
strレース名 = Text.BetweenDelimiters(文字列, "<span class=""race_name"">", "<"),
str時刻 = Text.BetweenDelimiters(文字列, "<div class=""cell time""><strong>", "<"),
// 8. HTMLテーブルの展開
HTMLtest = Web.Page(文字列),
#"展開された Data" = Table.ExpandTableColumn(HTMLtest, "Data", {"人気", "組番", "オッズ"}, {"Data.人気", "Data.組番", "Data.オッズ"}),
// 9. 列の追加
追加されたレース名 = Table.AddColumn(#"展開された Data", "レース名", each strレース名),
追加されたオッズ時刻 = Table.AddColumn(追加されたレース名, "オッズ時刻", each str時刻)
in
追加されたオッズ時刻
ポイントと注意点
パラメータの区切り文字: 各パラメータは
&(アンパサンド) で結合する必要があります。M言語上では& "&" &のように記述して文字列をつなぎます。xRange(順位範囲)の値: HTMLソースの<option value="...">を確認すると、以下のようになっています。- 1~500位:
01/3F - 501~1000位:
02/60 - 1001~1500位:
03/81 - 2001~2500位:
05/C3
取得したい順位に合わせて、コード内の
strxRangeの値を書き換えてください。- 1~500位:
まとめ
JRAのサイトのようにURLが変わらないページでも、「ページのソースを表示」で裏側のフォームやJavaScript(calldoActionなど)を解析し、正しいパラメータをPOST送信することでPower Queryでもデータが取得できます。
今回は固定値でのテストでしたが、この仕組みを応用すれば、レースIDなどを動的に変更して自動取得することも可能です。スクレイピングの壁にぶつかっている方の参考になれば幸いです。