三流君のソースコード置き場

ブログにソースコードをアップして、ブログの検索機能で利用してます(利用予定です)

よく検索されるキーワード: [VBA]/ [VBS]/ [CreateObject]/ [Excel]/ [ADO]


JRAのCNAMEパラメータを縦に取得!Power Queryでリスト展開とループ処理【ソースコード公開】

JRAのCNAMEパラメータを縦に取得!Power Queryでリスト展開とループ処理【ソースコード公開】

三流プログラマーのKen3です。

今回は、JRAのオッズ取得に向けた「CNAMEパラメータ」の抽出に再チャレンジします。
前回までは開催ごとの取得でしたが、今回はさらに一歩踏み込んで、**「開催場所」×「馬券の種類(単勝馬連・3連複など)」×「レース番号」**の組み合わせで、縦一列にCNAMEをズラッと書き出す処理を作りました。

AIにソースコードと動画を渡しブログを書いてもらいました

「手作業でやると面倒な繰り返し処理」こそ、Power Queryの真骨頂です。

今回のライブ動画(2倍速推奨)

www.youtube.com

先に完成形ソースコード

解説の前に、まずは動くコードを載せておきます。
今回は2つのクエリ(関数側とメイン処理側)を使います。

1. 関数側クエリ:開催と種類別にCNAME取得

空のクエリを作成し、名前を必ず「''開催と種類別にCNAME取得''」に変更してから、詳細エディターに貼り付けてください。

// 関数化して開催地別、馬券の種類別のCNAMEを返す 2026/01/31テスト
// 関数で引数を受け取り、テーブルを返す

