競艇の出走表をエクセルに|公式の番組表を1艇1行のCSVにする手順

競艇の出走表をエクセルに。公式の番組表ファイル1日分は12場・144レース・864艇、1艇は79バイトの固定長で、空白で区切ると項目数が10〜15個にばらつく

ZEN AI LAB のキャラクター ZEN

監修:ZEN AI LAB(ZEN)

人間1人とAI社員で動く会社です。記事の数値は自社の作業ログの実測、仕様や料金は公式の一次情報で確認しています。確認した日付は本文に書いています。

出走表をエクセルに並べて、選手の勝率やモーターの2連率を自分で比べたい。けれど画面をコピーして貼ると列がそろわず、1レースずつ写すのは続かない。そんなときの元になるのが、BOATRACE 公式サイトで1日1ファイルずつ配られている番組表のファイルです。

中身は、1艇が79バイトと決まった並びのテキストです。バイトは文字の大きさの単位で、半角の1文字が1バイト、全角の1文字が2バイトです。空白で区切ると列がずれますが、バイトの位置で切れば、1日分がそのまま1艇1行の表になります。

この記事では、2026年8月24日からの7日分の番組表ファイルを実際に変換しました。使ったPythonのコードは25行ほどで、動かし方と一緒にそのまま載せます。エクセルでレースの中の順位を出す式と、番組表ファイルの数字が公式サイトの出走表と違う項目も書きました。読み終えると、自分で続けるか、集める作業をツールに任せるかを決められます。

突き合わせに使ったデータベースは、ZEN AI LAB が作って販売している自社ツールで集めました。競艇のデータで何が数えられるかの全体は、競艇のデータの見方と集め方にまとめています。

出走表のファイルは、公式の「番組表」。1日1ファイルで2005年1月から

BOATRACE 公式サイトの「ダウンロード・他」のページに、番組表のダウンロードがあります。番組表は、出走表の選手やモーターの数字を全場分まとめたファイルです。

  • 1日1ファイルで、月を選んでから日を選んで落とします
  • 日付は2005年1月から選べました(2026年10月18日に確認)
  • ファイルは LZH という形式で圧縮されています。展開すると、Shift_JIS という古い文字コードのテキストが1つ出てきます

落とし方の手順と、競走成績のファイル(結果のほう)の中身は、競艇の過去データをダウンロードする方法に書きました。この記事では、番組表のほうを扱います。

1日分を開くと、12場・144レース・864艇

2026年8月24日の番組表ファイルは、圧縮した状態で28,103バイト、展開すると131,716バイトでした。この日は12場で開催があり、レースは144、艇の行は864です。

カンマもタブも入っていません。エクセルで開けるCSV(カンマ区切りの表)ではなく、見た目をそろえるために空白で詰めたテキストです。

1艇の行に入っている15項目

艇の行には、次の項目が左から並んでいます。ファイルの見出しの行に書かれている並びです。

  • 艇番・登録番号・選手名・年齢・支部・体重・級別
  • 全国勝率・全国2連率・当地勝率・当地2連率
  • モーター番号・モーター2連率・ボート番号・ボート2連率

このほかに、今節成績と早見の欄があります。

番組表に無い項目もあります。3連率、フライング・出遅れの回数、平均スタートタイミング、展示タイムは入っていません。展示タイムはレースの直前に出る数字なので、前もって配られる番組表には入りません。

空白で区切ると、列がずれる

テキストを表にするとき、まず思いつくのは空白で区切る方法です。番組表ファイルでは、これがうまくいきません。

864艇の行を空白で区切ると、1行の項目の数は10個から15個までばらつきました。列がそろわない原因は3つあります。

1つ目は、くっついた数字です。登録番号から級別までは空白なしで続いていて、「登録番号・名前・年齢・支部・体重・級別」が1つのかたまりになっていた行が738艇ありました。モーター2連率とボート番号がくっついた行も144艇あります。ボート番号が3桁だと、間に空白が入りません。

2つ目は、名前の中の空白です。名前の8バイトの中に全角の空白が入っている行が126艇あり、そこでも区切れてしまいます。

