DAM★ともの採点データを取得してCSV出力してみた

に公開

はじめに

カラオケで精密採点を歌うたびに、「あの曲、何点だったっけ?」「ビブラート何回だったかな?」と気になったことはありませんか?

DAM★ともには採点履歴が残っていますが、分析したくても一覧性がないCSVでダウンロードできない公式APIは200件までしか取れないという制約があります。

そこで今回、DAM★ともの採点データを全て取得してCSV化し、さらに3,000件以上の履歴も取得できるTypeScript製のAPIクライアントを作りました。

この記事で分かること

  • DAM★ともの採点データをプログラムから取得する方法
  • 公式API制限(200件)を超えて全履歴を取得する方法
  • 148〜187項目もの詳細データの活用方法
  • TypeScriptでの実装方法とAPI設計

作ったもの

https://github.com/LlechiKaito/karaoke-api

主な機能

✅ 3つの採点タイプに対応

  • 精密採点Ai - 最新の採点システム
  • 精密採点Ai Hearts - デュエット対応版
  • 精密採点DX-G - 従来版(詳細分析が強力)

✅ 詳細データを完全取得

公式サイトで見られる以上のデータが取れます:

{
  songName: "残酷な天使のテーゼ",
  totalScore: 95.680,

  // レーダーチャート
  radarChartPitch: 88,
  radarChartStability: 91,
  radarChartExpressive: 74,

  // ビブラート詳細
  vibratoCount: 28,
  vibratoSkill: 10,
  vibratoTotalSecond: 21.8,
  vibratoType: "A-3",  // 14種類のタイプ

  // 表現テクニック
  kobushiCount: 5,      // こぶし
  shakuriCount: 12,     // しゃくり
  fallCount: 8,         // フォール
  hiccupCount: 3,       // ヒーカップ

  // 音程グラフ(24セクション)
  // 全国平均との比較
  // などなど...
}

全148〜187項目のデータが取得可能!

✅ CSV出力機能

Excel対応のCSVで出力できるので、分析が捗ります:

# 精密採点Aiの全データをCSV出力
npm run export:csv:ai

# 上位5件だけCSV出力(テスト用)
npx ts-node src/exportCsv.ts --type=ai --limit=5

出力されるCSV:

  • BOM付きUTF-8(Excelでそのまま開ける)
  • 点数順に自動ソート
  • タイムスタンプ付きファイル名
  • 主要32項目を自動抽出

✅ 精密集計DX-G履歴取得(200件以上対応)