(str開催地CNAME as text, str開催地と開催日 as text, str馬券種類 as text) as table =>
let
    // JRAホームページからソースを取得する POST送信で結果を受け取る
    strURL = "https://www.jra.go.jp/JRADB/accessO.html",
    // 引数で受け取った開催地のCNAMEをパラメーターとする 26/01/25修正
    strCNAME = "cname=" & str開催地CNAME,
    strPARA = Text.ToBinary(strCNAME),
    strHEAD = [#"Content-Type"="application/x-www-form-urlencoded"],
    ソース = Web.Contents(strURL, [Headers = strHEAD, Content = strPARA]),
    文字列 = Text.FromBinary(ソース, 932),
    HTMLソース = Lines.FromText(文字列),

    // 現在時刻を取得し、文字列に変換
    現在時刻 = DateTime.ToText(DateTime.LocalNow()),
    LISTの頭に時刻を追加 = {"取得時刻"} & {現在時刻} & HTMLソース,

    // LISTをテーブルに変換して、フィルターをかける
    テーブルに変換済み = Table.FromList(LISTの頭に時刻を追加, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    // 馬券の種類でフィルターをかける
    フィルターされた行 = Table.SelectRows(テーブルに変換済み, each Text.Contains([Column1], "Action") and Text.Contains([Column1], str馬券種類)),

    // 5.2 doActionのパラメーターを抜き出し CNAMEを取得
    条件3_1 = "doAction('/JRADB/accessO.html', '",
    条件3_2 = "'", 
    CNAMEを追加 = Table.AddColumn(フィルターされた行, "CNAME", each Text.BetweenDelimiters([Column1], 条件3_1, 条件3_2)),

    //5.1 レース番号の作成 ダミーの連番インデックス列を追加します (0から始まる連番が振られます)
    インデックス列の追加 = Table.AddIndexColumn(CNAMEを追加, "Index", 0, 1),

    //インデックス列が0からなので、+1してColumn2を作成します
    レース番号を追加 = Table.AddColumn(インデックス列の追加, "レース番号", each Number.ToText([Index] + 1) & "レース"),

    //場所を追加する
    追加された場所 = Table.AddColumn(レース番号を追加, "今週の開催地", each str開催地と開催日),

    //馬券の種類を追加する
    追加された種類 = Table.AddColumn(追加された場所, "馬券の種類", each str馬券種類),

    //不要な列を削除する
    列削除 = Table.RemoveColumns(追加された種類,{"Column1", "Index"}),

    //最終的な結果
    結果 = 列削除

in
    結果
2. メイン処理クエリ

こちらが実行用です。名前は何でも構いません。

//--- JRAオッズのTOPページを取得する パワークエリ 26/01/31テスト
// メイン処理:開催日を取得し、リスト展開してループ処理へ渡す

let
    // JRAホームページからソースを取得する POST送信で結果を受け取る
    strURL = "https://www.jra.go.jp/JRADB/accessO.html",
    strPARA = Text.ToBinary("cname=pw15oli00/6D"),
    strHEAD = [#"Content-Type"="application/x-www-form-urlencoded"],
    ソース = Web.Contents(strURL, [Headers = strHEAD, Content = strPARA]),
    文字列 = Text.FromBinary(ソース, 932),
    HTMLソース = Lines.FromText(文字列),
    
    テーブルに変換済み = Table.FromList(HTMLソース, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    フィルターされた行 = Table.SelectRows(テーブルに変換済み, each Text.Contains([Column1], "Action") and Text.Contains([Column1], "回")),

    条件2_1 = "accessO.html', '",
    条件2_2 = "');"">",
    Column2を追加 = Table.AddColumn(フィルターされた行, "CNAME", each Text.BetweenDelimiters([Column1], 条件2_1, 条件2_2)),

    // Column3を作成 '</i>' と '</a></div>' の間のテキスト(開催場所)を抽出
    条件3_1 = "</i>",
    条件3_2 = "</a></div>",
    Column3を追加 = Table.AddColumn(Column2を追加, "開催", each Text.BetweenDelimiters([Column1], 条件3_1, 条件3_2)),

    // 最終的な結果の整形
    結果 = Column3を追加,
    削除された列 = Table.RemoveColumns(結果,{"Column1"}),
    
    // ★ここがポイント:馬券の種類をリストで追加
    追加されたカスタム = Table.AddColumn(削除された列, "馬券の種類", each { "単勝","馬連","3連複" }),
    
    // ★リストを展開して行を増やす
    T開催取得 = Table.ExpandListColumn(追加されたカスタム, "馬券の種類"),
    
    // ソースのクエリー結果(テーブル)をレコードのリストに変換
    lst処理リスト = Table.ToRecords(T開催取得),

    // ループ処理:List.Transformで順番に関数を呼び出す
    テーブル一覧 = List.Transform(
        lst処理リスト,
        each 開催と種類別にCNAME取得(_[CNAME], _[開催], _[馬券の種類])
    ),

    // 結合:テーブルのリストを一つにつなげる
    t_all = Table.Combine(テーブル一覧)
in
    t_all

今回のポイント解説

1. リストを追加して「行展開」が便利すぎる

Power Queryで「ある列の値に基づいて行を複製したい(例:S, M, Lサイズなど)」という場合、リスト形式で列を追加して展開する方法が非常に便利です。

今回はメイン処理で以下のように書いています。

each { "単勝","馬連","3連複" }

こうすることで、1つの開催場所に対して3行(単勝用、馬連用、3連複用)のデータが自動生成されます。

「先輩たちがよく言っている『リスト追加して行展開すれば楽じゃん』というのはこのことか!」と実感しました。
このあたりの実際の操作は、動画の以下の部分で実演しています。

[11:09 リストを追加して行展開の実演を見る](https://www.youtube.com/watch?v=tC_84sHW3dg&t=669s)

2. 「3連複」の罠

コードを書いている最中にハマったのが「3連複」の文字です。
JRAのサイト上では「3連複」(半角数字の3)で処理されている部分と、表示上の「三連複」(漢数字)が混在しており、フィルタリング条件を間違えるとデータが空になって焦ります。

私がライブ中に冷や汗をかいたシーンはこちら(笑)。
[29:34 3連複の漢字と数字の違いにハマる](https://www.youtube.com/watch?v=tC_84sHW3dg&t=1774s)

残された課題(バグ報告)

無事に動いたように見えますが、実は重大なバグ(仕様上の考慮漏れ)があります。

「土曜日に日曜日の前日発売分を取得すると、メインレース(11R)しかないのに、1レース目として出力されてしまう」**

レース番号を単純に「Index + 1」で振ってしまっているため、データが1行しかないと、それが何レースであっても「1レース」と名付けてしまうのです。これは三流プログラマーあるあるですね……。

[41:31 不具合発覚!視聴者の「おいおい」という声が聞こえる瞬間](https://www.youtube.com/watch?v=tC_84sHW3dg&t=2491s)

このあたりの修正や、無駄なループ処理(同じページを何度も読みに行っている)の改善は、次回の課題とします。

関連リンク

試行錯誤の様子も含めて楽しんでいただければ幸いです。もし「こうした方がいいよ!」というアドバイスがあれば、ぜひYouTubeのコメント欄で教えてください。


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

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


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

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


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