株式リンク生成ツール
アプリ
会社名が必要な場合は一覧リストをダウンロードしてからこのアプリににアップロードしてください
初期のパラメータを指定することができます
簡易サーバーを立ち上げて一覧リストを自動的に読み込む方法を最後に示してあります
例: 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







ディスカッション
コメント一覧
まだ、コメントがありません