【JSON-LD実装】構造化データ対応と注意点を具体的に説明する

## はじめに
本記事では以下について説明します。

- 構造化データとは
- 構造化データ対応による効果
- 対応の際の注意点
- JSON-LD形式による実装（Google推奨）

「構造化マークアップとは何か」については、こちらの記事（[10分でわかる構造化マークアップ](https://qiita.com/yuzuru_xa/items/2ac253d64e2a25cd3e3f)）が大変わかりやすかったので是非ご一読ください😊
実装に関しては**[構造化マークアップ支援ツール](https://www.google.com/webmasters/markup-helper/u/0/)を利用した方法**と、**JSON-LDによる実装**が紹介されています。

本記事では**JSON-LDによる実装をより具体的に**、さらに**実装の際の注意点**を紹介します。
筆者が失敗から学んだことを載せておりますので、皆さんは時間を有効に使えるよう参考にしていただければと思います。

## 構造化データとは
Webページに関する情報を分類して標準化したデータ形式です。
これを検索エンジン（Google）に提供することでコンテンツ内容をより正確に認識してもらえるようになります。
[10分でわかる構造化マークアップ](https://qiita.com/yuzuru_xa/items/2ac253d64e2a25cd3e3f)で丁寧に説明されています。

## 構造化データ対応による効果
クローラーにコンテンツ内容をより正しく理解してもらうことが目的になりますが、構造化データを元にして検索結果をリッチスニペット化してくれます（※1）

Googleで検索した際、以下のような表示をご覧になったことがあると思います。
<img width="613" alt="スクリーンショット 2019-11-26 19.46.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269379/3b2aa9cc-161b-adef-1ea1-1d7623ebc81a.png">

こちらがリッチスニペットと呼ばれるもので、ユーザーに対して様々な情報（レビューや価格帯など）がわかりやすく表示されるようになります。
**※1 検索結果に必ずしもリッチリザルトが保証されるわけではありません**

## 対応の際の注意点
実装自体はとても簡単なマークアップですが、リリース前のテストで注意すべき点があります。
構造化データタイプには `必須プロパティ` と `推奨プロパティ` があり、それぞれ問題があるとエラー・警告が発生します。
エラーや警告がなかった場合のメリットについて、Google Developersでは以下のように記載されております。

#### Google Developersによる構造化データタイプの定義
> コンテンツがリッチリザルトとして表示されるようにするには、必須プロパティが必要です。
> また、推奨プロパティを使用すると、コンテンツに関する詳細情報を追加できるので、ユーザー エクスペリエンスの向上につながります。

#### SEOへの影響は？
エラーを残したままリリースすると、GoogleSearchConsoleでエラーが発生していると表示されます。
いくつかの記事では「構造化データのエラーはSEO的には問題ない」と書かれていましたが、そのあたりの真偽は調査中です。

筆者は
**「警告（推奨プロパティ対応）に関しては許容するが、エラー（必須プロパティ対応）だけは解消しておこう」**
というスタンスで取り組みました。

### 構造化データテストツール
[構造化データのテストツール](https://search.google.com/structured-data/testing-tool/u/0/?hl=JA)という便利なものがあり、
こちらで構造化データの設定に不備がないかを簡単にチェックすることができます。

**正しく設定できた例**
![スクリーンショット 2019-11-28 0.51.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269379/5bfe9339-9c14-019f-355a-96b7cd7a5ec5.png)

**エラーが発生している例**
![スクリーンショット 2019-11-26 19.57.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269379/b8ba978d-5793-07e8-5d22-28291c422d7a.png)

[構造化データのテストツール](https://search.google.com/structured-data/testing-tool/u/0/?hl=JA)を使うと簡単にエラー内容を理解できますね。

リリース後、もしもデータ構造化対応したページに思わぬデータ不備が発覚した際は
GoogleSearchConsoleで「必須プロパティがないですよ😠」と怒られてしまいます。

例えば上記画像では `RatingValue` は必須であるというエラーが出ています。
Restaurant（※2）においてレビュー評価が必須と設定されているようです。
**※2 Restaurantというボキャブラリ（後述）はないので注意**

もしもレビュー評価がまだ0件のレストランに対して構造化データ対応したい時、どのようにすればよいのでしょうか？
これに関してはProduct(製品)の具体例の箇所で対応方法を記載しております。

以降ではJSON-LD形式の構造化マークアップ具体例、筆者の躓いた点を紹介しております。
ドキュメントを探ればわかる話ですが、本記事を参考に少しでも楽をしていただければと思います☺️

## JSON-LD形式による実装
構造化データは以下のように分類されています。
以下の中から適切な項目（ボキャブラリ）を選択し、構造化データ対応することで検索エンジン側が情報を正しく認識してくれます。

- [Article (記事)](https://developers.google.com/search/docs/data-types/article)
- [Book (書籍)](https://developers.google.com/search/docs/data-types/book)
- [Breadcrumb (パンくず)](https://developers.google.com/search/docs/data-types/breadcrumb)
- [Carousel (カルーセル)](https://developers.google.com/search/docs/data-types/carousel)
- [Course (コース)](https://developers.google.com/search/docs/data-types/course)
- [Critic review (評論家レビュー)](https://developers.google.com/search/docs/data-types/critic-review)
- [Dataset (データセット)](https://developers.google.com/search/docs/data-types/dataset)
- [Employer Aggregate Rating (雇用主の総合評価)](https://developers.google.com/search/docs/data-types/employer-rating)
- [Event (イベント)](https://developers.google.com/search/docs/data-types/event)
- [Fact Check (事実確認)](https://developers.google.com/search/docs/data-types/factcheck)
- [FAQ (よくある質問)](https://developers.google.com/search/docs/data-types/faqpage)
- [How-to (使い方)](https://developers.google.com/search/docs/data-types/how-to)
- [Job Posting (求人)](https://developers.google.com/search/docs/data-types/job-posting)
- [Job Training (職業訓練)](https://developers.google.com/search/docs/data-types/job-training)　←β版
- [Local Business Listing (ローカルビジネスリスティング)](https://developers.google.com/search/docs/data-types/local-business)　←ここにRestaurantが含まれる、ややこしポイント
- [Logo (ロゴ)](https://developers.google.com/search/docs/data-types/logo)
- [Movie (映画)](https://developers.google.com/search/docs/data-types/movie)
- [Occupation (職業)](https://developers.google.com/search/docs/data-types/occupation)
- [Product (製品)](https://developers.google.com/search/docs/data-types/product)
- [Q&A](https://developers.google.com/search/docs/data-types/qapage)
- [Recipe (レシピ)](https://developers.google.com/search/docs/data-types/recipe)
- [Review snippet (レビュースニペット)](https://developers.google.com/search/docs/data-types/review-snippet)
- [Sitelinks searchbox (サイトリンク検索ボックス)](https://developers.google.com/search/docs/data-types/sitelinks-searchbox)
- [Software App (ソフトウェアアプリ)](https://developers.google.com/search/docs/data-types/software-app)
- [Speakable (話せる)](https://developers.google.com/search/docs/data-types/speakable)　←β版　※3
- [Subscription and paywalled content (サブスクリプション及び有料コンテンツ)](https://developers.google.com/search/docs/data-types/paywalled-content)
- [Video (ビデオ)](https://developers.google.com/search/docs/data-types/video)

※3　TTS（TextToSpeach）による音声の再生に適している記事やWebページ内のプロパティが識別するセクション

## 具体例
上記リンクからドキュメントを読めばわかることですが、あらかじめ知っておくと楽だと思うので記載しておきます。

### 企業ロゴ（Logo）
ドキュメントは[こちら](https://developers.google.com/search/docs/data-types/logo)。
企業のロゴ画像を登録できます。以下は「Twitter」とGoogleで検索したときの例です。
<img width="490" alt="スクリーンショット 2019-11-26 11.51.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269379/c1a788e2-1e33-ad74-58a8-ea66f9850536.png">

具体的には以下の通りに設定します。

```
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Organization",
  "url": "http://www.example.com",
  "logo": "http://www.example.com/images/logo.png"
}
</script>
```

#### 注意点

- イメージサイズが指定されている（112x112px以上）
- 画像URLがクロール、インデックス可能であること
- 画像の形式が `.jpg` `.png,` `.gif ` のいずれかであること（`.svg`はNG）

### 製品（Product）
ドキュメントは[こちら](https://developers.google.com/search/docs/data-types/product)。
製品ボキャブラリには `AggregateOffer`, `Offer`, `Product` の3種があります。

Productの場合

```
<script type="application/ld+json">
{
  "@context": "https://schema.org/",
  "@type": "Product",
  "name": "Executive Anvil",
  "image": [
    "https://example.com/photos/1x1/photo.jpg",
    "https://example.com/photos/4x3/photo.jpg",
    "https://example.com/photos/16x9/photo.jpg"
   ],
  "description": "Sleeker than ACME's Classic Anvil, the Executive Anvil is perfect for the business traveler looking for something to drop from a height.",
  "sku": "0446310786",
  "mpn": "925872",
  "brand": {
    "@type": "Thing",
    "name": "ACME"
  },
  "review": {
    "@type": "Review",
    "reviewRating": {
      "@type": "Rating",
      "ratingValue": "4",
      "bestRating": "5"
    },
    "author": {
      "@type": "Person",
      "name": "Fred Benson"
    }
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.4",
    "reviewCount": "89"
  },
  "offers": {
    "@type": "Offer",
    "url": "https://example.com/anvil",
    "priceCurrency": "USD",
    "price": "119.99",
    "priceValidUntil": "2020-11-05",
    "itemCondition": "https://schema.org/UsedCondition",
    "availability": "https://schema.org/InStock",
    "seller": {
      "@type": "Organization",
      "name": "Executive Objects"
    }
  }
}
</script>
```

長いですね。必須項目、推奨項目（なくても警告が出るだけ）を全て含めると上記の通りになります。
ドキュメントのサンプルから取ってきたものです。

#### 注意点
**以下は推奨プロパティだが、3点のうちいずれかを含むことが必須**となっている。

- aggregateRating（レビュー平均値、総数）
- Review（個人のレビュー）
- offers（金額、通貨国）

ちなみにドキュメントでは必須プロパティは`image(画像)`, `name(商品)` の2点となっている。
![スクリーンショット 2019-11-28 1.27.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269379/6579750d-54b2-477b-8536-2e5076522a0b.png)

#### さらに注意すべき点
`Review` データが複数ある場合は `aggregateRating` が必須となります。
`aggregateRating` はレビュー平均値、レビュー総数のプロパティを持っており、どちらも必須項目です。
データ不備によってレビュー総数だけ取得できていない場合等はエラーが発生するので注意してください。
※ちょっと何言ってるのか...となりましたら上記の **Productの場合** のコードをご覧ください。

もしレビューのない製品を構造化したいのであれば、offersの価格データのみ渡すよう制御すればOKです。
offersの持つプロパティで必須とされているのは「Price（金額）」です。
`補足: Price（固定の金額）ではなく金額範囲で指定したい場合、 lowPrice , highPrice による代替が可能`


このように、**各項目ごとに必須とされているデータがあることをチェックする必要があります。**
ややこしいことに、ボキャブラリ（ここではProduct）ごとの必須プロパティ、その必須プロパティの中にも必須プロパティがあるため、エラーが出ないように設定するには注意が必要です。

[構造化データのテストツール](https://search.google.com/structured-data/testing-tool/u/0/?hl=JA)にとりあえずボキャブラリだけ設定してテストしてみると、足りてない項目を親切に指摘してくれるので、指摘通りに対応すればOKです。
必須項目のデータ不備があっても問題ないよう、フロント側でチェックしておけば安全ですね。

筆者はこの辺（必須プロパティの中の必須プロパティ）をきっちり確認できておらず、リリース後に修正が必要になりました😉


### ローカルビジネスリスティング
ドキュメントは[こちら](https://developers.google.com/search/docs/data-types/local-business)。
LocalBusinessListing に関してはプロパティの注意点ではなく、ボキャブラリそのものに関する注意点を記載します。

#### 注意点
- ＠typeの設定

![スクリーンショット 2019-11-28 1.40.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269379/1a801943-41a1-547b-9d72-1a2779c62bd9.png)

LocalBusinessListingは、Product（製品）以上に選択肢があります。
ドキュメントでも書かれている通り、より具体的な `@type` を指定する必要があります。

以下、[ドキュメント](https://developers.google.com/search/docs/data-types/local-business)より
> LocalBusiness の定義の全文は schema.org/LocalBusiness で確認できます。各ビジネス拠点を LocalBusiness タイプとして定義します。できる限り具体的な LocalBusiness サブタイプ（Restaurant、DaySpa、HealthClub など）を使用してください。

具体的なLocalBusinessサブタイプがどこに記載されているかというと、
[https://schema.org/LocalBusiness](https://schema.org/LocalBusiness)の下の方にある `More Specific Types` にあります。

![スクリーンショット 2019-11-28 1.52.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269379/6b6ae51a-afba-91af-97d7-fb0905f97705.png)

上記のリンク（本記事ではスクショ貼ってます）を押下した先でも更に `More Specific Types` があるので、より具体的なサブクラスを指定してください。どれも当てはまらなかった場合、 `LocalBusiness` で代替可能です。

```
"@type": "LocalBusiness",
```

## まとめ
実装自体は簡単な構造化データ対応ですが、
より適切な設定をするにはきちんとドキュメントを読み込む必要があります。

ここで紹介したのはほんの一例ですが、参考になると幸いです☺️