三流君 ken3のmemo置き場

三流プログラマーのメモ書きです。主にVBAやWindowsの話題が多いです

挨拶・自己紹介:
失敗続きのAB型の変わり者 :三流プログラマー Ken3です
フリーのエンジニア・個人事業主です・・と書くと聞こえはイイが(それとなくカッコよく聞こえるが)、 現在は小さな案件の受注請負 と 短期派遣 で 日々つつましく?ほそぼそと暮らしてます。
Ken3三流君の連絡先:
[google formsで連絡する]
上記の問い合わせフォームに質問・感想など気軽に書き込んでください

よく検索されるキーワード: [質問回答XXXXさんへ] [CreateObject] [VBA] [JRA競馬オッズ]

JRAオッズ Power Query(M言語)でJavaScript動的ページのデータを取得する方法【3連単人気順】

Excel Power Query】JRA 3連単オッズ「人気順」が取れない!隠しパラメータを暴いてPOST送信で取得する方法

こんにちは、三流プログラマーのKen3です。

今回はYouTubeの視聴者さんからいただいた、JRA日本中央競馬会)の3連単オッズを『人気順』でPower Queryを使って取得したいが、うまくいかない」という質問への回答を備忘録としてまとめます。

通常、WebスクレイピングはURLを指定してデータを取得しますが、JRAのサイトのようにJavaScriptで動的にページ遷移するサイトでは一筋縄ではいきません。今回はその解決策として、裏側で動いている隠しパラメータを探し出し、M言語でPOST送信する方法を解説します。

AIに大まかな流れの図解を頼んでみた

いただいた質問:URLが変わらない問題

視聴者さんからの質問は以下の通りです。

3連単の「人気順」を取得しようとすると、URLが変わらず、クエリにどう記述していいかわかりません。 strPARA = Text.ToBinary(...) の部分でパラメータを指定してもうまくいきません。

JRAのオッズページは、表示ボタンを押してもURL(accessO.html)が変わらず、裏でJavaScriptsetParameter関数)が動いてデータを書き換えています。

解決へのアプローチ:ソースコードの裏側を暴く

ブラウザの「検証」機能や「ページのソースを表示」を使って、ボタンを押したときに「どんなデータがサーバーに送られているか」を調査しました。


www.youtube.com

■動画の概要 先週の配信で音声トラブルがあった「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
    追加されたオッズ時刻

ポイントと注意点

  1. パラメータの区切り文字: 各パラメータは & (アンパサンド) で結合する必要があります。M言語上では & "&" & のように記述して文字列をつなぎます。

  2. xRange(順位範囲)の値: HTMLソースの <option value="..."> を確認すると、以下のようになっています。

    • 1~500位: 01/3F
    • 501~1000位: 02/60
    • 1001~1500位: 03/81
    • 2001~2500位: 05/C3

    取得したい順位に合わせて、コード内の strxRange の値を書き換えてください。

まとめ

JRAのサイトのようにURLが変わらないページでも、「ページのソースを表示」で裏側のフォームやJavaScriptcalldoActionなど)を解析し、正しいパラメータをPOST送信することでPower Queryでもデータが取得できます。

今回は固定値でのテストでしたが、この仕組みを応用すれば、レースIDなどを動的に変更して自動取得することも可能です。スクレイピングの壁にぶつかっている方の参考になれば幸いです。


質問・感想・クレームなど、
気軽にコメント欄に書いてもらえるとうれしいです。

[Googleフォームにコメントを残す]
↑質問・コメントの入力フォームです、気軽に書いてください


フッター:最後にKen3Videoの動画一覧を紹介します

YouTubeにアップした動画です。他の動画を一瞬でも見てもらえるとさらに嬉しいです。
再生リスト:[三流君Ken3の最新動画]←リストの一覧形式で表示する


また、ブログを見に来てくださいね。ではまたぁ~