公式DAM APIは最大200件までしか取得できませんが、精密集計DX-Gサイト(https://dx-g.clubdam.info)から直接スクレイピングすることで、全履歴を取得できます。

# 全履歴を取得
npm run dxg:history:all

# CSV出力
npm run dxg:history:csv

セットアップ方法

1. インストール

git clone https://github.com/YOUR_USERNAME/karaoke-api
cd karaoke-api
npm install

2. 環境変数ファイルの作成

cp .env.example .env

3. 環境変数の設定

使いたい機能に応じて、以下の環境変数を設定します。

3-1. CDM_CARD_NO(必須)

全ての機能で必要な認証情報です。

取得手順:

  1. DAM★ともにログイン
  2. ブラウザの開発者ツール(F12)を開く
  3. ネットワークタブを選択
  4. 適当にページを操作して CalorieHistoryListXML.do のリクエストを探す
  5. レスポンス内の <cdmCardNo>ここの値</cdmCardNo> をコピー

.env ファイルに設定:

CDM_CARD_NO=取得したcdmCardNo

3-2. CDM_TOKEN(精密採点DX-G使用時のみ)

精密採点DX-G(公式API) を使用する場合のみ必要です。
npm run dev:dxgnpm run export:csv:dxg を使う場合に設定します。

取得手順:

  1. DAM★ともにログイン
  2. ブラウザの開発者ツール(F12)を開く
  3. ネットワークタブを選択
  4. DX-Gの採点履歴ページにアクセス
  5. ネットワークタブで GetScoringDxgListXML.do のリクエストを探す
  6. リクエストの Query String Parameters から cdmToken の値をコピー

.env ファイルに設定:

CDM_TOKEN=取得したcdmToken

3-3. DXG_HISTORY_USERNAME(精密集計DX-G履歴取得使用時のみ)

精密集計DX-G履歴取得 を使用する場合のみ必要です。
npm run dxg:historynpm run dxg:history:csv を使う場合に設定します。

取得手順:

  1. DAM★とも マイページにアクセス
  2. ページ上部に表示される「〇〇〇〇さん」の部分を確認
  3. 「さん」の前の部分があなたのユーザー名です

例: 「LLENNさん」と表示されている場合、ユーザー名は LLENN

.env ファイルに設定:

DXG_HISTORY_USERNAME=あなたのユーザー名

3-4. 設定例

最終的な .env ファイルの例:

# 必須(全ての機能で必要)
CDM_CARD_NO=あなたのcdmCardNo

# 精密採点DX-G(公式API)を使う場合のみ
CDM_TOKEN=あなたのcdmToken

# 精密集計DX-G履歴取得を使う場合のみ
DXG_HISTORY_USERNAME=あなたのユーザー名

環境変数の使い分け

環境変数 必須 用途
CDM_CARD_NO ✅ 必須 全ての機能で必要
CDM_TOKEN ⚠️ 条件付き 精密採点DX-G(公式API)使用時のみ
DXG_HISTORY_USERNAME ⚠️ 条件付き 精密集計DX-G履歴取得使用時のみ

4. 動作確認

# 精密採点Aiの最新5件を取得
npm run dev:ai

データが表示されれば成功です!

使い方

基本的な使用例

import { DAMClient } from './damClient';

const client = new DAMClient({
  cdmCardNo: process.env.CDM_CARD_NO!,
});

// 最新5件を取得
const result = await client.getScoringAiList({ detailFlg: 1 });
result.list?.data.forEach(score => {
  console.log(`${score.songName} - ${score.totalScore}点`);
});

全データを取得して統計分析

// 最大200件の全データを取得
const allData = await client.getAllScoringData('ai');
console.log(`総データ数: ${allData.length}件`);

// 平均点を計算
const average = allData.reduce((sum, s) => sum + s.totalScore, 0) / allData.length;
console.log(`平均点: ${average.toFixed(3)}点`);

// 90点以上の曲を抽出
const highScores = allData.filter(s => s.totalScore >= 90);
console.log(`90点以上: ${highScores.length}曲`);

// ビブラート回数でソート
const sortedByVibrato = allData.sort((a, b) =>
  (b as any).vibratoCount - (a as any).vibratoCount
);

精密集計DX-Gから全履歴を取得

公式API制限を回避して、全履歴を取得します:

import { DxgHistoryClient } from './dxgHistoryClient';

const client = new DxgHistoryClient('YOUR_USERNAME');

// 全履歴を取得(200件以上も可能)
const allHistory = await client.getAllHistory();
console.log(`総データ数: ${allHistory.length}件`);

// 点数でソート
const topScores = allHistory
  .sort((a, b) => b.totalScore - a.totalScore)
  .slice(0, 10);

topScores.forEach(record => {
  console.log(`${record.songName} - ${record.totalScore}点`);
  console.log(`素点: ${record.baseScore}点 + ボーナス: ${record.bonusScore}点`);
});

技術的な工夫

1. 2つのアプローチ

このプロジェクトでは、2つの異なるアプローチでデータを取得しています:

公式API経由(最大200件)

// damClient.ts
async getScoringAiList(params: {
  detailFlg?: number;
  offset?: number;
  limit?: number;
}) {
  const response = await axios.get(
    'https://www.clubdam.com/app/damtomo/GetScoringAiListXML.do',
    { params: { ...params, cdmCardNo: this.scrambledCardNo } }
  );
  return this.xmlParser.parseStringPromise(response.data);
}

メリット:

  • 公式APIなので安定している
  • 詳細データが取得できる(148〜187項目)
  • XMLからJSONへ自動変換

デメリット:

  • 最大200件までしか取得できない

スクレイピング経由(全履歴取得可能)

// dxgHistoryClient.ts
async getHistory(page: number = 1): Promise<DxgHistoryRecord[]> {
  const url = `https://dx-g.clubdam.info/user/${this.username}?page=${page}`;
  const response = await axios.get(url);
  const $ = cheerio.load(response.data);

  const records: DxgHistoryRecord[] = [];
  $('table.score-history tr').each((i, elem) => {
    // HTMLテーブルをパース
    const record = this.parseTableRow($, elem);
    if (record) records.push(record);
  });

  return records;
}

メリット:

  • 全履歴を取得できる(3,000件以上も可能)
  • ページネーションで自動取得

デメリット:

  • スクレイピングなのでHTML構造変更に弱い
  • 取得できる項目が少ない(15項目程度)

2. 型安全なAPI設計

TypeScriptの型定義を完備:

export interface ScoringData {
  scoringAiId: string;
  songName: string;
  artistName: string;
  playDate: string;
  totalScore: number;
  // ... 他多数
}

export type ScoringType = 'ai' | 'ai-hearts' | 'dx-g';

export interface DAMClientConfig {
  cdmCardNo: string;
  cdmToken?: string;
}

これにより、IDEの補完が効いて開発がスムーズになります。

3. ページネーション対応

全データ取得時の自動ページング:

async getAllHistory(): Promise<DxgHistoryRecord[]> {
  const allRecords: DxgHistoryRecord[] = [];
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    console.log(`ページ ${page} を取得中...`);
    const records = await this.getHistory(page);

    if (records.length === 0) {
      hasMore = false;
    } else {
      allRecords.push(...records);
      page++;
      await this.sleep(1000); // サーバー負荷軽減
    }
  }

  return allRecords;
}

