株式リンク生成ツール

2026年4月14日

アプリ

会社名が必要な場合は一覧リストをダウンロードしてからこのアプリににアップロードしてください

初期のパラメータを指定することができます

簡易サーバーを立ち上げて一覧リストを自動的に読み込む方法を最後に示してあります

例: stock-link-generator.html?codes=268A,7203,6758

start stock-link-generator.html?codes=268A,7203,6758

株式リンク生成ツール

株式リンク生成ツール

data_j.xls または CSV ファイルをここにドロップ
または | JPXからダウンロード

JavaScript

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>株式リンク生成ツール</title>
  <script src="https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js"></script>
  <style>
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body { font-family: "Segoe UI", "Hiragino Sans", sans-serif; max-width: 1200px; margin: 0 auto; padding: 20px; background: #f5f5f5; }
    h1 { text-align: center; margin-bottom: 16px; color: #333; }
    .controls { display: flex; gap: 8px; align-items: flex-start; flex-wrap: wrap; margin-bottom: 12px; }
    .controls > * { flex-shrink: 0; }
    textarea { width: 100%; height: 60px; padding: 10px; font-size: 14px; border: 1px solid #ccc; border-radius: 8px; resize: vertical; }
    input[type="text"] { padding: 10px 12px; font-size: 14px; border: 1px solid #ccc; border-radius: 8px; width: 220px; }
    button { padding: 10px 24px; font-size: 14px; background: #0070f3; color: white; border: none; border-radius: 8px; cursor: pointer; }
    button:hover { background: #005bb5; }
    .btn-secondary { background: #666; }
    .btn-secondary:hover { background: #444; }
    .output { display: flex; flex-direction: column; gap: 10px; }
    .row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; background: white; border-radius: 8px; padding: 8px 14px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
    .code { font-weight: bold; color: #333; font-size: 16px; min-width: 56px; }
    .name { color: #666; font-size: 13px; max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .links { display: flex; gap: 5px; flex-wrap: wrap; }
    .links a { background: #0070f3; color: white; padding: 8px 9px; border-radius: 5px; font-size: 12px; text-decoration: none; white-space: nowrap; }
    .links a:hover { background: #005bb5; }
    .empty { text-align: center; color: #888; margin-top: 20px; }
    .spinner { text-align: center; color: #888; margin-top: 20px; }
    #searchResults { background: white; border: 1px solid #ccc; border-radius: 8px; max-height: 300px; overflow-y: auto; display: none; position: absolute; z-index: 100; width: 300px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); }
    #searchResults.show { display: block; }
    .sr-item { padding: 8px 12px; cursor: pointer; border-bottom: 1px solid #eee; font-size: 13px; }
    .sr-item:hover { background: #e8f0fe; }
    .sr-item .sr-code { font-weight: bold; margin-right: 8px; color: #0070f3; }
    .sr-item .sr-name { color: #333; }
    .wrapper { position: relative; display: flex; gap: 8px; align-items: flex-start; flex-wrap: wrap; margin-bottom: 8px; width: 100%; }
    .loading-bar { width: 100%; height: 4px; background: #e0e0e0; border-radius: 2px; margin-bottom: 12px; overflow: hidden; display: none; }
    .loading-bar.show { display: block; }
    .loading-bar-inner { height: 100%; background: #0070f3; width: 0%; transition: width 0.3s; }
    .drop-zone { border: 3px dashed #ccc; border-radius: 12px; padding: 16px; text-align: center; color: #888; margin-bottom: 12px; transition: all 0.2s; }
    .drop-zone.dragover { border-color: #0070f3; background: #e8f0fe; color: #0070f3; }
  </style>
</head>
<body>
  <h1>株式リンク生成ツール</h1>
  <div class="loading-bar" id="loadingBar"><div class="loading-bar-inner" id="loadingBarInner"></div></div>
  <div class="drop-zone" id="dropZone">
    <div style="font-size:14px;">data_j.xls または CSV ファイルをここにドロップ</div>
    <div style="font-size:12px; margin-top:5px;">
      または <button id="fileBtn" style="padding:6px 16px; font-size:12px; background:#666; color:white; border:none; border-radius:6px; cursor:pointer;">ファイルを選択</button>
      | <a href="https://www.jpx.co.jp/markets/statistics-equities/misc/01.html" target="_blank" style="color:#0070f3;">JPXからダウンロード</a>
    </div>
  </div>
  <input type="file" id="csvInput" accept=".csv,.xls,.xlsx" style="display:none;">
  <div class="wrapper">
    <input type="text" id="searchInput" placeholder="銘柄名・コードで検索...">
    <button onclick="doSearch()">検索</button>
    <div id="searchResults"></div>
  </div>
  <textarea id="codes" placeholder="コードを入力(1行に1つ、例:268A)またはURLパラメータ ?codes=268A,7203"></textarea>
  <div class="controls">
    <button onclick="generate()">リンク生成</button>
    <button class="btn-secondary" onclick="copyAll()">すべてコピー</button>
    <button class="btn-secondary" onclick="clearAll()">クリア</button>
  </div>
  <div id="output"></div>
  <div id="debug" style="margin-top:20px; padding:12px; background:#fff; border-radius:8px; font-size:13px; color:#666; display:none;">
    <b>デバッグ:</b><br>
    <div id="debugInfo"></div>
  </div>

  <script>
    const SITES = [
      { name: "Yahoo! Finance", url: (c) => `https://finance.yahoo.co.jp/quote/${c}.T` },
      { name: "Yahoo! 掲示板", url: (c) => `http://textream.yahoo.co.jp/rd/finance/${c}` },
      { name: "karauri", url: (c) => `https://karauri.net/${c}/` },
      { name: "irbank", url: (c) => `https://irbank.net/${c}/pl` },
      { name: "株探", url: (c) => `https://kabutan.jp/stock/news?code=${c}` },
      { name: "MSN Money", url: (c) => `https://www.msn.com/ja-jp/money/stockdetails/${c}-jp-stock/fi-cepzxm?id=cepzxm` },
      { name: "Google Finance", url: (c) => `https://www.google.com/finance/quote/${c}:TYO?hl=ja` },
      { name: "チャートナビ", url: (c) => `https://chartnavi.com/brand/code/_${c}/` },
      { name: "nikkeiyosoku", url: (c) => `https://nikkeiyosoku.com/stock/${c}/` },
      { name: "株マップ", url: (c) => `https://jp.kabumap.com/servlets/kabumap/Action?SRC=basic/top/base&codetext=${c}` },
      { name: "みんかぶ", url: (c) => `https://minkabu.jp/stock/${c}` },
      { name: "株ドット", url: (c) => `https://www.kabutore.biz/genzaikabuka/${c}.html` },
    ];

    let stockData = [];

    function parseCSV(text) {
      const lines = text.split("\n");
      const rows = [];
      for (let i = 1; i < lines.length; i++) {
        const line = lines[i].trim();
        if (!line) continue;
        const parts = line.split(",");
        if (parts.length >= 3) {
          rows.push({
            code: parts[1].trim(),
            name: parts[2].trim(),
          });
        }
      }
      return rows;
    }

    function parseCSV(text) {
      const lines = text.split("\n");
      const rows = [];
      for (let i = 1; i < lines.length; i++) {
        const line = lines[i].trim();
        if (!line) continue;
        const parts = line.split(",");
        if (parts.length >= 3) {
          rows.push({
            code: parts[1].trim(),
            name: parts[2].trim(),
          });
        }
      }
      return rows;
    }

    function parseXLS(data) {
      const wb = XLSX.read(data, { type: "array" });
      const ws = wb.Sheets[wb.SheetNames[0]];
      const json = XLSX.utils.sheet_to_json(ws, { header: 1 });
      const rows = [];
      for (let i = 1; i < json.length; i++) {
        const row = json[i];
        if (row.length >= 3) {
          rows.push({
            code: String(row[1]).trim(),
            name: String(row[2]).trim(),
          });
        }
      }
      return rows;
    }

    async function loadData() {
      const bar = document.getElementById("loadingBar");
      const barInner = document.getElementById("loadingBarInner");
      bar.classList.add("show");
      barInner.style.width = "30%";

      const files = ["data_j.xls", "data_j.csv", "data_j.xlsx"];

      for (let i = 0; i < files.length; i++) {
        try {
          barInner.style.width = `${30 + (i * 20)}%`;
          const res = await fetch(files[i] + "?" + Date.now());
          const ab = await res.arrayBuffer();
          const name = files[i].toLowerCase();
          if (name.endsWith(".csv")) {
            const text = new TextDecoder().decode(ab);
            stockData = parseCSV(text);
          } else {
            stockData = parseXLS(ab);
          }
          barInner.style.width = "100%";
          setTimeout(() => bar.classList.remove("show"), 300);
          document.getElementById("debug").style.display = "block";
          document.getElementById("debugInfo").innerHTML = `${name}読込: ${stockData.length}行<br>1行目: ${JSON.stringify(stockData[0])}`;
          if (stockData.length > 0) generate();
          return;
        } catch (e) {}
      }

      bar.classList.remove("show");
    }

    function getCodesFromURL() {
      const params = new URLSearchParams(window.location.search);
      const codesParam = params.get("codes");
      if (codesParam) return codesParam.split(/[,\s\n]+/).map(c => c.trim().toUpperCase()).filter(c => c);
      const cParam = params.getAll("c");
      if (cParam.length) return cParam.map(c => c.trim().toUpperCase()).filter(c => c);
      return [];
    }

    function generate() {
      const codes = document.getElementById("codes").value.split("\n").map(c => c.trim().toUpperCase()).filter(c => c);
      const out = document.getElementById("output");
      if (!codes.length) { out.innerHTML = '<p class="empty">コードを入力してください</p>'; return; }
      out.innerHTML = codes.map(code => {
        const stock = stockData.find(s => s.code === code);
        const name = stock ? stock.name : "";
        return `<div class="row">
          <span class="code">${code}</span>
          ${name ? `<span class="name" title="${name}">${name}</span>` : ""}
          <div class="links">${SITES.map(s => `<a href="${s.url(code)}" target="_blank">${s.name}</a>`).join("")}</div>
        </div>`;
      }).join("");
    }

    window.addEventListener("DOMContentLoaded", () => {
      loadData();
      const urlCodes = getCodesFromURL();
      if (urlCodes.length) {
        document.getElementById("codes").value = urlCodes.join("\n");
      }
    });

    function copyAll() {
      const codes = document.getElementById("codes").value.split("\n").map(c => c.trim().toUpperCase()).filter(c => c);
      const text = codes.flatMap(code => SITES.map(s => s.url(code))).join("\n");
      navigator.clipboard.writeText(text).then(() => alert("コピーしました!"));
    }

    function clearAll() {
      document.getElementById("codes").value = "";
      document.getElementById("output").innerHTML = "";
    }

    const searchInput = document.getElementById("searchInput");
    const searchResults = document.getElementById("searchResults");

    function doSearch() {
      const q = searchInput.value.trim().toUpperCase();
      if (!q) { searchResults.classList.remove("show"); return; }
      const results = stockData.filter(s => s.code.includes(q) || s.name.toUpperCase().includes(q)).slice(0, 30);
      if (!results.length) { searchResults.classList.remove("show"); return; }
      searchResults.innerHTML = results.map(s =>
        `<div class="sr-item" data-code="${s.code}"><span class="sr-code">${s.code}</span><span class="sr-name">${s.name}</span></div>`
      ).join("");
      searchResults.classList.add("show");
    }

    searchInput.addEventListener("input", doSearch);
    searchInput.addEventListener("keydown", (e) => {
      if (e.key === "Enter") doSearch();
    });

    searchResults.addEventListener("click", (e) => {
      const item = e.target.closest(".sr-item");
      if (!item) return;
      const code = item.dataset.code;
      const ta = document.getElementById("codes");
      const lines = ta.value.split("\n").map(l => l.trim()).filter(l => l);
      if (!lines.includes(code)) {
        if (ta.value && !ta.value.endsWith("\n")) ta.value += "\n";
        ta.value += code + "\n";
      }
      searchInput.value = "";
      searchResults.classList.remove("show");
      generate();
    });

    const dropZone = document.getElementById("dropZone");

    dropZone.addEventListener("dragover", (e) => {
      e.preventDefault();
      dropZone.classList.add("dragover");
    });
    dropZone.addEventListener("dragleave", () => dropZone.classList.remove("dragover"));
    dropZone.addEventListener("drop", (e) => {
      e.preventDefault();
      dropZone.classList.remove("dragover");
      const file = e.dataTransfer.files[0];
      if (!file) return;
      loadFile(file);
    });

    document.getElementById("fileBtn").addEventListener("click", () => {
      document.getElementById("csvInput").click();
    });

    function loadFile(file) {
      const name = file.name.toLowerCase();
      const reader = new FileReader();
      if (name.endsWith(".csv")) {
        reader.onload = (ev) => {
          stockData = parseCSV(ev.target.result);
          document.getElementById("debug").style.display = "block";
          document.getElementById("debugInfo").innerHTML = `CSV読込: ${stockData.length}行<br>1行目: ${JSON.stringify(stockData[0])}`;
          generate();
        };
        reader.onerror = () => alert("ファイルの読み込みに失敗しました");
        reader.readAsText(file, "UTF-8");
      } else {
        reader.onload = (ev) => {
          stockData = parseXLS(new Uint8Array(ev.target.result));
          document.getElementById("debug").style.display = "block";
          document.getElementById("debugInfo").innerHTML = `XLS読込: ${stockData.length}行<br>1行目: ${JSON.stringify(stockData[0])}`;
          generate();
        };
        reader.onerror = () => alert("ファイルの読み込みに失敗しました");
        reader.readAsArrayBuffer(file);
      }
    }

    document.getElementById("csvInput").addEventListener("change", (e) => {
      const file = e.target.files[0];
      if (!file) return;
      loadFile(file);
    });

    document.addEventListener("click", (e) => {
      if (!e.target.closest(".wrapper")) searchResults.classList.remove("show");
    });
  </script>
</body>
</html>

Web サーバーを使った バッチファイル

次のようにファイルを配置してください 自動的に一覧ファイルが読み込まれます

D:.
│   stock-link-generator.bat
│
└───stock-link-generator
        data_j.xls
        stock-link-generator.html
        stock.txt

バッチファイル

call D:\WinPython\scripts\env_for_icons.bat  %*
cd "D:\stock-link-generator"
start "" /B Python -m http.server
timeout /t 10 
start http://localhost:8000/stock-link-generator.html?codes=268A,7203,6758

銘柄コードのファイルを読んだバッチファイル

call D:\WinPython\scripts\env_for_icons.bat  %*
cd "D:\stock-link-generator"
start "" /B Python -m http.server
timeout /t 10 
:: start http://localhost:8000/stock-link-generator.html?codes=268A,7203,6758

for /F "tokens=1 delims= " %%A in ('C:\app\busybox\sed.exe -e ":loop" -e "N" -e "$!bloop" -e "s/\r\n/\n/g"  -e "s/\n/\+/g" D:\stock-link-generator\stock.txt') do (
    set "stock=%%A"
)
echo  %stock%
:: 
start http://localhost:8000/stock-link-generator.html?codes=%stock%



timeout /t 10

stock.txt

5803
7011
5016
268A

JavaScript,

Posted by eightban