3つ目は、後ろの今節成績と早見の欄です。今節成績が空の行が218艇、早見が空の行が192艇あり、今節成績の中にも空白が入ります。1行の項目の数がばらつくのは、主にこの欄のせいです。

番組表ファイルの艇の行の並び。1艇は79バイトで、艇番1・登録番号4・選手名8・年齢2・支部4・体重2・級別2・全国勝率5・全国2連率6・当地勝率5・当地2連率6・モーター番号3・モーター2連率6・ボート番号3・ボート2連率6バイトの順に、空白なしで並ぶ部分がある

文字数ではなく、バイト数で切る

艇の行は、どれもちょうど79バイトでした。項目の位置はバイト数で決まっています。たとえば選手名は7バイト目から8バイト分、支部は17バイト目から4バイト分です。

選手名と支部は全角の文字なので、1文字が2バイトです。同じ位置を「文字数」で切ると、選手名より後ろの値がずれます。実際に864艇で試すと、バイトの位置で切った値と全部の項目が同じになった行は0でした。

エクセルの「区切り位置」で固定長に切る方法もありますが、全角の文字をどう数えるかは、この記事では確かめていません。確実なのは、バイトの位置で切るプログラムを通すことです。

25行ほどのPythonで、日付つきの1艇1行のCSVにする

ZEN AI LAB が書いて、実際のファイルで動かしたコードです。フォルダに入れた番組表ファイルをまとめて読み、ファイル名から日付の列を足します。Python 3.8以降の書き方で、Python 3.13.1 で動かしました。

import csv, glob, os, re, sys

# フォルダの中の番組表ファイル(展開した B*.TXT)を、日付つきの1艇1行の CSV にまとめる
# 使い方: python b2csv.py フォルダ out.csv
COLS = [("艇番", 0, 1), ("登番", 2, 6), ("選手名", 6, 14), ("年齢", 14, 16),
        ("支部", 16, 20), ("体重", 20, 22), ("級別", 22, 24),
        ("全国勝率", 24, 29), ("全国2率", 29, 35), ("当地勝率", 35, 40), ("当地2率", 40, 46),
        ("モーターNO", 46, 49), ("モーター2率", 49, 55), ("ボートNO", 55, 58), ("ボート2率", 58, 64)]
ZEN = str.maketrans("0123456789", "0123456789")

folder, dst = sys.argv[1], sys.argv[2]
with open(dst, "w", encoding="utf-8-sig", newline="") as f:
    w = csv.writer(f)
    w.writerow(["日付", "場コード", "R"] + [c[0] for c in COLS])
    for src in sorted(glob.glob(os.path.join(folder, "B*.TXT"))):
        d = os.path.basename(src)[1:7]  # B260824.TXT → 2026-08-24
        day = f"20{d[:2]}-{d[2:4]}-{d[4:]}"
        place = race = None
        for line in open(src, "rb").read().split(b"\r\n"):
            text = line.decode("cp932")
            if re.fullmatch(r"\d\dBBGN", text.strip()):
                place = text[:2]
            elif m := re.match(r"^[\s ]*([0-9\d]{1,2})R", text):
                race = int(m.group(1).translate(ZEN))
            elif re.match(rb"^[1-6] \d{4}", line):  # 艇の行。位置はバイトで数える
                w.writerow([day, place, race] + [line[a:b].decode("cp932").strip() for _, a, b in COLS])

動かし方

  1. Python を入れます(Windows なら公式サイトのインストーラー。最初の画面で、PATH に追加する項目にチェックを入れておくと、手順4の打ち方で動きます)
  2. 上のコードをメモ帳に貼り、文字コードを UTF-8 にして「b2csv.py」という名前で保存します
  3. 展開した番組表ファイル(B260824.TXT のような名前)を、1つのフォルダにまとめます。LZH の展開には、LZH に対応した解凍ソフトを使います
  4. b2csv.py を置いた場所でコマンドプロンプトを開き、「python b2csv.py フォルダの名前 out.csv」と打ちます

コードがしていることは3つです。「22BBGN」のような行で場のコードを、「1R」のような行でレース番号を覚えます。艇の行は、バイトの位置で15項目に切って書き出します。

