CHUNITHMの所持キャラとランクの一覧をcsv形式でダウンロードするブックマークレットを作りました

タイトル通りです

# 使い方
1. 下のコードをコピー
1. 適当なページ(このページでOK)をブックマークに登録
1. ブックマークを編集→名前を適当に、URLをコピーしたコードに
1. CHUNITHM-NET→コレクション→キャラクター→キャラクターを変更
1. 「選択してください」のメニューがある画面でブックマークレットを実行

# コード
```javascript
javascript:(function(){if(location.href!=="https://new.chunithm-net.com/chuni-mobile/html/mobile/collection/characterList/")return;var sel=document.querySelector('select[name="idx"]');sel.value="9999";try{changeSelect();}catch(e){}function csvEscape(field){if(field==null)return"";field=field.toString();if(field.includes('"'))field=field.replace(/"/g,'""');if(field.includes(',')||field.includes('"')||field.includes('\n'))return '"'+field+'"';return field;}var csvLines=["キャラ名,ランク"];var links=document.querySelectorAll('#list > div > div > div > div.list_chara_textblock > div.character_name_block > form > a');links.forEach(function(link){var text=link.textContent.trim();var container=link.closest('div.list_chara_textblock');var imgs=container.querySelectorAll('div.character_list_rank_block.font_0 div.character_list_info_block div > span img');var numbers=[];var reg=/num_s_lv_(\d+)\.png/;imgs.forEach(function(img){var match=reg.exec(img.src);if(match)numbers.push(match[1]);});var combined=numbers.length?parseInt(numbers.join('')):"";csvLines.push(csvEscape(text)+","+csvEscape(combined));});var csvContent=csvLines.join("\n");var blob=new Blob([csvContent],{type:"text/csv;charset=utf-8;"});var url=URL.createObjectURL(blob);var a=document.createElement("a");a.href=url;a.download="chara_ranks.csv";document.body.appendChild(a);a.click();document.body.removeChild(a);URL.revokeObjectURL(url);})();
```

# 見やすくしたやつ
コメント付き
コピペには使わないほうがいいかも(ブラウザによって動かないこともあるっぽい)
```javascript
javascript:(function(){
  // 現在のURLが対象ページでなければ警告を表示して終了
  if(location.href !== "https://new.chunithm-net.com/chuni-mobile/html/mobile/collection/characterList/"){
    alert("このページでは実行できません");
    return;
  }

  // <select name="idx"> 要素を取得し、値を設定して changeSelect() を呼び出す
  var sel = document.querySelector('select[name="idx"]');
  sel.value = "9999";
  try {
    changeSelect();
  } catch(e) {}

  // CSVフィールドを適切にエスケープする関数
  function csvEscape(field) {
    if(field == null) return "";
    field = field.toString();
    // ダブルクオートをエスケープ
    if(field.includes('"')) field = field.replace(/"/g, '""');
    // フィールドにカンマ、ダブルクオート、改行が含まれる場合はダブルクオートで囲む
    if(field.includes(',') || field.includes('"') || field.includes('\n')) 
      return '"' + field + '"';
    return field;
  }

  // CSVのヘッダー行を設定
  var csvLines = ["キャラ名,ランク"];
  // キャラクターごとのリンク要素を選択
  var links = document.querySelectorAll(
    '#list > div > div > div > div.list_chara_textblock > div.character_name_block > form > a'
  );

  // 各リンクごとにキャラ名とランクを抽出
  links.forEach(function(link) {
    var text = link.textContent.trim();  // キャラ名
    // リンクの最も近い親コンテナを取得
    var container = link.closest('div.list_chara_textblock');
    // そのコンテナ内の画像要素を全て取得
    var imgs = container.querySelectorAll(
      'div.character_list_rank_block.font_0 div.character_list_info_block div > span img'
    );
    var numbers = [];
    var reg = /num_s_lv_(\d+)\.png/;  // 画像名から数字を抽出する正規表現
    imgs.forEach(function(img) {
      var match = reg.exec(img.src);
      if(match) numbers.push(match[1]);  // 数字部分を配列に追加
    });
    // 抽出した数字を連結して整数に変換、なければ空文字列
    var combined = numbers.length ? parseInt(numbers.join('')) : "";
    // キャラ名とランクをCSV形式で配列に追加
    csvLines.push(csvEscape(text) + "," + csvEscape(combined));
  });

  // CSVの内容を1つの文字列に結合
  var csvContent = csvLines.join("\n");
  // CSVデータをBlobとして作成
  var blob = new Blob([csvContent], {type:"text/csv;charset=utf-8;"});
  // ダウンロード用のURLを生成
  var url = URL.createObjectURL(blob);
  // 一時的なリンクを作成してクリックし、CSVをダウンロード
  var a = document.createElement("a");
  a.href = url;
  a.download = "chara_ranks.csv";
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  // 生成したURLを解放
  URL.revokeObjectURL(url);
})();
```