Skip to content

結構化資料驗證:複合式搜尋結果測試與 Schema 檢查工具

結構化資料驗證:三個工具的分工

結構化資料 寫完不要憑感覺,一個尾隨逗號就能讓整段標記失效,而且 完全沒有錯誤提示

三個工具各管一段。

三個工具的分工

工具檢查什麼範圍速度
複合式搜尋結果測試能不能取得複合式結果、缺哪些欄位單頁即時
Schema Markup Validator純語法與型別是否合法單頁即時
Search Console 強化報告全站範圍的錯誤統計全站數天延遲

三個都要用

它們管不同的事,缺一個就會有盲區:

  • 只用複合式結果測試 → 看不到全站有幾頁出錯。
  • 只用強化報告 → 改完要等好幾天才知道對不對。
  • 只用通用驗證器 → 語法對了,但可能不符合搜尋引擎的顯示條件。

複合式搜尋結果測試

Rich Results Test 是最常用的一個。

兩種輸入模式

模式適合
網址測線上的頁面(也能看到渲染後的結果)
程式碼測還沒上線的標記、或本機的內容

程式碼模式可以測本機

本機的網址(localhost)它連不到。把建置產出的 HTML 貼進「程式碼」分頁就能測,這是上線前驗證的方式。

bash
# 把產出的 HTML 複製到剪貼簿(Windows)
cat dist/seo/schema-article.html | clip

報告怎麼讀

區塊意思
偵測到的項目找到哪些型別、各幾個
有效項目通過,可以取得複合式結果
無效項目有錯誤,不會 取得複合式結果
警告缺少建議欄位,仍可能顯示
預覽部分型別可以預覽搜尋結果的外觀

錯誤與警告的差別很重要

分級意思要不要修
錯誤缺少 必填 欄位必須修,否則完全不會顯示
警告缺少 建議 欄位依情況補,不影響能否顯示

常見的警告是「缺少 image」或「缺少 author.url」,補上會讓呈現更完整,但不補也還是有效。

它也看得到渲染後的結果

用網址模式測試時,「檢視測試過的網頁」會顯示 渲染後的 HTML,這對 動態注入的結構化資料 是必要的驗證:

動態注入一定要用這個確認

搜尋引擎渲染後讀不到那段標記的話,不管寫得多正確都沒用。搜尋渲染後的 HTML 裡有沒有 application/ld+json 就知道了。

也可以看「JavaScript 主控台訊息」有沒有程式碼錯誤,詳見 純 CSR 的 SEO 問題

Schema Markup Validator

validator.schema.org 是 Schema.org 官方的驗證器。

複合式結果測試Schema Markup Validator
檢查標準搜尋引擎的顯示條件Schema.org 的定義
涵蓋型別只有支援複合式結果的全部型別
會報告缺少必填欄位型別與屬性是否存在、值的型態

什麼時候需要它

情況為什麼
用了不支援複合式結果的型別前者不認識,它認識
想確認屬性名稱有沒有拼錯它會標出不存在的屬性
想確認嵌套結構合不合法它會檢查型別關係

例如:HowTo 已經停止顯示複合式結果,複合式結果測試可能就不報告它了,但通用驗證器仍會檢查它的語法。

通過通用驗證器不等於能取得複合式結果

兩者標準不同。合法的 Schema.org 標記不一定符合搜尋引擎額外要求的必填欄位。

順序是先過通用驗證器(語法對),再過複合式結果測試(符合顯示條件)。

Search Console 強化報告

Search Console 會依您標記的型別自動出現對應報告(文章、麵包屑、商品、常見問答等)。

特性說明
範圍全站
延遲數天
分級錯誤、警告、有效
額外資訊可以看到 哪些網址 有問題

它才抓得到「整批頁面同一個錯誤」

單頁測試只驗一頁。模板改壞了會讓幾百頁同時出錯,那只有全站報告看得出來。

而且它會列出具體的網址清單,可以直接拿去修。

一份完整的驗證流程

順序做什麼用什麼
1建置後檢查語法與必填欄位自己寫的腳本(見下方)
2確認型別與屬性合法Schema Markup Validator
3確認符合顯示條件複合式搜尋結果測試
4上線後確認全站Search Console 強化報告

第 1 步最該做卻最常被跳過

前三步都是人工的,一定會有忘記的時候。寫成腳本放進建置流程,才能保證每次都跑。

自動化驗證

建置後掃過產出的 HTML,抽出所有 ld+json 檢查:

js
// scripts/validate-schema.mjs
import { readdir, readFile } from 'node:fs/promises';
import path from 'node:path';

const DIST = 'dist';

// 各型別的必填欄位
const REQUIRED = {
  Article: ['headline'],
  BreadcrumbList: ['itemListElement'],
  FAQPage: ['mainEntity'],
  ItemList: ['itemListElement'],
  Product: ['name'],
  VideoObject: ['name', 'description', 'thumbnailUrl', 'uploadDate'],
};

const files = (await readdir(DIST, { recursive: true })).filter((f) =>
  f.endsWith('.html'),
);

const errors = [];

