アニメ海外の反応サイトの各話ページSEO設計(TVSeries/TVEpisode/FAQで構造化する)

自作の「アニメ海外の反応まとめ」(anime.autoarticles.net)は、実在の人気アニメを対象に、各話への海外ファンの盛り上がりをまとめた静的サイトだ。生成器 build.js外部依存ゼロ(Node標準モジュールだけ)で、data/anime/*.json を読んで public/ にHTMLを吐く。この記事では、各話ページを検索エンジンに正しく理解させるための構造化データ設計を、手元で試せる形で説明する。

どのスキーマを、どのページに付けるか

作品ページ・各話ページで使い分けているJSON-LDは次の通り。

  • 作品ページ: TVSeriesproductionCompany にスタジオ、分かる場合は director)。
  • 各話: TVEpisode(親の TVSeries に紐づける)。
  • 各話の一覧: ItemList
  • パンくず: BreadcrumbList(「ホーム > シーズン > 作品」)。
  • Q&A: FAQPage(本文のFAQと内容を一致させる)。

ポイントは「構造化データと画面表示の内容を必ず一致させる」ことだ。FAQPage の質問・回答は、実際にページに表示しているQ&Aと同じ文字列を使う。表示にない情報を構造化データにだけ入れるのは、Googleのガイドライン違反になり得る。

データを事実に寄せる

作品のタイトル・スタジオ・原作・放送情報などの metadata は、実在の一次情報(公式サイト・配信サイト等)で確認したものだけを入れる。一方、各話の「海外の反応」は匿名ハンドルのファン目線の感想として作成し、実在人物の発言の捏造はしない。各反応には hype/funny/moved/love/shock/angry/scared/think の感情ラベルを付け、build.js が色分けする。この「事実は検証、感想は明示的に一般化」という線引きが、サイトの信頼性を保っている。

実際に試す

依存ゼロで、各話ページに TVSeries+TVEpisode+BreadcrumbList のJSON-LDを埋め込む最小SSGをNodeで再現する。build.js として保存し node build.js を実行する(Node 18以上)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// build.js — 各話ページに構造化データを埋め込む最小SSG(依存ゼロ)
const fs = require("fs");

const anime = {
title: "サンプルアニメ",
studio: "サンプルスタジオ",
slug: "sample-anime",
episodes: [
{ n: 1, title: "第1話 出会い", faq: [{ q: "何話まで放送?", a: "現在1話が公開中です。" }] },
],
};
const BASE = "https://anime.autoarticles.net";

const ld = (obj) =>
`<script type="application/ld+json">${JSON.stringify(obj)}</script>`;

function episodePage(a, ep) {
const url = `${BASE}/anime/${a.slug}/${ep.n}/`;
const jsonld = [
{
"@context": "https://schema.org",
"@type": "TVEpisode",
episodeNumber: ep.n,
name: ep.title,
partOfSeries: { "@type": "TVSeries", name: a.title, productionCompany: { "@type": "Organization", name: a.studio } },
url,
},
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
itemListElement: [
{ "@type": "ListItem", position: 1, name: "ホーム", item: BASE + "/" },
{ "@type": "ListItem", position: 2, name: a.title, item: `${BASE}/anime/${a.slug}/` },
{ "@type": "ListItem", position: 3, name: ep.title, item: url },
],
},
{
"@context": "https://schema.org",
"@type": "FAQPage",
mainEntity: ep.faq.map((f) => ({
"@type": "Question",
name: f.q,
acceptedAnswer: { "@type": "Answer", text: f.a },
})),
},
];
// 画面に出すFAQと構造化データのFAQは同じ文字列を使う
const faqHtml = ep.faq.map((f) => `<dt>${f.q}</dt><dd>${f.a}</dd>`).join("");
return `<!DOCTYPE html><html lang="ja"><head><meta charset="utf-8">
<title>${a.title} ${ep.title} 海外の反応</title>
<link rel="canonical" href="${url}">
${jsonld.map(ld).join("\n")}
</head><body><h1>${a.title} ${ep.title}</h1><dl>${faqHtml}</dl></body></html>`;
}

fs.mkdirSync("public", { recursive: true });
for (const ep of anime.episodes) {
fs.writeFileSync(`public/ep-${ep.n}.html`, episodePage(anime, ep));
}
console.log("done");

期待される出力と確認:

  • public/ep-1.html が生成される。
  • grep '"@type":"TVEpisode"' public/ep-1.html でエピソードのスキーマが埋まっていることを確認できる。
  • 生成HTMLをGoogleのリッチリザルトテストに貼ると、TVEpisode・BreadcrumbList・FAQのJSON-LDがエラーなしで認識される。
  • FAQの質問文がHTMLの <dt> と JSON-LD で一致していることを確認する(不一致は避ける)。

各話ページの実物は アニメ海外の反応まとめ で確認できる。

まとめ

  • 各話は TVEpisode を親 TVSeries に紐づけ、BreadcrumbListFAQPage を添える。
  • 構造化データと画面表示は必ず一致させる。
  • metadata は一次情報で検証し、感想は一般化して明示する。

アニメ海外の反応サイトの各話ページSEO設計(TVSeries/TVEpisode/FAQで構造化する)
https://blog.hashito.biz/2026/07/12/anime-reactions-episode-seo-structured-data/
著者
hashito
作成日
2026年7月12日
著作権