Claude Media
Claude CodeでJSON-LD構造化データを実装し、テストで検証する手順

Claude CodeでJSON-LD構造化データを実装し、テストで検証する手順

記事ページのArticle・BreadcrumbList・Organizationを、Claude CodeでJSON-LDとして実装します。スクリプトで崩れを拾い、リッチリザルトテストで最終確認する流れです。

JSON-LDは、ページの意味を検索エンジンに伝えるための構造化データです。記事ページならArticle、パンくずならBreadcrumbList、サイト運営者ならOrganizationを、<script type="application/ld+json"> に書きます。コードの量は多くありません。難しいのは、書いた内容がページの見た目や実際のデータとずれたまま公開されてしまうことです。

Claude Codeは、テンプレートへの差し込みとテストの繰り返しを任せるのに向いています。一方で、どの型をどのページに付けるか、そして最後にリッチリザルトテストで確かめる作業は人の側に残ります。この記事では、その分担を決めて手を動かす順番を説明します。

付ける型と付ける場所を先に決める

最初に決めるのは「どの型を、どのページのテンプレートに置くか」です。Claude Codeに「構造化データを全部入れて」と頼むと、ページに見えていない情報まで書かれがちです。Googleのガイドラインは、利用者に見えないコンテンツをマークアップしないこと、ページの内容を正しく表すことを求めています。

記事サイトでよく使う3つの型を表にします。

型置く場所必須プロパティ注意点
Article置く場所各記事ページ必須プロパティなし(当てはまるものを入れる)注意点日時はISO 8601。タイムゾーンも付ける
BreadcrumbList置く場所パンくずのある各ページ必須プロパティitemListElement と各項目の position ほか注意点2件以上。最後の項目以外は item が必要
Organization置く場所トップページ必須プロパティなし(関連するものを入れる)注意点logo は112×112px以上

ArticleとOrganizationには必須プロパティがありません。「必須が足りない」というエラーは出ないので、入れ忘れはテストでは見つかりません。後半のスクリプトが必要になる理由もここにあります。

パンくずは、URL構造をなぞるのではなく、利用者が実際にたどる経路を表すのが推奨です。サイトのトップ階層や、ページ自身を項目に含めることは必須ではありません。

CLAUDE.mdに実装の規約を固定する

型と置き場所が決まったら、Claude Codeに毎回伝える内容をCLAUDE.mdに書きます。口頭の指示だけだと、セッションごとに属性の名前や日時の書式が揺れます。次のような断片が、規約の例になります(Googleの記述を、この記事の手順に合わせてまとめた書き方です)。

## 構造化データ(JSON-LD)
 
- 形式はJSON-LDのみ。MicrodataとRDFaは使わない
- 画面に表示していない情報(著者、日付、パンくず)はマークアップしない
- Article: headline / image / datePublished / dateModified / author を必ず出す
- 日時はISO 8601で、タイムゾーンを付ける(例: 2026-10-10T12:00:00+09:00)
- author は配列にして1人ずつ別オブジェクトにする。名前に「posted by」等を混ぜない
- BreadcrumbList は2件以上。最後以外は item(URL)を持たせる
- JSON-LD は script タグへ出す前に "<" を `\u003c` に置換する
- 変更したら scripts/check-jsonld.mjs をローカルのURLに対して実行し、0件になるまで直す

author を1人ずつ別オブジェクトにする点は、Articleのドキュメントに沿っています。複数の著者を1つの name にカンマでつなぐ書き方は、避けるべき例として示されています。著者の名前には「posted by」のような導入語を混ぜないことも、同じ節で求められています。

最後の行が、この記事の核になります。Claude Codeに「テストを通すこと」を明示的な完了条件として渡す書き方です。

記事ページのテンプレートに差し込む

