1087 字
5 分鐘
在 Astro 部落格自動生成 Schema 結構化資料

結構化資料(Structured Data)可以讓搜尋引擎更好地理解你的網頁內容,進而在搜尋結果中顯示豐富的摘要。本文介紹如何在 Astro 部落格實作自動 Schema 生成系統,支援 BlogPosting、FAQPage、HowTo 和 Breadcrumb。

什麼是 Schema 結構化資料?#

Schema.org 定義了一套標準的詞彙,用於描述網頁內容。Google、Bing 等搜尋引擎會讀取這些資料,並在搜尋結果中呈現:

  • BlogPosting:文章標題、發布日期、作者
  • FAQPage:常見問題摺疊卡片
  • HowTo:步驟教學列表
  • Breadcrumb:麵包屑導航路徑

系統架構#

我們的目標是從 Markdown 內容自動提取資訊,生成對應的 Schema:

Markdown 文章
├── Frontmatter ─────────── BlogPosting Schema
│ (title, published, description)
├── ## 常見問題 區塊 ────── FAQPage Schema
│ (Q: ... A: ...)
├── ### 步驟 1/2/3 ──────── HowTo Schema
└── category ────────────── Breadcrumb Schema

實作步驟#

步驟 1:建立 Schema 工具函式#

src/utils/schema-utils.ts 建立各種 Schema 生成函式:

// BlogPosting Schema - 文章基本資訊
export function generateBlogPostingSchema(
entry: CollectionEntry<"posts">,
author: { name: string; url: string },
siteUrl: string,
lang: string
) {
return {
"@context": "https://schema.org",
"@type": "BlogPosting",
headline: entry.data.title,
description: entry.data.description,
datePublished: entry.data.published.toISOString(),
dateModified: entry.data.updated?.toISOString()
|| entry.data.published.toISOString(),
author: {
"@type": "Person",
name: author.name,
url: author.url,
},
mainEntityOfPage: {
"@type": "WebPage",
"@id": `${siteUrl}/posts/${entry.slug}/`,
},
inLanguage: lang,
};
}

步驟 2:FAQ Schema 自動提取#

從 Markdown 中找到「## 常見問題」區塊,提取 Q&A:

function extractFAQFromMarkdown(markdownContent: string): FAQItem[] {
const faqs: FAQItem[] = [];
// 支援多種標題格式
const faqSectionMatch = markdownContent.match(
/##\s*(常見問題|FAQ|常見疑問|Q\s*&\s*A)\s*\n([\s\S]*?)(?=\n##\s|$)/i
);
if (!faqSectionMatch) return faqs;
const faqSection = faqSectionMatch[2];
const qaPairs = faqSection.split(/###\s*Q:/);
for (const pair of qaPairs) {
if (!pair.trim()) continue;
const questionMatch = pair.match(/^([^]*?)(?=\n\s*A:)/);
const answerMatch = pair.match(/A:\s*([^]*?)$/);
if (questionMatch && answerMatch) {
faqs.push({
question: cleanMarkdownForSchema(questionMatch[1].trim()),
answer: cleanMarkdownForSchema(answerMatch[1].trim()),
});
}
}
return faqs;
}
export function generateFAQSchema(markdownContent: string) {
const faqs = extractFAQFromMarkdown(markdownContent);
if (faqs.length === 0) return null;
return {
"@context": "https://schema.org",
"@type": "FAQPage",
mainEntity: faqs.map((faq) => ({
"@type": "Question",
name: faq.question,
acceptedAnswer: {
"@type": "Answer",
text: faq.answer,
},
})),
};
}

步驟 3:HowTo Schema 自動提取#

從 Markdown 找出步驟式標題(步驟 1、步驟 2…):

function extractStepsFromMarkdown(markdownContent: string): HowToStep[] {
const steps: HowToStep[] = [];
// 匹配 ### 步驟 1: 標題 或 ### Step 1: Title
const stepPattern = /###\s*(?:步驟|Step)\s*\d+[::]\s*([^\n]+)\n([\s\S]*?)(?=###\s*(?:步驟|Step)\s*\d+|##\s|$)/gi;
let match;
while ((match = stepPattern.exec(markdownContent)) !== null) {
steps.push({
name: match[1].trim(),
text: cleanMarkdownForSchema(match[2].trim()),
});
}
return steps;
}
export function generateHowToSchema(
markdownContent: string,
title: string,
description: string
) {
const steps = extractStepsFromMarkdown(markdownContent);
if (steps.length < 2) return null;
return {
"@context": "https://schema.org",
"@type": "HowTo",
name: title,
description: description,
step: steps.map((step, index) => ({
"@type": "HowToStep",
position: index + 1,
name: step.name,
text: step.text,
})),
};
}

步驟 4:Breadcrumb Schema#

根據文章的 category 生成麵包屑:

export function generateBreadcrumbSchema(
siteUrl: string,
siteName: string,
category: string,
postTitle: string,
postSlug: string
) {
return {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
itemListElement: [
{
"@type": "ListItem",
position: 1,
name: siteName,
item: siteUrl,
},
{
"@type": "ListItem",
position: 2,
name: category,
item: `${siteUrl}/categories/${encodeURIComponent(category)}/`,
},
{
"@type": "ListItem",
position: 3,
name: postTitle,
item: `${siteUrl}/posts/${postSlug}/`,
},
],
};
}

步驟 5:整合至文章頁面#

src/pages/posts/[...slug].astro 中生成並注入 Schema:

// 生成各種 Schema
const schemas = [];
// BlogPosting(必備)
schemas.push(generateBlogPostingSchema(entry, author, siteUrl, lang));
// FAQ(如果有常見問題區塊)
const faqSchema = generateFAQSchema(entry.body);
if (faqSchema) schemas.push(faqSchema);
// HowTo(如果有步驟教學)
const howToSchema = generateHowToSchema(
entry.body,
entry.data.title,
entry.data.description
);
if (howToSchema) schemas.push(howToSchema);
// Breadcrumb
if (entry.data.category) {
schemas.push(generateBreadcrumbSchema(
siteUrl,
siteName,
entry.data.category,
entry.data.title,
entry.slug
));
}

在 HTML <head> 中注入:

{schemas.map((schema) => (
<script
is:inline
type="application/ld+json"
set:html={JSON.stringify(schema)}
/>
))}

驗證 Schema#

使用 Google 的工具驗證生成的 Schema:

  1. Rich Results Test - 測試單一網址
  2. Schema Markup Validator - 驗證 JSON-LD 語法

常見問題#

Q: 為什麼 FAQ Schema 有時候不會生成?#

A: FAQ Schema 只會在文章包含「## 常見問題」區塊,且有正確格式的 Q&A 時才會生成。格式要求是 ### Q: 問題A: 答案。如果格式不對,extractFAQFromMarkdown 會回傳空陣列。

Q: HowTo Schema 的步驟數量有限制嗎?#

A: Google 建議至少 2 個步驟。我們的實作會在步驟少於 2 個時回傳 null,不生成 HowTo Schema。

Q: 這些 Schema 對 SEO 有多大幫助?#

A: Schema 本身不是排名因素,但可以提升 CTR(點擊率)。FAQ 會在搜尋結果顯示可展開的問答;HowTo 可能顯示步驟預覽。根據不同主題,CTR 提升可達 10-30%。

Q: 如何新增其他類型的 Schema?#

A: 在 schema-utils.ts 新增對應的 generateXxxSchema 函式,然後在文章頁面中呼叫即可。常見的還有 Product、Review、Event 等類型。

總結#

  1. BlogPosting:從 frontmatter 自動生成,每篇文章必備
  2. FAQPage:從「## 常見問題」區塊自動提取
  3. HowTo:從「### 步驟 N」標題自動提取
  4. Breadcrumb:根據 category 自動生成導航路徑
  5. 自動化:只需按照約定格式撰寫 Markdown,Schema 會自動生成

測試環境:macOS Sequoia 15.3, Astro 5.12.8, 2026/02/01

參考來源:

Schema.org

Google Structured Data

Google FAQ Schema

Google HowTo Schema

在 Astro 部落格自動生成 Schema 結構化資料
https://laplusda.com/posts/astro-auto-schema-generation/
作者
Zero
發佈於
2026-02-01
許可協議
CC BY-NC-SA 4.0
這篇文章有幫助嗎?

回報錯字、失效連結,或告訴我你想看的延伸主題。