0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Kaggle CLIでノートブックの実行状況とログを7行に要約する(bat+メモ帳からの卒業/Windows文字化け対策つき)

0
Posted at

はじめに

こんにちは、松延です。ZEN大学(通信制)の2年生で、AIエンジニアを目指して勉強しています。

Kaggleのノートブック(kernel)をローカルから kaggle kernels push で動かすようになってから、地味に時間を取られていたのが「今どうなっているかの確認」でした。実行中なのか、終わったのか、失敗したのか。失敗したならどこで落ちたのか。これを毎回ブラウザやログファイルで確認するのが面倒だったんです。

この記事では、Kaggle CLIの kaggle kernels status と kaggle kernels output を Python から呼び出して、状態とログを約7行のサマリー+CSV 1行にまとめるスクリプトを紹介します。あわせて、Windows で日本語が文字化けしたときの対策と、bat からでもAIエージェントからでも同じスクリプトを使えるようにするための工夫もまとめます。

なお、提出(submit)まわりの話ではなく、あくまで実行状況の監視とログの要約に絞った内容です。

以前のやり方

最初は、こんな bat ファイルで済ませていました。

@echo off
kaggle kernels status koheip/my-notebook
kaggle kernels output koheip/my-notebook -p .\out -o
notepad .\out\my-notebook.log

動くには動くのですが、困りごとがいくつかありました。

  • ログが長く、メモ帳でスクロールしてエラー箇所を探すのが大変
  • 日本語を含む出力がコンソール上で文字化けする
  • 「いつ、どのノートブックが、どういう状態だったか」の記録が残らない
  • AIエージェントにログ確認を頼むと、ログ全文を読ませることになり無駄が多い

特に最後の点が大きかったです。エージェントに渡すなら、必要な情報だけに絞った短いテキストのほうがずっと扱いやすいんですよね。

CLIで取る情報

使うコマンドは2つだけです(公式ドキュメント: Kaggle CLI Documentation、kaggle.com/docs/api)。

kaggle kernels status

最新の実行の状態を表示します。owner/kernel-slug の形式で指定します。

kaggle kernels status koheip/my-notebook

出力は次のような1行です(失敗時は Failure message: "..." の行が追加されます)。

koheip/my-notebook has status "KernelWorkerStatus.COMPLETE"

CLIのバージョンによって complete のような小文字表記だったり、KernelWorkerStatus. 付きだったりするので、スクリプト側では最後の単語だけ取り出して小文字にそろえるようにしています。

kaggle kernels output

最新の実行の出力ファイルをダウンロードします。主なオプションは次のとおりです。

オプション 意味
-p, --path 保存先フォルダ
-o, --force 既存ファイルを上書き
-q, --quiet 進捗表示を抑える
--file-pattern 正規表現に一致するファイルだけ取得

ポイントは、出力ファイルと一緒に <kernel-slug>.log という実行ログも保存されることです。中身は多くの場合、stream_name(stdout / stderr)・time・data を持つ要素の JSON 配列になっています。今回はこのログを読んで要約します。

大きな出力ファイルがあるノートブックでは、--file-pattern で絞ると速くなります。

認証

Kaggle CLI は、環境変数 KAGGLE_API_TOKEN、~/.kaggle/access_token、従来の ~/.kaggle/kaggle.json などで認証できます。今回は「トークンを専用ファイルから読み、子プロセスにだけ環境変数として渡す」方式にしました。こうすると、bat から呼んでもエージェントから呼んでも、同じ読み込み経路になります。

要約スクリプト例

kaggle_status_summary.py として保存して使います。標準ライブラリのみで動きます。

# kaggle_status_summary.py
import argparse
import csv
import json
import os
import re
import subprocess
import sys
from datetime import datetime
from pathlib import Path

# Windowsでも標準出力をUTF-8に寄せる(Python 3.7+)
if hasattr(sys.stdout, "reconfigure"):
    sys.stdout.reconfigure(encoding="utf-8", errors="replace")