実装の形はフレームワークで変わります。Next.jsのApp Routerなら、layout.js か page.js の中にscriptタグとして描画するのが推奨されています。Nuxtなど別のフレームワークの進め方は、Claude CodeでNuxtアプリを開発する手順の検証ループが参考になります。

Next.jsの例を示します。記事データからArticleとBreadcrumbListを組み立て、1つのページに2つのscriptタグとして出します。

// app/articles/[slug]/page.tsx(抜粋)
const toLd = (data: object) =>
  JSON.stringify(data).replace(/</g, "\\u003c");
 
const article = {
  "@context": "https://schema.org",
  "@type": "Article",
  headline: post.title,
  image: [post.ogImageUrl],
  datePublished: post.publishedAt, // 例: 2026-10-10T12:00:00+09:00
  dateModified: post.updatedAt ?? post.publishedAt,
  author: [{ "@type": "Person", name: post.authorName,
             url: post.authorUrl }],
};
 
const breadcrumb = {
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  itemListElement: [
    { "@type": "ListItem", position: 1, name: "ホーム",
      item: "https://example.com/" },
    { "@type": "ListItem", position: 2, name: post.categoryName,
      item: `https://example.com/${post.categorySlug}` },
    { "@type": "ListItem", position: 3, name: post.title },
  ],
};
 
return (
  <>
    <script type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: toLd(article) }} />
    <script type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: toLd(breadcrumb) }} />
    {/* 本文 */}
  </>
);

replace(/</g, "\\u003c") を挟む理由は、Next.jsのガイドにあります。JSON.stringify は、XSSに使われる文字列を無害化しません。記事タイトルに </script> が入っただけで、ページが壊れる可能性があります。ガイドは < をUnicodeエスケープの \u003c に置換する方法を示しています。

Organizationはトップページに置きます。ドキュメントの例に沿うと、次のような最小構成になります。

{
  "@context": "https://schema.org",
  "@type": "Organization",
  "name": "Example Corporation",
  "url": "https://www.example.com",
  "logo": "https://www.example.com/images/logo.png"
}

sameAs にSNSなどのプロフィールURLを並べることもできます。ただし、実在し、運営者自身のものであるURLだけを入れます。運営者を偽るような書き方は、ガイドラインで禁じられています。

JSON-LDは、JavaScriptで後から注入しても、Googleがレンダリング時のDOMから読めるとされています。とはいえ、サーバー側のHTMLに最初から含めたほうが、確認が単純になります。

Claude Codeに書かせると混ざりやすい内容

テンプレートの形が合っていても、中身のデータで規約違反になることがあります。コード生成では、サンプルのレビューや評価を「それらしく」補うことが起こりえます。次の3点は、レビューのときに必ず見ます。

  • 画面に出ていない値: ガイドラインは、読者に見えないコンテンツのマークアップを禁じています。JSON-LDが演者を記述するなら、本文も同じ演者を説明している必要があります
  • 実在しない評価やレビュー: 偽のレビューなど、無関係で誤解を招く内容のマークアップは禁止です。aggregateRating のような項目を、サンプル値のまま残さないでください
  • 運営者の偽装: 構造化データで人物や組織になりすますこと、所有関係や主な目的を偽ることは禁止です

違反すると、構造化データの問題による手動対策の対象になりえます。手動対策を受けると、そのページはリッチリザルトの表示資格を失いますが、通常のウェブ検索での順位には影響しないとされています。

Articleの image と author は、データ側で埋まっているかも確認します。image はクロールとインデックスが可能なURLで、記事の内容を表す画像を指定します。サイズ違いを複数渡すことが推奨されています。author は、種別(Person か Organization)と、url または sameAs を付けると、Googleが著者を識別しやすくなります。

リッチリザルトテストの前に、スクリプトで崩れを拾う

リッチリザルトテスト(Rich Results Test)は、デプロイ後にURLを入れて使うブラウザ上のツールです。Claude Codeが繰り返し叩くには向きません。そこで、ローカルで開発サーバーを立てた状態で、HTMLから ld+json を取り出して検査するスクリプトを置きます。

