🎁 买Hostinger主机,免费获得此源码 | zfuye.org

规范URL检测器

分析HTML以验证规范标签实现并识别SEO问题。

HTML输入

提供此HTML的来源URL,以检查自引用规范标签和HTTP/HTTPS一致性。

分析结果

等待分析...

    为什么要自建规范URL检测器?

    市面上大多数规范URL检测器工具要么需要注册账号,要么有广告骚扰,甚至按月收费。自己在 Hostinger 主机上部署一套规范URL检测器,数据完全由自己掌控,永久免费使用,还可以根据实际需求自由修改。

    规范URL检测器核心优势

    3步部署到你的网站

    1. 👉 购买 Hostinger 主机(约¥17/月起,支付宝付款)
    2. 📱 联系作者 QQ:2445964978,购买后免费获取规范URL检测器完整源码
    3. 📁 上传 index.html 到网站目录,访问域名即可直接使用

    🎁 购买主机 · 免费领取完整源码

    购买 Hostinger 主机后联系我,获取源码打包下载链接

    扫码加QQ

    📱 QQ:2445964978

    📧 [email protected]

    📖 完整建站图文教程 →

    🚀 立即购买 Hostinger
    parser = new DOMParser(); const doc = parser.parseFromString(html, "text/html"); const canonicalTags = Array.from(doc.querySelectorAll('link[rel="canonical"]')); const headCanonical = Array.from(doc.head.querySelectorAll('link[rel="canonical"]')); let resolvedHref = ""; // --- Placement: outside --- if (canonicalTags.length && headCanonical.length === 0) { addIssue("error", "Canonical tag outside ", "Move the canonical tag inside the section. Canonical tags outside are ignored by most search engines."); } // --- Missing --- if (canonicalTags.length === 0) { addIssue("error", "No canonical tag found", 'Add a tag inside the section so search engines know the preferred URL for this page.'); } // --- Multiple canonical tags --- if (canonicalTags.length > 1) { const hrefs = canonicalTags.map((t) => t.getAttribute("href") || "(empty)"); addIssue("error", `Multiple canonical tags found (${canonicalTags.length})`, `Only one canonical tag should exist per page. Found: ${hrefs.join(", ")}. Remove all but one.`); } if (canonicalTags.length >= 1) { const href = (canonicalTags[0].getAttribute("href") || "").trim(); resolvedHref = href; if (!href) { addIssue("error", "Empty canonical URL", "The canonical tag exists but its href attribute is empty. Provide a full, absolute URL."); } else if (!/^https?:\/\//i.test(href)) { addIssue("warning", "Relative canonical URL", `Use an absolute URL (including protocol and domain) instead of a relative canonical URL like "${href}". Relative canonicals are more error-prone and not recommended by Google.`); } else { let parsedHref = null; try { parsedHref = new URL(href); } catch { addIssue("error", "Invalid canonical URL", `"${href}" is not a valid absolute URL. Double-check for typos or malformed syntax.`); } if (parsedHref) { // --- Canonical chains / self-reference vs a known page URL --- let parsedPage = null; if (pageUrl) { try { parsedPage = new URL(pageUrl); } catch { addIssue("warning", "Page URL is not a valid absolute URL", "The optional page URL you provided could not be parsed. Self-reference and protocol checks were skipped."); } } if (parsedPage) { const sameUrl = parsedHref.href.replace(/\/$/, "") === parsedPage.href.replace(/\/$/, ""); if (!sameUrl) { addIssue("warning", "Canonical does not self-reference the page URL", `The page at "${parsedPage.href}" declares a canonical pointing to "${parsedHref.href}". This may be intentional (e.g. for duplicate/paginated content) but can also indicate a canonical chain — verify the target page's own canonical also points to itself.`); } else { addIssue("success", "Canonical self-references the page URL", "The canonical URL matches the page it was found on, which is the expected pattern for a page's primary canonical."); } // --- HTTP/HTTPS inconsistency --- if (parsedHref.protocol !== parsedPage.protocol) { addIssue("warning", "HTTP/HTTPS protocol mismatch", `The page is served over "${parsedPage.protocol}" but the canonical URL uses "${parsedHref.protocol}". Canonical URLs should use the same protocol as the live site (prefer HTTPS).`); } } if (parsedHref.protocol !== "https:") { addIssue("warning", "Canonical URL is not HTTPS", "Canonical URLs should point to the HTTPS version of a page when your site supports HTTPS."); } if (issues.every((i) => i.severity !== "error")) { addIssue("success", "Canonical tag is well-formed", "Found exactly one canonical tag inside , with a valid absolute URL."); } } } } const errorCount = issues.filter((i) => i.severity === "error").length; const warningCount = issues.filter((i) => i.severity === "warning").length; const totalChecks = issues.length || 1; const passedChecks = issues.filter((i) => i.severity === "success").length; // Score: start at 100, subtract per error/warning, floor at 0. let score = 100 - errorCount * 30 - warningCount * 12; score = Math.max(0, Math.min(100, score)); return { canonicalUrl: resolvedHref, issues, score, errorCount, warningCount, totalChecks, passedChecks, overall: errorCount > 0 ? "error" : warningCount > 0 ? "warning" : "success" }; } function scoreTier(score) { if (score >= 80) return "good"; if (score >= 50) return "fair"; return "poor"; } function renderReport(report) { canonicalUrl.value = report.canonicalUrl || ""; if (report.overall === "success") { statusCard.className = "status success"; statusCard.textContent = "Valid Canonical Implementation"; } else if (report.overall === "warning") { statusCard.className = "status warning"; statusCard.textContent = `${report.warningCount} Warning${report.warningCount === 1 ? "" : "s"} Found`; } else { statusCard.className = "status warning"; statusCard.textContent = `${report.errorCount} Issue${report.errorCount === 1 ? "" : "s"} Found`; } scoreCard.style.display = "flex"; scoreRing.textContent = String(report.score); scoreRing.className = "score-ring " + scoreTier(report.score); scoreSub.textContent = `${report.passedChecks} of ${report.totalChecks} checks passed`; issueList.innerHTML = report.issues .map( (issue) => `
  • ${issue.severity === "success" ? "✔" : issue.severity === "error" ? "✖" : "⚠"} ${escapeHtml(issue.title)}
    ${escapeHtml(issue.recommendation)}
  • ` ) .join(""); } function escapeHtml(str) { return String(str).replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); } function resetResultUI(message) { statusCard.className = "status neutral"; statusCard.textContent = message; canonicalUrl.value = ""; scoreCard.style.display = "none"; issueList.innerHTML = ""; lastReport = null; copyBtn.disabled = true; downloadBtn.disabled = true; downloadJsonBtn.disabled = true; } function analyzeHTML() { const html = htmlInput.value.trim(); if (!html) { resetResultUI("Please paste HTML source."); return; } const pageUrl = pageUrlInput.value.trim(); const report = buildReport(html, pageUrl); report.pageUrl = pageUrl || null; lastReport = report; renderReport(report); copyBtn.disabled = false; downloadBtn.disabled = false; downloadJsonBtn.disabled = false; } function buildTextReport(report) { const lines = ["Canonical URL Checker Report", "", `SEO Health Score: ${report.score}/100 (${report.passedChecks}/${report.totalChecks} checks passed)`, `Canonical URL: ${report.canonicalUrl || "Not Found"}`, report.pageUrl ? `Page URL: ${report.pageUrl}` : null, "", "Issues & Recommendations:"].filter(Boolean); report.issues.forEach((issue) => { lines.push(`- [${issue.severity.toUpperCase()}] ${issue.title}`); lines.push(` ${issue.recommendation}`); }); return lines.join("\n"); } analyzeBtn.addEventListener("click", analyzeHTML); copyBtn.disabled = true; downloadBtn.disabled = true; downloadJsonBtn.disabled = true; resetBtn.addEventListener("click", () => { htmlInput.value = ""; pageUrlInput.value = ""; resetResultUI("Waiting for analysis..."); }); copyBtn.addEventListener("click", async () => { if (!lastReport) return; const report = buildTextReport(lastReport); try { await navigator.clipboard.writeText(report); const originalText = copyBtn.textContent; const originalColor = copyBtn.style.backgroundColor; copyBtn.textContent = "Copied!"; copyBtn.style.backgroundColor = "#16a34a"; setTimeout(() => { copyBtn.textContent = originalText; copyBtn.style.backgroundColor = originalColor || "#1d9bf0"; }, 2000); } catch { // Clipboard API unavailable or blocked (e.g. insecure context) — // fail gracefully without breaking the rest of the tool. alert("Failed to copy report. Your browser may have blocked clipboard access."); } }); downloadBtn.addEventListener("click", () => { if (!lastReport) return; const report = buildTextReport(lastReport); try { const blob = new Blob([report], { type: "text/plain" }); const link = document.createElement("a"); link.href = URL.createObjectURL(blob); link.download = "canonical-url-report.txt"; link.click(); URL.revokeObjectURL(link.href); } catch { alert("Failed to download the report in this browser."); } }); downloadJsonBtn.addEventListener("click", () => { if (!lastReport) return; try { const blob = new Blob([JSON.stringify(lastReport, null, 2)], { type: "application/json" }); const link = document.createElement("a"); link.href = URL.createObjectURL(blob); link.download = "canonical-url-report.json"; link.click(); URL.revokeObjectURL(link.href); } catch { alert("Failed to download the report in this browser."); } }); exampleBtn.addEventListener("click", () => { htmlInput.value = ` Example

    Hello World

    `; pageUrlInput.value = "https://example.com/page"; analyzeHTML(); });