クローラー型検索エンジン

<!doctype html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Crawlio Search</title>
    <style>
      :root {
        color-scheme: light;
        --text: #202124;
        --muted: #5f6368;
        --line: #dadce0;
        --blue: #4285f4;
        --red: #ea4335;
        --yellow: #fbbc04;
        --green: #34a853;
        --shadow: 0 18px 40px rgba(60, 64, 67, 0.15);
      }

      * {
        box-sizing: border-box;
      }

      body {
        margin: 0;
        min-height: 100vh;
        color: var(--text);
        font-family: Arial, "Hiragino Kaku Gothic ProN", "Yu Gothic", Meiryo, sans-serif;
        background:
          radial-gradient(circle at top left, rgba(66, 133, 244, 0.12), transparent 32rem),
          linear-gradient(180deg, #fff 0%, #f7f9fc 68%, #eef3fa 100%);
      }

      a {
        color: inherit;
        text-decoration: none;
      }

      .topbar {
        display: flex;
        align-items: center;
        justify-content: space-between;
        min-height: 64px;
        padding: 0 28px;
      }

      .brand {
        display: inline-flex;
        align-items: center;
        gap: 7px;
        color: #3c4043;
        font-size: 15px;
        font-weight: 700;
      }

      .brand-dot {
        width: 8px;
        height: 8px;
        border-radius: 50%;
      }

      .nav {
        display: flex;
        gap: 22px;
        color: #3c4043;
        font-size: 14px;
      }

      .nav a:hover {
        text-decoration: underline;
      }

      main {
        width: min(1120px, calc(100% - 32px));
        margin: 0 auto;
      }

      .search-shell {
        position: relative;
        display: grid;
        place-items: center;
        min-height: 430px;
        padding: 38px 0 46px;
        overflow: hidden;
      }

      .crawler-visual {
        position: absolute;
        inset: 12px 0 auto;
        height: 320px;
        pointer-events: none;
        opacity: 0.92;
      }

      .orbit {
        position: absolute;
        left: 50%;
        top: 50%;
        border: 1px solid rgba(95, 99, 104, 0.18);
        border-radius: 50%;
        transform: translate(-50%, -50%);
      }

      .orbit-a {
        width: min(640px, 86vw);
        height: 210px;
      }

      .orbit-b {
        width: min(440px, 68vw);
        height: 145px;
        transform: translate(-50%, -50%) rotate(-12deg);
      }

      .node {
        position: absolute;
        width: 12px;
        height: 12px;
        border-radius: 50%;
        box-shadow: 0 0 0 8px rgba(66, 133, 244, 0.08);
      }

      .node-a {
        left: calc(50% - 302px);
        top: 120px;
        background: var(--blue);
      }

      .node-b {
        left: calc(50% + 250px);
        top: 88px;
        background: var(--green);
      }

      .node-c {
        left: calc(50% + 72px);
        top: 216px;
        background: var(--red);
      }

      .scan-line {
        position: absolute;
        left: 50%;
        top: 64px;
        width: 3px;
        height: 220px;
        background: linear-gradient(180deg, transparent, rgba(66, 133, 244, 0.72), transparent);
        animation: scan 3.4s ease-in-out infinite;
      }

      .wordmark {
        position: relative;
        z-index: 1;
        margin: 52px 0 25px;
        font-size: clamp(68px, 12vw, 112px);
        font-weight: 700;
        line-height: 0.95;
      }

      .blue { color: var(--blue); }
      .red { color: var(--red); }
      .yellow { color: var(--yellow); }
      .green { color: var(--green); }

      .search-form {
        position: relative;
        z-index: 1;
        width: min(640px, 100%);
      }

      .search-box {
        display: grid;
        grid-template-columns: 24px 1fr 42px;
        align-items: center;
        min-height: 58px;
        padding: 0 8px 0 21px;
        background: #fff;
        border: 1px solid var(--line);
        border-radius: 32px;
        box-shadow: 0 2px 8px rgba(60, 64, 67, 0.08);
        transition: box-shadow 160ms ease, border-color 160ms ease;
      }

      .search-box:focus-within,
      .search-box:hover {
        border-color: transparent;
        box-shadow: var(--shadow);
      }

      .search-box svg,
      .icon-button svg {
        width: 22px;
        height: 22px;
        fill: #5f6368;
      }

      input {
        width: 100%;
        border: 0;
        outline: 0;
        padding: 0 14px;
        color: var(--text);
        font-size: 17px;
        background: transparent;
      }

      .icon-button {
        display: grid;
        place-items: center;
        width: 42px;
        height: 42px;
        border: 0;
        border-radius: 50%;
        background: transparent;
        cursor: pointer;
      }

      .icon-button:hover {
        background: #f1f3f4;
      }

      .actions {
        display: flex;
        justify-content: center;
        gap: 12px;
        margin-top: 24px;
      }

      .actions button {
        min-width: 112px;
        min-height: 38px;
        border: 1px solid #f8f9fa;
        border-radius: 4px;
        padding: 0 18px;
        color: #3c4043;
        background: #f8f9fa;
        font-size: 14px;
        cursor: pointer;
      }

      .actions button:hover {
        border-color: #dadce0;
        box-shadow: 0 1px 1px rgba(0, 0, 0, 0.08);
      }

      .results-area {
        display: none;
        max-width: 760px;
        margin: 0 auto 46px;
      }

      .results-area.visible {
        display: block;
      }

      .result-meta {
        margin-bottom: 18px;
        color: var(--muted);
        font-size: 14px;
      }

      .result {
        padding: 18px 0;
        border-top: 1px solid #edf0f2;
      }

      .result-url {
        color: #3c4043;
        font-size: 13px;
      }

      .result h3 {
        margin: 4px 0 6px;
        color: #1a0dab;
        font-size: 21px;
        font-weight: 400;
      }

      .result p {
        margin: 0;
        color: #4d5156;
        font-size: 14px;
        line-height: 1.55;
      }

      .crawler-panel {
        margin: 12px 0 28px;
        padding: 24px;
        background: rgba(255, 255, 255, 0.82);
        border: 1px solid rgba(218, 220, 224, 0.9);
        border-radius: 8px;
        box-shadow: 0 12px 32px rgba(60, 64, 67, 0.08);
        backdrop-filter: blur(10px);
      }

      .panel-heading {
        display: flex;
        align-items: end;
        justify-content: space-between;
        gap: 18px;
        margin-bottom: 18px;
      }

      .eyebrow {
        margin: 0 0 4px;
        color: var(--blue);
        font-size: 12px;
        font-weight: 700;
        letter-spacing: 0.08em;
        text-transform: uppercase;
      }

      h2 {
        margin: 0;
        font-size: 24px;
      }

      .pulse {
        display: inline-flex;
        align-items: center;
        gap: 8px;
        color: #137333;
        font-size: 12px;
        font-weight: 700;
      }

      .pulse::before {
        content: "";
        width: 8px;
        height: 8px;
        border-radius: 50%;
        background: var(--green);
        box-shadow: 0 0 0 8px rgba(52, 168, 83, 0.12);
      }

      .crawl-grid {
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: 12px;
      }

      .crawl-card {
        min-height: 122px;
        padding: 16px;
        border: 1px solid #e6eaee;
        border-radius: 8px;
        background: #fff;
      }

      .crawl-card strong {
        display: block;
        margin-bottom: 8px;
        font-size: 15px;
      }

      .crawl-card span {
        display: block;
        color: var(--muted);
        font-size: 13px;
        line-height: 1.45;
      }

      .crawl-progress {
        height: 5px;
        margin-top: 14px;
        overflow: hidden;
        border-radius: 999px;
        background: #edf0f2;
      }

      .crawl-progress i {
        display: block;
        height: 100%;
        width: var(--progress);
        border-radius: inherit;
        background: linear-gradient(90deg, var(--blue), var(--green));
      }

      .stats-band {
        display: grid;
        grid-template-columns: repeat(4, minmax(0, 1fr));
        gap: 1px;
        overflow: hidden;
        margin-bottom: 44px;
        border: 1px solid #dfe4ea;
        border-radius: 8px;
        background: #dfe4ea;
      }

      .stats-band div {
        padding: 22px;
        background: #fff;
      }

      .stats-band strong,
      .stats-band span {
        display: block;
      }

      .stats-band strong {
        margin-bottom: 5px;
        font-size: 27px;
      }

      .stats-band span {
        color: var(--muted);
        font-size: 13px;
      }

      footer {
        display: flex;
        flex-wrap: wrap;
        gap: 22px;
        padding: 18px 28px;
        color: #70757a;
        background: #f2f2f2;
        font-size: 14px;
      }

      .sr-only {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
        border: 0;
      }

      @keyframes scan {
        0%, 100% {
          transform: translateX(-260px);
          opacity: 0.35;
        }
        50% {
          transform: translateX(260px);
          opacity: 1;
        }
      }

      @media (max-width: 760px) {
        .topbar {
          padding: 0 16px;
        }

        .nav {
          gap: 12px;
          font-size: 13px;
        }

        .search-shell {
          min-height: 390px;
        }

        .crawler-visual {
          height: 270px;
        }

        .node-a {
          left: 6%;
        }

        .node-b {
          left: 86%;
        }

        .node-c {
          left: 58%;
        }

        .actions {
          flex-wrap: wrap;
        }

        .crawl-grid,
        .stats-band {
          grid-template-columns: 1fr;
        }

        .panel-heading {
          align-items: start;
          flex-direction: column;
        }
      }
    </style>
  </head>
  <body>
    <header class="topbar">
      <a class="brand" href="#" aria-label="Crawlio Search">
        <span class="brand-dot blue"></span>
        <span class="brand-dot red"></span>
        <span class="brand-dot yellow"></span>
        <span class="brand-dot green"></span>
        <span>Crawlio</span>
      </a>
      <nav class="nav" aria-label="メイン">
        <a href="#crawler">Crawler</a>
        <a href="#index">Index</a>
        <a href="#status">Status</a>
      </nav>
    </header>

    <main>
      <section class="search-shell" aria-labelledby="hero-title">
        <div class="crawler-visual" aria-hidden="true">
          <div class="orbit orbit-a"></div>
          <div class="orbit orbit-b"></div>
          <div class="node node-a"></div>
          <div class="node node-b"></div>
          <div class="node node-c"></div>
          <div class="scan-line"></div>
        </div>
        <h1 id="hero-title" class="wordmark">
          <span class="blue">C</span><span class="red">r</span><span class="yellow">a</span><span class="blue">w</span><span class="green">l</span><span class="red">i</span><span class="blue">o</span>
        </h1>
        <form class="search-form" id="searchForm">
          <label class="sr-only" for="query">検索キーワード</label>
          <div class="search-box">
            <svg aria-hidden="true" viewBox="0 0 24 24">
              <path d="M10.8 18a7.2 7.2 0 1 1 5.1-12.3 7.2 7.2 0 0 1-5.1 12.3Zm0-2a5.2 5.2 0 1 0 0-10.4 5.2 5.2 0 0 0 0 10.4Zm6.3.1 4 4-1.4 1.4-4-4 1.4-1.4Z" />
            </svg>
            <input id="query" name="query" autocomplete="off" placeholder="URL、キーワード、サイト名を検索" />
            <button class="icon-button" type="button" id="voiceButton" aria-label="音声検索">
              <svg aria-hidden="true" viewBox="0 0 24 24">
                <path d="M12 14a3 3 0 0 0 3-3V6a3 3 0 1 0-6 0v5a3 3 0 0 0 3 3Zm5-3a5 5 0 0 1-10 0H5a7 7 0 0 0 6 6.9V21h2v-3.1a7 7 0 0 0 6-6.9h-2Z" />
              </svg>
            </button>
          </div>
          <div class="actions">
            <button type="submit">検索</button>
            <button type="button" id="crawlButton">クローラーを走らせる</button>
          </div>
        </form>
      </section>

      <section class="results-area" aria-live="polite">
        <div class="result-meta" id="resultMeta">約 8,420,000 件中 0.38 秒</div>
        <div class="results" id="results"></div>
      </section>

      <section class="crawler-panel" id="crawler" aria-labelledby="crawler-title">
        <div class="panel-heading">
          <div>
            <p class="eyebrow">Live Crawl</p>
            <h2 id="crawler-title">巡回中のページ</h2>
          </div>
          <span class="pulse">ONLINE</span>
        </div>
        <div class="crawl-grid" id="crawlGrid"></div>
      </section>

      <section class="stats-band" id="index" aria-label="インデックス統計">
        <div>
          <strong>12.8B</strong>
          <span>Indexed pages</span>
        </div>
        <div>
          <strong>94ms</strong>
          <span>Median lookup</span>
        </div>
        <div>
          <strong>37K/s</strong>
          <span>Crawl rate</span>
        </div>
        <div>
          <strong>99.98%</strong>
          <span>Freshness</span>
        </div>
      </section>
    </main>

    <footer id="status">
      <span>Japan</span>
      <span>Privacy</span>
      <span>Terms</span>
      <span>Search Console</span>
    </footer>

    <script>
      const results = [
        {
          title: "Crawlio Search Console - サイトのクロール状況",
          url: "https://crawlio.example/search-console",
          text: "サイトマップ、robots.txt、インデックス登録、検索パフォーマンスをまとめて確認できます。"
        },
        {
          title: "高速インデックスの仕組み",
          url: "https://crawlio.example/docs/indexing",
          text: "分散クローラーがページを発見し、内容を解析して、新しい検索結果へ反映します。"
        },
        {
          title: "ニュース、画像、動画を横断検索",
          url: "https://crawlio.example/discover",
          text: "キーワードに関連するページ、メディア、トレンドをひとつの検索画面で素早く探せます。"
        },
        {
          title: "Web Crawler Health Report",
          url: "https://status.crawlio.example/crawler",
          text: "現在のクロール速度、エラー率、再訪問キュー、インデックス鮮度のライブ統計です。"
        }
      ];

      const crawlItems = [
        ["news.metro.jp/today", "HTML parsed / 32 links discovered", 78],
        ["shop.example.com/products", "Sitemap queued / canonical found", 64],
        ["docs.dev.local/api", "Robots allowed / snippets updated", 91],
        ["media.example.net/video", "Metadata extracted / thumbnail indexed", 56],
        ["blog.studio.jp/launch", "Fresh content detected / rank signals ready", 84],
        ["archive.city.jp/events", "Recrawl scheduled / duplicate checked", 43]
      ];

      const form = document.querySelector("#searchForm");
      const queryInput = document.querySelector("#query");
      const resultsArea = document.querySelector(".results-area");
      const resultMeta = document.querySelector("#resultMeta");
      const resultList = document.querySelector("#results");
      const crawlGrid = document.querySelector("#crawlGrid");
      const crawlButton = document.querySelector("#crawlButton");
      const voiceButton = document.querySelector("#voiceButton");

      function renderResults(query = "クローラー") {
        const filtered = results.map((item) => ({
          ...item,
          title: query ? `${item.title} | ${query}` : item.title
        }));

        resultMeta.textContent = `約 ${(8420000 + query.length * 17321).toLocaleString("ja-JP")} 件中 ${(0.21 + Math.random() * 0.28).toFixed(2)} 秒`;
        resultList.innerHTML = filtered
          .map(
            (item) => `
              <article class="result">
                <div class="result-url">${item.url}</div>
                <h3>${item.title}</h3>
                <p>${item.text}</p>
              </article>
            `
          )
          .join("");
        resultsArea.classList.add("visible");
      }

      function renderCrawlGrid(offset = 0) {
        crawlGrid.innerHTML = crawlItems
          .map(([url, status, progress], index) => {
            const shifted = Math.min(99, Math.max(24, progress + ((offset + index * 7) % 18) - 7));
            return `
              <article class="crawl-card">
                <strong>${url}</strong>
                <span>${status}</span>
                <div class="crawl-progress" aria-label="クロール進捗 ${shifted}%">
                  <i style="--progress: ${shifted}%"></i>
                </div>
              </article>
            `;
          })
          .join("");
      }

      form.addEventListener("submit", (event) => {
        event.preventDefault();
        renderResults(queryInput.value.trim() || "クローラー");
      });

      crawlButton.addEventListener("click", () => {
        renderCrawlGrid(Math.floor(Math.random() * 20));
        renderResults(queryInput.value.trim() || "live crawl");
        document.querySelector("#crawler").scrollIntoView({ behavior: "smooth", block: "start" });
      });

      voiceButton.addEventListener("click", () => {
        queryInput.value = "最新のインデックス状況";
        queryInput.focus();
      });

      renderCrawlGrid();
    </script>
  </body>
</html>

投稿者: chosuke

趣味はゲームやアニメや漫画などです

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です