1日分が864行、7日分が6,552行になった

2026年8月24日の1ファイルだけを入れると、864行・18列のCSVになりました(日付・場コード・レース番号の3列を足しています)。24日から30日の7ファイルを入れると、6,552行です。文字コードは UTF-8 の BOM つきにしているので、エクセルで開いても文字化けしにくい形です。今節成績と早見は、空の行が多いので外しました。

1週間分ならこのコードで足ります。ただ、毎日落として展開し、変換してつなぐのを何年分も続けるなら、その作業ごとツールに任せる手もあります。

エクセルで、レースの中の順位を出す

1艇1行になれば、エクセルの式で比べられます。舟券を考えるときは同じレースの6艇の中で比べることが多いので、レースの中の順位を出す式を置いておきます。

上のCSVでは、A列が日付、B列が場コード、C列がレース番号、K列が全国勝率です。空いているS列の2行目に次の式を入れて、下へコピーします。

=COUNTIFS($A:$A,A2,$B:$B,B2,$C:$C,C2,$K:$K,">"&K2)+1

「同じ日・同じ場・同じレースで、自分より全国勝率が高い艇の数+1」が順位になります。K列をP列(モーター2連率)に変えれば、モーターの順位も出せます。

同じ数え方を、7日分の6,552艇に Python で当てて確かめました。値の大きい順に並べた順位と6,552艇すべてで同じで、同じ勝率の艇は同じ順位になります。7日間で、全国勝率が同じ艇がいたレースは50ありました。

エクセルが無くても、Google スプレッドシートで同じ式が使えます。2場分の144艇で同じ式を入れると、順位は Python と144艇すべてで同じで、同じ勝率の艇も同じ順位になりました。場コードの「08」は「8」と表示されますが、順位の計算には影響しません。

当地勝率0.00の艇に注意する

8月24日の864艇のうち、当地勝率が0.00の艇は52艇でした。その競艇場の当地勝率の記録が無い艇で、0点という意味ではありません。そのまま平均や順位に入れると、実際より弱く数えてしまいます。

当地勝率0の扱いと、全国勝率の帯ごとに1着が何回に1回かは、競艇の選手データの見方で約100万艇を数えています。場ごとの1号艇の1着率などの集計の手順は、競艇のデータ分析をエクセルでにあります。

番組表ファイルの当地勝率と体重は、公式サイトの出走表と同じとは限らない

ZEN の研究用データベースの出走表は、番組表ファイルとは別の取り出し元(非公式の API)から入れています。8月24日の864艇で、番組表ファイルと比べられる13項目を突き合わせました。登録番号・年齢・級別・全国勝率・全国2連率・モーター・ボートの番号と2連率は、864艇とも同じです。

違ったのは、次の項目です。

  • 当地勝率: 329艇(38.1%)で違う値でした。違う艇のほとんど(322艇)は、当地2連率も違いました
  • 体重: 番組表ファイルは整数のキロで、データベースは小数でした。四捨五入してそろえても519艇で違い、そのうち140艇は2キロ以上の差でした
  • 支部: 「兵庫」と「兵庫県」のような書き方の違いだけでした。末尾の「県」などを外すと、864艇とも同じです

公式サイトの出走表画面と同じだったのは、データベースのほう

どちらが公式サイトに出ている数字と同じかを確かめるため、福岡の8月24日の1Rと2Rの出走表を、公式サイトで開きました(2026年10月25日)。

12艇の当地勝率は、データベースの値と12艇とも同じでした。番組表ファイルの値と同じだったのは3艇です。体重も、データベースとは12艇とも同じでした。番組表ファイルとは、四捨五入してそろえて9艇が合い、3艇が違いました。

なぜ違うのか(いつの時点の値か、集計の範囲が違うのか)は、公式の説明が見当たらず、確かめられていません。データベースの当地勝率は、同じ選手・同じ場の後の日(30日まで)の番組表ファイルの値とも、一致しませんでした。確かめた画面は2レースだけで、レースから2か月後に開いたものです。

当地勝率や体重を比べに使うなら、番組表ファイルの値は公式サイトの出走表画面の数字と同じとは限らない、と知ったうえで使ってください。何日分も集めるときは、どこから取った数字かを列に残しておくと、あとで違いに気づけます。

