ぽっぺんしゃんにょろりんこ

匿名・非追跡型アダルト動画検索エンジンの設計ノート

SEOに強い!SPAでもできる動的生成の構造化データ(JSON-LD)の話

こんにちは、にょろりんこの備忘録的技術ブログです。

みなさん、構造化データ(JSON-LD)って使っていますか?

検索エンジン、特にGoogle「このページは何を扱っているか」を正しく伝えるための仕組みで、リッチリザルトにも表示される重要な要素です。

JSON-LD(JavaScript Object Notation for Linked Data)は、その構造化データをHTMLに埋め込むための形式のひとつで、Schema.orgの定義に従っていればGoogleがきちんと読み取ってくれます。

たとえば、動画なら VideoObject求人なら JobPostingイベント告知なら Event といったスキーマが有名ですね。

ティアラと時計

こうした構造化データを使うことで、検索結果にサムネイルや開催日時、給与情報などが表示され、ユーザーにとっても視認性の高い結果が得られます。ユーザーにとって有益ということは、当然SEO的にも有益ですね。

ただ、ひとつ問題があります。

構造化データは、基本的に「1ページに1つ」記述するものであり、その性質上、いわゆる静的なページで使われることがほとんどです。

一方、Sae-PornsのようなSPA(Single Page Application)構成では、ページ遷移がない代わりに、ユーザーの操作に応じて中身だけを都度動的に生成していきます。そのため、こうした構造化データを「いつ・どこに・どうやって挿入するか?」という課題が出てきます。

でも、方法がないわけではありません。

今回は、Sae-PornsのようなSPAサイトで、ユーザーが動画をクリックした瞬間に構造化データ(VideoObject)をJavaScript動的生成し、<head> 内に自動挿入する方法をご紹介します。


構造化データは「いつ」出すべきか?SPAの難しさ

構造化データは、「このページには何が書いてあるか?」をGoogleをはじめとする検索エンジンなどに伝えるための設計図のようなものです。でも、SPAの場合、ページの中身はJavaScriptの実行後に初めて表示されるため、クローラーにとっては最初は中身が空っぽに見えてしまいます。

たとえば、こんなSPA構成を想像してみてください。

  1. ユーザーがページにアクセス
  2. 空っぽのHTMLが読み込まれる
  3. JavaScriptでデータ取得
  4. DOM更新
  5. ようやく画面に動画や情報が表示される

静的なHTMLページであれば、最初から構造化データが<head>内に書いてあるので、クローラーも問題なく読み取れます。ですがSPAでは、構造化データも「あとからJSで書く」必要があるということになります。

このとき、重要になるのが「どのタイミングで構造化データを出すか?」です。

Sae-Pornsでは、トップページに動画の一覧が表示され、ユーザーがサムネイルをクリックすると、その場で詳細が表示される仕組みになっています。ページ遷移はしませんが、「表示が切り替わる=1動画にフォーカスされる瞬間」がある、というわけです。

そこで今回は、動画をクリックして詳細を表示した瞬間に、その動画専用の構造化データ(VideoObject)を動的に生成し、HTMLの<head>内に挿入するというアプローチを採用しました。

次章では、実際にどのようにして<script type="application/ld+json">を動的に生成しているか、コードベースで紹介していきます。

実装解説:動画クリック時に構造化データを出す仕組み

では実際に、どうやってSPA内で構造化データ(JSON-LD)を動的に出しているのか?
Sae-Pornsの実装をもとに、最小構成で解説してみます。インデックス側での処理(index.html)

<!-- 外部スクリプト -->

<script src="/video-detail.js"></script>

まず、構造化データを出す本体は video-detail.js にあるので、それを読み込んでおきます。

サムネイルがクリックされたときの処理

$(document).on('click', '.thumbnail-link', function (e) {
  const vid = $(this).data('id');
  window.app.loadVideo(vid);
});

そして、ユーザーが動画のサムネイルをクリックしたときに、該当動画のIDをもとに loadVideo() を呼び出します。これで、その動画の詳細表示+構造化データの生成処理に進みます。

