SEO 站长助手(快捷脚本)

在任意页面右侧浮动一个快捷脚本启动器,一键把当前域名丢到 Ahrefs/Similarweb/Google 等工具;支持自定义脚本列表(JSON 管理)。

// ==UserScript==
// @name         SEO 站长助手(快捷脚本)
// @namespace    https://yestool.org
// @version      1.0.0
// @description  在任意页面右侧浮动一个快捷脚本启动器,一键把当前域名丢到 Ahrefs/Similarweb/Google 等工具;支持自定义脚本列表(JSON 管理)。
// @author       https://github.com/yestool
// @license MIT
// @match        *://*/*
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_openInTab
// @run-at       document-idle
// ==/UserScript==

(function() {
  'use strict';

  // ---------- Utilities ----------
  const SKEY = 'qs_scripts_v1';
  const HKEY = 'qs_hidden_v1';

  // 简易 eTLD+1 近似:常见多段后缀覆盖,其他场景回退为最后两段
  const multiPartTLD = new Set([
    'co.uk','org.uk','ac.uk','gov.uk','co.jp','ne.jp','or.jp','com.au','net.au','org.au','co.nz','org.nz','com.br','com.cn','net.cn','org.cn','gov.cn','edu.cn','com.hk','com.sg'
  ]);
  function getDomain(hostname) {
    hostname = (hostname || location.hostname || '').toLowerCase();
    if (!hostname) return '';
    const parts = hostname.split('.');
    if (parts.length <= 2) return hostname;
    const last2 = parts.slice(-2).join('.');
    const last3 = parts.slice(-3).join('.');
    if (multiPartTLD.has(last2)) {
      // xxx.co.uk 场景 -> 取最后三段
      return parts.slice(-3).join('.');
    }
    if (multiPartTLD.has(last3)) {
      // 极少见更长多段,兜底
      return parts.slice(-4).join('.');
    }
    // 普通:取最后两段
    return last2;
  }

  function openTab(url, active = true) {
    try {
      GM_openInTab(url, { active, insert: true });
    } catch (e) {
      window.open(url, '_blank');
    }
  }

  function $(sel, root=document) { return root.querySelector(sel); }

  // 读取/初始化脚本定义
  function defaultScripts() {
    return [
      {
        name: "Ahrefs Backlink",
        type: "func",
        desc: "Ahrefs Backlink查询",
        code: `
          var domain = window.location.hostname;
          var ahrefsUrl = 'https://ahrefs.com/backlink-checker?input=' + encodeURIComponent(domain) + '&mode=subdomains';
          window.open(ahrefsUrl, '_blank');
        `
      },
      {
        name: "Ahrefs Site Explorer(domain)",
        type: "urlTemplate",
        desc: "用当前域名打开 Ahrefs Site Explorer(需已登录(不可用))",
        urlTemplate: "https://app.ahrefs.com/site-explorer/overview/v2/subdomains/live?target={domain}"
      },
      {
        name: "Google site:(domain)",
        type: "urlTemplate",
        desc: "快速 site: 当前域名",
        urlTemplate: "https://www.google.com/search?q=site%3A{domain}"
      },
      {
        name: "Similarweb ",
        type: "urlTemplate",
        desc: "查看 Similarweb 站点画像(需已登录(不可用))",
        urlTemplate: "https://pro.similarweb.com/#/digitalsuite/websiteanalysis/overview/website-performance/*/999/1m?webSource=Total&key={domain}"
      },
      {
        name: "Ahrefs Keyword Difficulty",
        type: "func",
        desc: "Ahrefs KD值查询(仅Google搜索页有效)",
        code: `
          var kw=document.querySelector('textarea[name=q]').value;
          var ahrefsUrl = 'https://ahrefs.com/keyword-difficulty/?country=us&input=' + encodeURIComponent(kw);
          window.open(ahrefsUrl, '_blank');
        `
      }
    ];
  }

  function readScripts() {
    let scripts = GM_getValue(SKEY, null);
    if (!scripts || !Array.isArray(scripts) || scripts.length === 0) {
      scripts = defaultScripts();
      GM_setValue(SKEY, scripts);
    }
    return scripts;
  }

  function saveScripts(scripts) {
    if (!Array.isArray(scripts)) return;
    GM_setValue(SKEY, scripts);
  }

  // ---------- UI ----------
  // 用 Shadow DOM 隔离样式,避免被站点 CSS 污染
  const host = document.createElement('div');
  host.id = 'qs-launcher-host';
  document.documentElement.appendChild(host);
  const shadow = host.attachShadow({ mode: 'open' });

  const style = document.createElement('style');
  style.textContent = `
    .qs-wrap{ position: fixed; top: 35%; right: 12px; z-index: 2147483647; font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial;}
    .qs-btn{ width: 44px; height: 44px; border-radius: 50%; background: #111; color:#fff; display:flex; align-items:center; justify-content:center; cursor:pointer; box-shadow:0 6px 18px rgba(0,0,0,.25); transition:.2s; }
    .qs-btn:hover{ transform: translateY(-1px); }
    .qs-hide{ display:none !important; }

    .qs-panel{ position: fixed; top: 20%; right: 70px; width: 320px; max-height: 60vh; overflow:auto; background:#fff; color:#111; border:1px solid #e5e7eb; border-radius:12px; box-shadow:0 10px 30px rgba(0,0,0,.15); }
    .qs-header{ display:flex; align-items:center; justify-content:space-between; padding:10px 12px; border-bottom:1px solid #eee; position:sticky; top:0; background:#fff; z-index:1;}
    .qs-title{ font-weight:600; font-size:14px;}
    .qs-actions button{ margin-left:8px; font-size:12px; padding:4px 8px; border:1px solid #e5e7eb; background:#f8fafc; border-radius:8px; cursor:pointer;}
    .qs-actions button:hover{ background:#eef2f7;}
    .qs-list{ padding:8px; }
    .qs-item{ display:flex; flex-direction:column; padding:8px; border-radius:10px; border:1px solid #f1f5f9; margin-bottom:8px; }
    .qs-item h4{ margin:0 0 6px 0; font-size:14px; }
    .qs-item p{ margin:0 0 8px 0; font-size:12px; color:#475569;}
    .qs-run{ align-self:flex-start; padding:6px 10px; border-radius:8px; border:1px solid #e5e7eb; background:#111; color:#fff; cursor:pointer; font-size:12px;}
    .qs-run:hover{ filter:brightness(1.05); }
    .qs-empty{ padding:16px; color:#64748b; font-size:13px; }
    .qs-close{ position:absolute; top:6px; right:8px; background:transparent; border:none; font-size:18px; cursor:pointer; color:#333;}
    .qs-toggle{ margin-left:8px; font-size:12px; padding:4px 8px; border:1px solid #e5e7eb; background:#fff; border-radius:8px; cursor:pointer;}
  `;

  const wrap = document.createElement('div');
  wrap.className = 'qs-wrap';

  const btn = document.createElement('div');
  btn.className = 'qs-btn';
  btn.title = '快捷脚本';
  btn.textContent = '⚡';

  const panel = document.createElement('div');
  panel.className = 'qs-panel qs-hide';
  panel.innerHTML = `
    <div class="qs-header">
      <div class="qs-title">快捷脚本</div>
      <div class="qs-actions">
        <button class="qs-manage" title="管理脚本(JSON)">⚙ 管理</button>
        <button class="qs-hidebtn" title="隐藏浮标">🙈 隐藏</button>
      </div>
      <button class="qs-close" title="关闭面板">×</button>
    </div>
    <div class="qs-list"></div>
  `;

  shadow.append(style, wrap);
  wrap.append(btn, panel);

  // 记住隐藏状态
  const hidden = GM_getValue(HKEY, false);
  if (hidden) wrap.classList.add('qs-hide');

  // 列表渲染
  function renderList() {
    const list = $('.qs-list', shadow);
    list.innerHTML = '';
    const scripts = readScripts();
    if (!scripts.length) {
      const div = document.createElement('div');
      div.className = 'qs-empty';
      div.textContent = '暂无脚本,点右上角 “⚙ 管理” 添加。';
      list.appendChild(div);
      return;
    }
    const ctx = buildContext();
    scripts.forEach((s, idx) => {
      const item = document.createElement('div');
      item.className = 'qs-item';
      const h4 = document.createElement('h4');
      h4.textContent = s.name || `脚本 #${idx+1}`;
      const p = document.createElement('p');
      p.textContent = s.desc || '';
      const run = document.createElement('button');
      run.className = 'qs-run';
      run.textContent = '执行';
      run.addEventListener('click', () => runScript(s, ctx));
      item.append(h4, p, run);
      list.appendChild(item);
    });
  }

  // 上下文对象
  function buildContext() {
    const hostname = location.hostname;
    const domain = getDomain(hostname);
    const selection = String(window.getSelection ? (window.getSelection()+'') : '') || '';
    const title = document.title || '';
    const url = location.href;
    return { hostname, domain, selection, title, url };
  }

  // 执行器:两类——urlTemplate / func(字符串函数体)
  function runScript(s, ctx) {
    try {
      if (s.type === 'urlTemplate' && s.urlTemplate) {
        const finalUrl = s.urlTemplate
          .replaceAll('{domain}', encodeURIComponent(ctx.domain))
          .replaceAll('{hostname}', encodeURIComponent(ctx.hostname))
          .replaceAll('{url}', encodeURIComponent(ctx.url))
          .replaceAll('{title}', encodeURIComponent(ctx.title))
          .replaceAll('{selection}', encodeURIComponent(ctx.selection));
        if (!finalUrl || !/^https?:\/\//i.test(finalUrl)) {
          alert('URL 模板无效');
          return;
        }
        openTab(finalUrl, true);
        return;
      }
      if (s.type === 'func' && s.code) {
        // 在 userscript 沙箱中执行,可直接操作 DOM
        const fn = new Function(s.code);
        fn.call(window);
        return;
      }
      alert('未知脚本类型或缺少必要字段。');
    } catch (e) {
      console.error('[Quick Scripts] 执行出错:', e);
      alert('脚本执行失败:' + e.message);
    }
  }

  // 事件绑定
  btn.addEventListener('click', () => {
    panel.classList.toggle('qs-hide');
    if (!panel.classList.contains('qs-hide')) renderList();
  });
  panel.querySelector('.qs-close').addEventListener('click', () => {
    panel.classList.add('qs-hide');
  });
  panel.querySelector('.qs-hidebtn').addEventListener('click', () => {
    wrap.classList.add('qs-hide');
    GM_setValue(HKEY, true);
  });
  panel.querySelector('.qs-manage').addEventListener('click', () => {
    const current = JSON.stringify(readScripts(), null, 2);
    const next = prompt(
      '以 JSON 数组形式编辑脚本:\n' +
      '支持两种类型:\n' +
      '1) { "name":"xxx", "type":"urlTemplate", "desc":"...", "urlTemplate":"https://...{domain}..." }\n' +
      '2) { "name":"xxx", "type":"func", "desc":"...", "code":"/* JS 函数体,可用 document.querySelector 等 */" }\n\n' +
      '当前:',
      current
    );
    if (next == null) return;
    try {
      const parsed = JSON.parse(next);
      if (!Array.isArray(parsed)) throw new Error('必须是数组');
      saveScripts(parsed);
      alert('已保存。');
      renderList();
    } catch (e) {
      alert('JSON 解析失败:' + e.message);
    }
  });

  // 在页面左下角加一个“显示浮标”的极简入口(当你把浮标隐藏后)
  function addRestoreBtn() {
    if ($('#qs-restore', shadow)) return;
    const r = document.createElement('button');
    r.id = 'qs-restore';
    r.textContent = '⚡';
    r.title = '显示快捷脚本浮标';
    r.style.position = 'fixed';
    r.style.left = '10px';
    r.style.bottom = '10px';
    r.style.zIndex = '2147483647';
    r.style.width = '36px';
    r.style.height = '36px';
    r.style.borderRadius = '50%';
    r.style.border = '1px solid #e5e7eb';
    r.style.background = '#fff';
    r.style.cursor = 'pointer';
    r.addEventListener('click', () => {
      wrap.classList.remove('qs-hide');
      GM_setValue(HKEY, false);
      r.remove();
    });
    shadow.appendChild(r);
  }
  if (hidden) addRestoreBtn();

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址