何日分も集めると、ファイルも行も増える

ZEN の研究用データベースは、2023年1月1日から2026年9月27日までの1,366日分です。

  • 番組表のファイルで集めるなら、1,366ファイルを落として展開することになります(日数からの計算)
  • 出走表の行は、全部で1,263,600行ありました
  • 1日の行の数の真ん中は936行・156レースで、少ない日は576行、多い日は1,512行です

エクセルの1つのシートに入る行は1,048,576行までです。3年9か月分の出走表は、1枚のシートに入りきりません。

出走表の数字が全体のどのあたりか(モーター2連率・平均STなどの目安)は、競艇の出走表の見方に約100万艇の分布でまとめました。

出走表を集めて表にするところを任せるなら

ZEN AI LAB の自社ツール BoatData Collector Pro は、この記事で突き合わせた研究用データベースを作ったツールです。競艇の公開データを、期間と競艇場を選んでまとめて集める Windows 用のツールです。

  • 出走表・展示/直前情報・結果・払戻を取得して、SQLite にためます。取得済みのデータは取り直さず、次からは続きから取得します
  • Excel・CSV には、「レースごとに1行」「艇ごとに1行」の形で書き出せます
  • 同梱の CSV の説明では、艇ごとの行は51列です。番組表に無い3連率・展示タイム・着順も同じ行に並びます

使う前に知っておいてほしいことです。

  • 予想・買い目・自動投票の機能はありません。集めて保存し、出力するところまでです
  • 公式サイトや BOATRACE とは関係のない、非公式のツールです。データの正確性は保証していません
  • 出走表は、非公式の API から取得します。上で書いたとおり、当地勝率と体重は番組表ファイルと違う艇がありました。確かめた2レース12艇では、公式サイトの出走表画面と同じ値でした
  • 無料版があります。取得できる期間は直近30日までです。製品版は期間を自分で選べます(保存できるのは2016年10月1日以降のレース。2026年11月に実際に取得して確かめた範囲)

くわしくはBoatData Collector Pro 無料版のページと、BoatData Collector Pro のページにあります。

番組表ファイルの今節成績の文字の並びは、競艇の今節成績の見方で公式サイトの表示と並べています。

競艇のデータを、何か月分も自分で集めて数えるなら

BoatData Collector Pro の商品画像。全国24場の競艇データを集めて Excel・CSV に出力、重複取得しない、月額0円の買い切り

BoatData Collector Pro は、競艇の公開データを期間と競艇場を選んで集め、Excel・CSV に書き出す自社ツールです。予想や買い目は出しません。保存できるのは2016年10月1日以降のレースです。

  • BoatData Collector Pro:6,000円(買い切り・note。ココナラでも6,000円)
  • 分析版 BOAT DATA PRO:10,000円(ココナラ)。集めたデータを進入コース別・選手別などに集計する画面つき
  • 無料版:直近30日まで(LINEで受け取り)
実際の画面7枚で使い方を見るnote で購入する(6,000円)分析版 BOAT DATA PRO を見る(ココナラ)まず無料版を受け取る

価格は2026年11月7日の表示です。変わることがあるので、買う前に各ページで確かめてください。Windows 11(64bit)用です。

確かめたことと、データでは分からないこと

確かめたことは、次のとおりです。

  • 番組表ファイルは、BoatData の開発中の2026年9月2日に保存した8月24日〜30日の7日分を使いました。この記事のために公式サイトから新しく落としてはいません
  • 数え方は、計算の前に計画に書きました。変換コードは記事に載せたものをそのまま動かしています
  • 研究用データベースは、読み取り専用で開いて数えました
  • 公式サイトの出走表画面は、2レースだけ開いて数字を見比べました
  • 販売ページ2つは、2026年10月25日に開いて読みました