4. CSV出力の工夫

Excel対応のCSV出力:

function convertToCSV(data: ScoringData[]): string {
  const headers = ['曲名', 'アーティスト', '総合点', ...];
  const rows = data.map(score => [
    escapeCSV(score.songName),
    escapeCSV(score.artistName),
    score.totalScore,
    // ...
  ]);

  const csvLines = [
    headers.join(','),
    ...rows.map(row => row.join(','))
  ];

  // BOM付きUTF-8(Excelで正しく開くため)
  return '\ufeff' + csvLines.join('\n');
}

実際に使ってみた結果

取得できたデータ

  • 精密採点Ai: 200件
  • 精密採点Ai Hearts: 200件
  • 精密採点DX-G(公式API): 200件
  • 精密集計DX-G履歴: 3,068件 🎉

分析してわかったこと

1. 平均点の推移

CSVをExcelで開いて、日付ごとの平均点をグラフ化したところ、徐々に上達していることが可視化できました。

2. 得意な音域の発見

高得点の曲を分析すると、キー設定が-2〜+1の範囲に集中していることが判明。自分の得意な音域がわかりました。

3. ビブラートの改善

ビブラート回数と点数の相関を分析。タイプ「A-3」(やや速め)が高得点につながることを発見。

4. 表現テクニックの傾向

  • こぶし: 平均3.2回
  • しゃくり: 平均8.5回
  • フォール: 平均5.1回

このデータを参考に、意識的にテクニックを使うように練習した結果、表現力の点数が5点アップ!

まとめ

DAM★ともの採点データを全て取得できるAPIクライアントを作成しました。

できること:

  • ✅ 3つの採点タイプに対応(Ai / Hearts / DX-G)
  • ✅ 詳細データを完全取得(148〜187項目)
  • ✅ CSV出力で分析が簡単
  • ✅ 公式API制限を超えて全履歴取得(3,000件以上)

使い道:

  • カラオケ上達のための分析
  • 得意な曲・苦手な曲の傾向把握
  • 時系列での成長記録
  • 統計的なアプローチでの練習計画

データドリブンでカラオケ上達を目指す方は、ぜひ使ってみてください!

リンク

参考資料

この記事は以下を参考に作成しました:


最後まで読んでいただき、ありがとうございました!

この記事が「いいね」と思ったら、ぜひ ❤️ をお願いします。
また、改善点や質問があれば、コメント欄でお気軽にどうぞ!

Discussion