video-detail.js 側 構造化データ(動画メタデータ)の生成と挿入

window.app.loadVideo = function(videoId) {
  $.ajax({
    url: '/shinphp535.php',
    ...
  }).done(function(data) {
    const v = data[0];

動画IDをもとに、動画メタ情報(タイトルや画像URL、再生時間など)を取得します。

古い構造化データを削除

const old = document.getElementById('jsonld-video-dynamic');
if (old) old.remove();

SPAでは何度も動画を切り替えるため、前回の構造化データが残っていると不都合です。そのため、まずは既存の <script> タグをID指定で削除、クリーニングから新たに追加します。

VideoObject を構築する

const ld = {
  "@context": "https://schema.org",
  "@type": "VideoObject",
  "name": v.title,
  "description": v.description || v.title,
  "thumbnailUrl": window.location.origin + thumbnailPath,
  "uploadDate": uploadDate,
  "duration": `PT${v.duration}S`,
  ...(v.embedflag == 1
    ? { "embedUrl": v.embed_url }
    : { "contentUrl": v.url }),
  "publisher": {
    "@type": "Organization",
    "name": "Sae-Porns",
    "url": "https://sae-porns.org"
  }
};

動画のタイトルや説明、再生時間、サムネイルURLなどをもとに、VideoObject スキーマに従って構造化データを組み立てます。

前回解説したように、embedflag に応じて embedUrl と contentUrl を切り替えている点もポイントです。

<head> に追加する

const script = document.createElement('script');
script.type = 'application/ld+json';
script.id = 'jsonld-video-dynamic';
script.text = JSON.stringify(ld, null, 2);
document.head.appendChild(script);

組み立てた構造化データを <script type="application/ld+json"> として <head> に追加します。これでGoogleクローラーが、ページ内の動画情報を正しく読み取れるようになります。

このように、ページ遷移がないSPA構成でも、状況に応じて構造化データを「その場で生成して埋め込む」ことで、  通常の静的サイトと同じように意味づけを行うことが可能になります。

構造化データを「SPA的に扱う」という考え方

構造化データ(JSON-LD)は、一般的には静的なHTMLの中にあらかじめ書いておくもの。そんなイメージを持っている方も多いかもしれません。

実際、「構造化データ 入門」などで検索すると、静的HTMLに<script type="application/ld+json">を書き込むスタイルの解説が多く見つかります。

でも、サイトの構成がSPA(Single Page Application)のように動的なものであっても、構造化データを扱うことはできます。しかも、「1ページに1つのスキーマを出す」という原則を守ったうえで、それをJavaScriptで動的に生成・挿入するという方法も、ちゃんと現実的に機能します。

SPAでは「表示されたタイミングで構造化データを出す」

静的サイトでは、あらかじめ構造化データを書いておくことができますが、SPAではユーザーの操作に応じてページの内容が切り替わっていくため、構造化データもその内容に合わせて必要なタイミングで出すという設計になります。

たとえばSae-Pornsでは、トップページには動画の一覧が表示されますが、この時点では構造化データは出しません。ユーザーがある動画をクリックして詳細を表示したときだけ、その動画に対応する VideoObject スキーマを構築し、<head> に挿入しています。

「今見えているもの」に意味を与える

SPAのような動的なサイトでも、ユーザーが見ているその瞬間の表示状態を「1ページ」と考えれば、そこに構造化データを対応させていくことは十分可能です。

静的に書かれているか動的に描かれているかに関わらず、「1ページ1スキーマの考え方に沿って構造化データを運用していくことが、結果的にGoogleなどの検索エンジンにも正しく伝わりやすい構成になると感じています。

SaePornsはユーザーにとって見やすいサイトであるだけでなく、検索エンジンにとっても見やすいサイト、そんなサイトを目指しています。

それではみなさん、よき開発ライフを。

本記事のコードを実装している、あなたを追跡しないアダルト動画の検索エンジン「SaePorns」はこちら。SPA×構造化データの仕組みがどう動いているか、実際に試してみてください。
※18歳未満の方のご利用はできません。

sae-porns.org