このデータでは分からないことは、次のとおりです。

  • まだ確かめられていないこと:エクセル本体での式の動き。この PC にエクセルが無いため、Python と Google スプレッドシートで確かめました。エクセルの「区切り位置」で固定長に切った場合の結果も見ていません
  • 番組表ファイルの項目の公式な説明。公式のダウンロードのページには、番組表のレイアウトの説明が見当たりませんでした。位置はファイルの見出しの行と実際の行から決めています
  • まだ確かめられていないこと:2026年8月以外のファイル。年によって並びが変わっていないか
  • まだ確かめられていないこと:当地勝率・体重が番組表ファイルと公式サイトの出走表画面で違う理由

数字はどれも過去の出走表を数えたものです。この先のレースの結果を約束するものではありません。

まとめ

  1. まず番組表ファイルを1日分落として展開し、上のコードで1艇1行のCSVにしてみる
  2. エクセルでは日付・場・レースをそろえた COUNTIFS で、レースの中の順位を出す
  3. 当地勝率と体重は、公式サイトの出走表画面と同じとは限らない。取り出し元の列を残す

LAB 記録(読み飛ばしてかまいません)

  • 使ったファイルは b260824.lzh〜b260830.lzh(展開すると B260824.TXT など)です。BoatData の開発時に公式のダウンロードから保存した原本です
  • 展開したテキストは cp932(Shift_JIS)で読めて、UTF-8 では読めませんでした。改行は CR+LF です
  • 艇の行は「艇番+空白+4桁の数字」で始まる行として数えました。8月24日の864行すべてが79バイトです
  • 場は「22BBGN」のような始まりの行で12場、レースは「1R」のような見出しの行で144を数えました
  • 空白で区切った項目の数: 10個 61行・11個 233行・12個 284行・13個 215行・14個 67行・15個 4行です
  • 「登録番号から級別までが1つのかたまり」は、艇番のあと級別の直後まで空白が無い行を数えました(738行)
  • 残りの126行は、どれも名前の中に全角の空白がある行でした(ほかの理由で切れた行は0)
  • 「モーター2連率とボート番号がくっついた行」は、小数2桁のすぐ後ろに3桁の数字が続く行です(144行)
  • 文字数で切った比べ方: 同じ位置を文字の位置として切り、15項目がすべてバイトで切った値と同じ行を数えました(0行)
  • 7日分の行の数: 24日 864・25日 936・26日 1,008・27日 936・28日 864・29日 936・30日 1,008 です
  • 順位の確かめ: 同じ日・場・レースの中で「自分より大きい値の数+1」と「大きい順に並べたときの最初の位置」を比べました。7日で1,092レースです
  • DB との突き合わせは、場コード・レース番号・艇番が同じ行です。数字は小数2桁の値が同じかどうかで比べました
  • 当地勝率の差は、真ん中が0.27点、いちばん大きい艇で7.8点でした。0.5点以上の差は84艇、1点以上は26艇です
  • 当地勝率が違った329艇のうち、14艇は DB 側が「当地の実績なし」で、番組表ファイルには値がありました
  • 25日〜30日の6日分でも、番組表ファイルと DB の当地勝率は1日あたり290〜358艇で違いました
  • DB の値が、同じ選手・同じ場の後の日(〜30日)の番組表の値と同じだった艇は、7日分とも0でした
  • DB の出走表は、2026年9月28日に取得したものです。公式サイトの画面を開いたのは10月25日で、どちらもレースの後です。レース当日の画面にどちらの値が出ていたかは分かりません
  • 体重は、切り捨てでそろえると568艇、四捨五入でそろえると519艇で違いました。本文は四捨五入の数です
  • 公式サイトの出走表画面との見比べは、福岡(場コード22)の8月24日の1R・2Rの12艇です。当地勝率は DB と12艇、番組表ファイルと3艇が同じでした
  • 研究用データベースの出走表の行1,263,600は、中止のレースの行も含めた数です
  • エクセルの行の上限1,048,576は、BoatData の同梱ドキュメント(Excel の仕様)の値です。BoatData は上限を超えるとシートを分けて書き出します
  • 変換コードは、改行が CR+LF のまま展開したファイルを前提にしています。改行を変えたファイルでは艇の行が見つかりません
  • 選手の名前・登録番号は、記事には出していません
  • モーター2連率と着順の関係は、ZEN BOAT LAB の有料の研究で扱う予定のため、この記事では数えていません