このスクリプトは、テストが警告を出さない項目を補う役割を持ちます。Articleの datePublished と dateModified について、ドキュメントは「リッチリザルトテストは警告を表示しない」と書いています。時刻やタイムゾーンの抜けは、テストを通っても残ります。

// scripts/check-jsonld.mjs  node scripts/check-jsonld.mjs <URL|ファイル>
import { readFileSync } from "node:fs";
 
const src = process.argv[2];
const html = /^https?:/.test(src)
  ? await (await fetch(src)).text()
  : readFileSync(src, "utf8");
 
const re = /<script[^>]*type="application\/ld\+json"[^>]*>([\s\S]*?)<\/script>/g;
const blocks = [...html.matchAll(re)].map((m) => m[1]);
let errors = 0;
const fail = (msg) => { console.log("NG  " + msg); errors++; };
 
if (blocks.length === 0) fail("ld+json が見つからない");
const items = [];
for (const [i, raw] of blocks.entries()) {
  try { items.push(JSON.parse(raw)); }
  catch (e) { fail(`block ${i + 1}: JSONとして読めない`); }
}
 
for (const a of items.filter((x) => x["@type"] === "Article")) {
  for (const k of ["headline", "image", "datePublished",
                   "dateModified", "author"]) {
    if (!a[k]) fail(`Article.${k} が空`);
  }
  const iso = /T\d{2}:\d{2}:\d{2}(Z|[+-]\d{2}:\d{2})$/;
  for (const k of ["datePublished", "dateModified"]) {
    if (a[k] && !iso.test(a[k])) fail(`${k} に時刻かタイムゾーンが無い`);
  }
}
for (const b of items.filter((x) => x["@type"] === "BreadcrumbList")) {
  const list = b.itemListElement ?? [];
  if (list.length < 2) fail("ListItemが2件未満");
  list.forEach((li, i) => {
    if (li.position !== i + 1) fail(`position が ${i + 1} ではない`);
    if (!li.name) fail(`ListItem ${i + 1} に name が無い`);
    if (!li.item && i !== list.length - 1) fail(`ListItem ${i + 1} に item が無い`);
  });
}
console.log(`${blocks.length}ブロック / ${errors}件の指摘`);
process.exit(errors ? 1 : 0);

検査対象は、ドキュメントで必須・推奨とされている項目に絞っています。サイトによって増やしたい項目が出たら、同じスクリプトに足します。

検証ループをClaude Codeに回させる

スクリプトがあれば、Claude Codeには「0件になるまで直す」という終了条件を渡せます。対話の中でも、非対話モードでも動かせます。

npm run dev &
claude -p "記事ページのJSON-LDを実装して。完了条件は \
node scripts/check-jsonld.mjs http://localhost:3000/articles/sample \
が0件で終わること。指摘が出たら原因を直して再実行する" \
  --allowedTools "Read,Edit,Bash"

claude -p は非対話モードで、--allowedTools は確認なしに使うツールを指定するフラグです。CIに載せる場合も、同じコマンドを使えます。ただし、承認なしで編集とシェル実行を許す設定なので、検証用のブランチなど、巻き戻せる場所で回してください。

ループが終わったら、Claude Codeには出力を読ませます。「0件」の表示だけでなく、ブロック数 が想定の2(ArticleとBreadcrumbList)と合っているかを見ます。0件でも、ブロックが片方しか出ていなければ、実装は半分です。

リッチリザルトテストで最終確認する

スクリプトが通ったら、公開URLで確認します。

手順