DEFAULT_TOKEN_FILE = Path.home() / ".config" / "kaggle_token.txt"


def build_env(token_file: Path) -> dict:
    """トークンをファイルから読み、子プロセス用の環境変数を作る"""
    env = os.environ.copy()
    env["PYTHONIOENCODING"] = "utf-8"
    env["PYTHONUTF8"] = "1"
    if token_file.exists():
        env["KAGGLE_API_TOKEN"] = token_file.read_text(encoding="utf-8").strip()
    return env


def run(cmd: list[str], env: dict) -> str:
    r = subprocess.run(
        cmd, env=env, capture_output=True,
        text=True, encoding="utf-8", errors="replace",
    )
    if r.returncode != 0:
        raise RuntimeError(f"{' '.join(cmd)} failed: {r.stderr.strip()[:300]}")
    return r.stdout


def get_status(kernel: str, env: dict) -> tuple[str, str]:
    out = run(["kaggle", "kernels", "status", kernel], env)
    m = re.search(r'has status "([^"]+)"', out)
    status = m.group(1).split(".")[-1].lower() if m else "unknown"
    f = re.search(r'Failure message: "(.*)"', out)
    return status, (f.group(1) if f else "")


def read_log(log_path: Path) -> list[tuple[str, str]]:
    """(stream_name, text) のリストにする。JSONでなければ素のテキストとして扱う"""
    raw = log_path.read_text(encoding="utf-8", errors="replace")
    try:
        items = json.loads(raw)
        return [(i.get("stream_name", ""), str(i.get("data", ""))) for i in items]
    except (json.JSONDecodeError, AttributeError, TypeError):
        return [("", line) for line in raw.splitlines()]


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("kernel", help="owner/kernel-slug")
    ap.add_argument("--out", default="kaggle_out")
    ap.add_argument("--csv", default="kaggle_status_log.csv")
    ap.add_argument("--token-file", type=Path, default=DEFAULT_TOKEN_FILE)
    args = ap.parse_args()

    env = build_env(args.token_file)
    status, failure = get_status(args.kernel, env)

    slug = args.kernel.split("/")[1]
    out_dir = Path(args.out) / slug
    entries, files = [], []
    if status in ("complete", "error", "cancelacknowledged"):
        out_dir.mkdir(parents=True, exist_ok=True)
        run(["kaggle", "kernels", "output", args.kernel,
             "-p", str(out_dir), "-o", "-q"], env)
        files = [p.name for p in out_dir.iterdir() if p.is_file()]
        log_path = out_dir / f"{slug}.log"
        if log_path.exists():
            entries = read_log(log_path)

    err_pat = re.compile(r"(Error|Exception|Traceback|エラー)")
    stderr_n = sum(1 for s, _ in entries if s == "stderr")
    err_lines = [t.strip() for _, t in entries if err_pat.search(t)]
    last_line = next((t.strip() for _, t in reversed(entries) if t.strip()), "")
    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    # ここが「約7行」のサマリー
    print(f"[kernel]  {args.kernel}")
    print(f"[checked] {now}")
    print(f"[status]  {status}" + (f" / {failure}" if failure else ""))
    print(f"[log]     {len(entries)} entries (stderr: {stderr_n})")
    print(f"[errors]  {len(err_lines)} lines")
    print(f"[last_err] {err_lines[-1][:120] if err_lines else '-'}")
    print(f"[tail]    {last_line[:120] or '-'}  / files: {len(files)}")

    # 履歴としてCSVに1行追記(Excelで開けるようにBOM付き)
    csv_path = Path(args.csv)
    new = not csv_path.exists()
    with csv_path.open("a", newline="", encoding="utf-8-sig") as f:
        w = csv.writer(f)
        if new:
            w.writerow(["checked_at", "kernel", "status", "log_entries",
                        "stderr", "error_lines", "last_error", "files"])
        w.writerow([now, args.kernel, status, len(entries), stderr_n,
                    len(err_lines), err_lines[-1][:200] if err_lines else "",
                    len(files)])
    return 0 if status != "error" else 1