for (const file of files) {
  const html = await readFile(path.join(DIST, file), 'utf8');
  const blocks = [
    ...html.matchAll(
      /<script type="application\/ld\+json">([\s\S]*?)<\/script>/g,
    ),
  ];

  for (const [, json] of blocks) {
    let data;

    // 1. 語法檢查
    try {
      data = JSON.parse(json);
    } catch (e) {
      errors.push(`${file}: JSON 語法錯誤 — ${e.message}`);
      continue;
    }

    // 2. 必要欄位檢查
    for (const entity of [data].flat()) {
      if (!entity['@context']) errors.push(`${file}: 缺少 @context`);
      if (!entity['@type']) {
        errors.push(`${file}: 缺少 @type`);
        continue;
      }

      for (const field of REQUIRED[entity['@type']] ?? []) {
        if (!entity[field]) {
          errors.push(`${file}: ${entity['@type']} 缺少必填欄位 ${field}`);
        }
      }
    }

    // 3. 角括號原字元檢查(CLAUDE.md 的硬規定)
    if (/[<>]/.test(json.replace(/&[a-z]+;/g, ''))) {
      errors.push(`${file}: ld+json 內含角括號原字元`);
    }
  }
}

if (errors.length > 0) {
  console.error(`發現 ${errors.length} 個結構化資料問題:`);
  for (const e of errors) console.error(`  ✗ ${e}`);
  process.exit(1);
}

console.log(`✓ ${files.length} 個頁面的結構化資料通過檢查`);
json
{
  "scripts": {
    "validate:schema": "node scripts/validate-schema.mjs",
    "postbuild": "npm run validate:schema"
  }
}

這個腳本能抓到的三類問題

問題為什麼重要
JSON 語法錯誤整段標記失效,而且沒有錯誤提示
缺少必填欄位不會取得複合式結果
角括號原字元容易誤觸 <script> 的解析規則

第三項是這個站特有的規範,ld+json 裡不寫 <title> 這類角括號,一律改用純名稱敘述。

更進一步的檢查

除了語法,還可以檢查 一致性

js
// 檢查 Article 的 headline 是否與頁面的 h1 一致
const h1 = html.match(/<h1[^>]*>(.*?)<\/h1>/s)?.[1]?.replace(/<[^>]+>/g, '').trim();
const article = entities.find((e) => e['@type'] === 'Article');

if (article && h1 && !h1.includes(article.headline.slice(0, 20))) {
  errors.push(`${file}: Article 的 headline 與頁面 h1 不一致`);
}
js
// 檢查 FAQPage 的問答數與頁面的收合區塊一致
const details = [...html.matchAll(/<summary>([^<]+)<\/summary>/g)];
const faq = entities.find((e) => e['@type'] === 'FAQPage');

if (faq && details.length !== faq.mainEntity.length) {
  errors.push(
    `${file}: FAQPage 標記 ${faq.mainEntity.length} 組,頁面上有 ${details.length} 組`,
  );
}

一致性檢查抓的是最嚴重的問題

標記與內容不符 是唯一會招致人工處罰的部分。語法錯誤只是失效,不一致才是違規。

這兩個檢查各抓一種常見的不一致:標題改了忘了改標記、問答改了忘了改標記。

驗證要對建置產物做

結構化資料裡的網址常常寫成 token(例如 %SITE%/seo/...),由設定檔在建置時替換成當前環境的網域。

不要拿原始檔去驗證

原始檔裡的網址還是 token,貼進線上工具會被判定為無效網址。驗證一律對建置產物做。

正確的順序:

bash
# 1. 建置(此時網址的 token 已被替換)
npm run build

# 2. 對產出的 HTML 跑自動化檢查
npm run validate:schema

# 3. 把某一頁的 HTML 貼進線上工具做最終確認
cat dist/seo/schema-article.html | clip

檢查清單

項目標準
建置後有自動檢查 JSON 語法建議
檢查必填欄位建議
檢查 ld+json 無角括號原字元建議
通過 Schema Markup Validator建議
通過複合式搜尋結果測試(無錯誤)建議
動態注入的標記已確認渲染後讀得到必備
標記與頁面內容一致(標題、問答數)必備
上線後看過 Search Console 強化報告建議
驗證對象是建置產物而非原始檔必備

常見問題

三個工具該用哪一個?

都要用,它們管不同的事。複合式搜尋結果測試看單頁能不能取得特殊呈現、通用驗證器看純語法與型別是否合法、Search Console 的強化報告看全站範圍的錯誤統計。前兩者即時,後者有延遲但涵蓋整站。

錯誤和警告差在哪?

錯誤表示缺少必填欄位,那個型別完全不會取得複合式結果;警告表示缺少建議欄位,仍可能顯示但資訊較少。錯誤必須修,警告依情況補。

驗證通過為什麼還是沒出現複合式結果?

驗證通過只代表語法與必填欄位正確,不保證會顯示。搜尋引擎仍會依內容品質、網站權重與該次搜尋的情境決定要不要用。此外部分型別已被官方縮減或停止顯示,例如:HowTo

為什麼通用驗證器通過但複合式結果測試失敗?

因為兩者的標準不同。通用驗證器只看 Schema.org 的定義是否合法,複合式結果測試還檢查搜尋引擎額外要求的必填欄位。合法的標記不一定符合特定搜尋引擎的顯示條件。

可以在本機驗證嗎?

官方工具需要公開網址,但可以用貼上原始碼的模式測試本機的內容。另外語法與必填欄位的檢查完全可以寫成腳本在建置後跑,那才是能擋在提交階段的做法。

延伸閱讀

參考資料:Google 搜尋中心:複合式搜尋結果測試