リッチリザルトテストで確認する流れ

  1. 1

    URLを入力する

    デプロイ済みのページURLを入れて「URLをテスト」を押します。コード入力は、JavaScriptの制限(CORSなど)があるため、URL入力が推奨されています。

  2. 2

    結果を読む

    対応する型なら、英語表示で「Page is eligible for rich results」と出ます。エラーと警告が出たら、構文ミスかプロパティの不足が多い、と説明されています。

  3. 3

    対応外の型はHTMLで確認する

    ツールが対応していない型は、レンダリング後のHTMLに構造化データが含まれているかで判断します。含まれていれば、Googleは処理できます。

  4. 4

    URL検査で見え方を確かめる

    数ページに載せた段階で、Search ConsoleのURL検査でGoogleから見えるHTMLを確認します。robots.txtやnoindexでブロックされていないことも前提です。

エラーは直し、警告は余裕があれば直します。重大でない警告は、リッチリザルトの対象になるためには必須ではないとされています。

ここで通ったことは、表示の保証ではありません。ガイドラインには、マークアップが正しくても検索結果に出る保証はないと書かれています。出ない理由には、そのアルゴリズムが別の表示のほうが適切と判断した場合や、構造化データがページの主な内容を表していない場合、テストが拾えない誤りがある場合などが挙がっています。

公開後は、Search Consoleのリッチリザルトのレポートで、有効なページ数の推移を見ます。テンプレートやサーバーの都合で、公開後に壊れることがあるためです。効果を測りたい場合は、構造化データを入れる前に数か月分のデータがあるページを選び、入れた後と比べる方法が紹介されています。Search ConsoleのデータをClaude Codeで集計する手順は、GA4とSearch Consoleを突き合わせる記事が参考になります。

FAQのリッチリザルトは、実装の対象から外す

ネタとして挙がりやすいFAQPageについては、前提が変わりました。Googleの更新履歴には、FAQのリッチリザルトが2026年5月7日以降は検索結果に表示されなくなる、という非推奨の告知があります。その後、FAQ構造化データのドキュメント自体も削除されました。

それ以前にも、FAQの表示は、政府や医療分野の権威あるサイトだけに限られていました。記事ページに追加するJSON-LDの候補から、FAQPageは外して問題ありません。すでに入れているFAQのJSON-LDは、検索結果の見え方を目的にするなら保守する理由がありません。

実装でよく崩れる箇所

症状原因の例直し方
JSONとして読めない原因の例タイトルの引用符や改行が未エスケープ直し方JSON.stringify でオブジェクトから生成する
ブロックが出ない原因の例クライアントだけで描画している直し方サーバー側のHTMLに出す。出力HTMLを curl で確かめる
日付がずれて解釈される原因の例タイムゾーンが無く、Googlebotが使うタイムゾーンで読まれる直し方+09:00 などを付けて出力する
著者が1人にまとまる原因の例複数人の名前を1つの name に連結直し方配列にして1人ずつ別オブジェクトにする
パンくずがエラー原因の例最後以外の項目で item が無い直し方中間の項目にURLを持たせる
画像が評価されない原因の例画像URLがクロールできない直し方robots.txtを確認し、URL検査で見られるか確かめる

どれも、スクリプトが先に拾えるように検査項目を足せます。同じ崩れを2回見たら、スクリプトとCLAUDE.mdの両方に1行ずつ足す運用が現実的です。

まとめ

JSON-LDの実装で効くのは、コードそのものより、検査を自動化しておくことです。型の選択と、公開後の最終確認は人が持ちます。テンプレートへの差し込みと、崩れの修正の繰り返しは、完了条件を渡したClaude Codeが回せます。

最初に作るものは、トップページのOrganization、記事ページのArticle、パンくずのBreadcrumbListの3つで足ります。FAQPageを足す必要はありません。これらを1ページ分実装し、スクリプトとリッチリザルトテストの両方を通してから、テンプレートを全記事へ広げてください。

なお、ランディングページを別のCMSで運用している場合も、考え方は同じです。進め方はClaude CodeでLPをCMSに実装する流れにまとめています。

この記事を共有:XはてブLinkedIn