if __name__ == "__main__":
    sys.exit(main())

実行するとこんな感じになります(値は例です)。

[kernel]  koheip/my-notebook
[checked] 2026-10-31 15:42:10
[status]  complete
[log]     214 entries (stderr: 12)
[errors]  0 lines
[last_err] -
[tail]    処理が完了しました  / files: 3

工夫したところは次の3点です。

  • 終了コード:error のときは 1 を返すので、bat の if errorlevel 1 やエージェント側で分岐しやすい
  • 状態の正規化:CLIのバージョン差を吸収するため、KernelWorkerStatus.COMPLETE → complete のようにそろえる
  • 実行中はダウンロードしない:running / queued のときは status だけ見て終わるので、短い間隔で呼んでも軽い

ただし、Kaggle の API にはレート制限があります。ループで監視する場合は、数分おきなど間隔を空けるようにしましょう。

トークンファイルの置き方

トークンは Kaggleの設定ページ で発行し、1行だけのテキストファイルとして保存します。

# PowerShell:トークンファイルを作成(中身はトークン1行だけ)
New-Item -ItemType Directory -Force "$HOME\.config" | Out-Null
Set-Content -Path "$HOME\.config\kaggle_token.txt" -Value "ここにトークン" -Encoding ascii -NoNewline

このファイルは Git 管理下に置かない、記事やチャットに貼らない、の2点は徹底しています。エージェントから使うときも、渡すのは「スクリプトのパス」だけで、トークンそのものは見せずに済みます。

Windowsの文字化け対策

Windows の日本語環境では、コンソールのコードページが既定で CP932(Shift_JIS)です。一方、Kaggle のログは UTF-8 なので、何もしないと日本語が化けたり、UnicodeEncodeError で止まったりします。

対策は「コンソール側」と「Python側」の両方で UTF-8 にそろえることです。

bat から呼ぶ場合

@echo off
chcp 65001 >nul
set PYTHONIOENCODING=utf-8
set PYTHONUTF8=1
python "%~dp0kaggle_status_summary.py" koheip/my-notebook
if errorlevel 1 echo [!] 実行がエラーで終わっています

bat ファイル自体に日本語を書く場合は、ファイルを UTF-8 で保存しておくと chcp 65001 後に正しく表示されます。

PowerShell から呼ぶ場合

chcp 65001 > $null
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$OutputEncoding = [System.Text.Encoding]::UTF8
$env:PYTHONIOENCODING = "utf-8"
$env:PYTHONUTF8 = "1"

python .\kaggle_status_summary.py koheip/my-notebook
if ($LASTEXITCODE -ne 0) { Write-Host "[!] 実行がエラーで終わっています" }

Python 側でやっていること

スクリプト内でも念のため次の3つをしています。

  • sys.stdout.reconfigure(encoding="utf-8") で標準出力を UTF-8 に
  • subprocess.run(..., encoding="utf-8", errors="replace") で子プロセスの出力を UTF-8 として読む
  • 子プロセス(kaggle CLI)の環境変数にも PYTHONIOENCODING=utf-8 を渡す

chcp 65001 だけ、PYTHONIOENCODING だけでは片方しか直らないケースがあり、私も最初はハマりました。両方そろえるのがコツです。

まとめ

  • kaggle kernels status で状態、kaggle kernels output で出力と <slug>.log を取得できる
  • Python でログを読み、約7行のサマリー+CSV 1行にまとめると、確認がぐっと楽になる
  • トークンはファイルから読み込んで子プロセスに渡すと、bat からもエージェントからも同じ経路で使える
  • Windows では chcp 65001 と PYTHONIOENCODING=utf-8 の両方で文字化けを防ぐ

メモ帳でログをスクロールしていたころに比べて、確認にかかる時間も、エージェントに渡す情報量も大きく減りました。同じように Kaggle のノートブックをローカルから回している方の参考になればうれしいです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?