ホームページの上部に表示することが望まれる「パンくずリスト」。どのように辿ってきたページなのかを示すというメリットもあることから、グリム童話「ヘンゼルとグレーテル」で、兄妹が道に迷わないようにと森の中で落としていったパンくずから名称がつけられています。英語では「breadcrumbs list」「breadcrumbs」と呼ばれています。

具体的にはどういったものなのか、どうして設置するのか、SEOとしての効果と、効果を最大限に発揮する設置方法などをご紹介していきます。

パンくずリストとは?SEO効果と設置方法

パンくずリストとは?

今ご覧いただいているこのページにも上部に「パンくずリスト」が設置されています。

ホーム > Blog > SEO > パンくずリストとは?SEO効果と設置方法

となってますよね?

これはホームページの階層構造に近いものを示しています。ホームページの階層構造と言うと、専門的にはパソコンのフォルダの階層構造とイコールなので、「それに近い」ということになります。

一番右には現在表示されているページのタイトルが表示されていて、その左の「SEO」は、この記事のカテゴリーが表示されています。その左に表示されている「Blog」をクリックすると、Blogのすべてのカテゴリーが表示され、「SEO」をクリックすると「SEO」カテゴリーのブログのすべてが表示されるようになっています。もちろん、「ホーム」をクリックすればトップページが表示されます。

パンくずリストを設置する理由、メリット

では、どうしてパンくずリストが一般的に設置されているのでしょうか?その理由、メリットをご説明します。

ユーザビリティの向上

サイト閲覧者に対して、現在見ているページのサイト全体の中の位置づけが伝わりやすくすると同時に、同じカテゴリーの他のページを見たいと思ったときにそこにたどり着きやすくするという意味合いがまずあります。

SEO対策になる

パンくずリストを効果的に設置すれば、SEO対策になります。つまり、サイトのアクセス数、PV数が向上する方向に動きやすくなるということです。

SEO対策になる理由

では、どうしてSEO対策になるのでしょうか?その理由は、直接的なものではなく、間接的なものです。詳しくご説明します。

正式に定義されたものである

パンくずリストは、Schema.orgというGoogle、Microsoft、Yahooなどが設立した団体によって、定義されたものとなっています。つまり検索エンジンを持つGoogleやMicrosoftは、パンくずリストという形態を正式に認定しているということになるわけです。

Googleがサイトの構造を把握しやすくなる

構造化マークアップという記述をコーディングの中で施すことで、Googleがパンくずリストを正しく認識し、サイトの構造を把握しやすくなります。それにより、クロールされやすくなるという効果が見込めます。構造化マークアップの全体像はこちらでご説明していますが、パンくずリストの構造化マークアップに特化した部分を後述します。

適切にサイト内リンクが張り巡らされる

本サイトを例にすると、SEOに関連する記事はすべてSEOのカテゴリーページにリンクが張られます。有益なSEOの記事がたくさん書かれているという前提ですが、Googleはこの場合にSEOのカテゴリーページを高く評価する傾向があります。

当サイトは2022年9月4日現在、「SEO対策 東京」で9位です。Googleにインデックスされてからわずか5カ月です、すごいですね!ちなみに「SEO対策 武蔵野市」では2~3か月ですでに1位でした。

というのはさておき、次の画像は、検索結果画面における表示です。カテゴリーページが評価されているのが分かります。一時はカテゴリーページだけが表示されてしまっていたので、そうならないような対処をしてこの状態です。

SEO対策も踏まえた適切な設置方法

このような結果を生むには、適切に設置しなければなりません。その方法まで教えちゃいます。

表示される名称を検索ワードを意識したものにする

上述のカテゴリーページは、「SEO対策 東京」という検索ワードに対して上位に表示されているのであって、それはもちろん、「SEO」というワードを使っているからです。

なので、パンくずに表示される文言は、検索ワードを意識して決めることが望ましいと言えます。

構造化マークアップを行う

schema.orgが定義する構造化マークアップというコーディングの記法があり、それを実施します。これは高度で、パンくずリストが表示されていても、そこまで実施されてないサイトは多くあります。WordPressの場合に、All in One SEOというプラグインが多く使われていて、自動で構造化マークアップされるのですが、適切にされてません。

なので私は、お客様のサイトでAll in One SEOを使うとしても、パンくずリストの構造化マークアップ機能はオフにして、自分でコーディングしています。

構造化マークアップは、JSON-LTDという記法と、Microdataという記法の大きく2種類あり、どちらでもGoogleは認識してくれますが、GoogleはJSON-LTDの記法を推奨しているため、そちらで記述しておくことが望まれます。

上部に表示する

Googleは、上部にある情報をよりそのページにおいて重要性が高いと判断する傾向があります。実際のところ、サイト閲覧者にとっても、最下部に配置されていても気づきにくいでしょうから、その意味でも、上部に表示しないとあまり意味がないかもしれません。

スマートフォンでも上部に表示する

スマートフォンでは非表示にするか、下部に移動させるといったことをしているサイトを見かけますが、Googleはスマートフォンファーストでの評価を宣言していますし、スマートフォンでも上部に表示させないと効果が薄れる可能性があります。

WordPressでパンくずリストを設置する

コーディングまではお分かりにならない多くの方が用いているWordPressなので、WordPressでの設置方法をご案内します。

パンくずリストが設置されるテーマを利用する

テーマによって、パンくずリストが標準で付いているものもあると思いますので、テーマを利用する場合は、そういうものを利用しましょう。私自身はテーマを使ってサイトを作った経験がないので、あまり詳しくないですが、例えばSWELLはついています。デモサイトで見ると、表示されているページのタイトルが非表示になっているのが気になりますが・・。無料のCocoonもついてますが、少なくともデフォルトでは下部に配置されました。

プラグインを使う

WordPressには、「Breadcrumb NVXT」というプラグインがありますので、それを使う手もあります。ただこのプラグインは、私が確認した際は検索結果画面で日本語に対応してなかったので、その点はネックとしてあるかもしれません。

自分で実装する

私はこのページが参考になりました。以下のコードはほぼそのままなんですが、元の記述だとカスタム投稿タイプ名のところが構造化マークアップされないので、そこも構造化マークアップされるように記述を足したものになります。

<?php
// トップページでは何も出力しないように
  if ( is_front_page() ) return false;
  //そのページのWPオブジェクトを取得
  $wp_obj = get_queried_object();
  //JSON-LD用のデータを保持する変数
  $json_array = array();
  echo '<div class="breadcrumbs">'.  //任意
    '<ul>'.
      '<li>'.
        '<a href="'. esc_url( home_url() ) .'"><span>ホーム</span></a>'.
      '</li>';
  if ( is_attachment() ) {
    /* 添付ファイルページ ( $wp_obj : WP_Post )
        添付ファイルページでは is_single() も true になるので先に分岐*/
    $post_title = apply_filters( 'the_title', $wp_obj->post_title );
    echo '<li><span>'. esc_html( $post_title ) .'</span></li>';
  } elseif ( is_single() ) {
    // 投稿ページ ( $wp_obj : WP_Post )
    $post_id    = $wp_obj->ID;
    $post_type  = $wp_obj->post_type;
    $post_title = apply_filters( 'the_title', $wp_obj->post_title );
    // カスタム投稿タイプかどうか
    if ( $post_type !== 'post' ) {
      $the_tax = "";  //そのサイトに合わせて投稿タイプごとに分岐させて明示的に指定してもよい
      // 投稿タイプに紐づいたタクソノミーを取得 (投稿フォーマットは除く)
      $tax_array = get_object_taxonomies( $post_type, 'names');
      foreach ($tax_array as $tax_name) {
          if ( $tax_name !== 'post_format' ) {
              $the_tax = $tax_name;
              break;
          }
      }
      $post_type_link = esc_url( get_post_type_archive_link( $post_type ) );
      $post_type_label = esc_html( get_post_type_object( $post_type )->label );
      //カスタム投稿タイプ名の表示
      echo '<li>'.
            '<a href="'. $post_type_link .'">'.
              '<span>'. $post_type_label .'</span>'.
            '</a>'.
          '</li>';
      //JSON-LDデータ
      $json_array[] = array(
        'id' => $post_type_link,
        'name' => $post_type_label
      );
      } else {//通常の投稿の場合、カテゴリーを表示
        $the_tax = 'category';  
      }
      // 投稿に紐づくタームを全て取得
      $terms = get_the_terms( $post_id, $the_tax );
      // タクソノミーが紐づいていれば表示
      if ( $terms !== false ) {
        $child_terms  = array();  // 子を持たないタームだけを集める配列
        $parents_list = array();  // 子を持つタームだけを集める配列
        //全タームの親IDを取得
        foreach ( $terms as $term ) {
          if ( $term->parent !== 0 ) {
            $parents_list[] = $term->parent;
          }
        }
        //親リストに含まれないタームのみ取得
        foreach ( $terms as $term ) {
          if ( ! in_array( $term->term_id, $parents_list ) ) {
            $child_terms[] = $term;
          }
        }
        // 最下層のターム配列から一つだけ取得
        $term = $child_terms[0];
        if ( $term->parent !== 0 ) {
          // 親タームのIDリストを取得
          $parent_array = array_reverse( get_ancestors( $term->term_id, $the_tax ) );
          foreach ( $parent_array as $parent_id ) {
            $parent_term = get_term( $parent_id, $the_tax );
            $parent_link = esc_url( get_term_link( $parent_id, $the_tax ) );
            $parent_name = esc_html( $parent_term->name );
            echo '<li>'.
                  '<a href="'. $parent_link .'">'.
                    '<span>'. $parent_name .'</span>'.
                  '</a>'.
                '</li>';
            //JSON-LDデータ
            $json_array[] = array(
              'id' => $parent_link,
              'name' => $parent_name
            );
          }
        }
        $term_link = esc_url( get_term_link( $term->term_id, $the_tax ) );
        $term_name = esc_html( $term->name );
        // 最下層のタームを表示
        echo '<li>'.
              '<a href="'. $term_link .'">'.
                '<span>'. $term_name .'</span>'.
              '</a>'.
            '</li>';
        //JSON-LDデータ
        $json_array[] = array(
          'id' => $term_link,
          'name' => $term_name
        );
      }
      // 投稿自身の表示
      echo '<li><span>'. esc_html( strip_tags( $post_title ) ) .'</span></li>';
  } elseif ( is_page() || is_home() ) {
    // 固定ページ ( $wp_obj : WP_Post )
    $page_id    = $wp_obj->ID;
    $page_title = apply_filters( 'the_title', $wp_obj->post_title );
    // 親ページがあれば順番に表示
    if ( $wp_obj->post_parent !== 0 ) {
      $parent_array = array_reverse( get_post_ancestors( $page_id ) );
      foreach( $parent_array as $parent_id ) {
        $parent_link = esc_url( get_permalink( $parent_id ) );
        $parent_name = esc_html( get_the_title( $parent_id ) );
        echo '<li>'.
              '<a href="'. $parent_link .'">'.
                '<span>'. $parent_name .'</span>'.
              '</a>'.
            '</li>';
        //JSON-LDデータ
        $json_array[] = array(
          'id' => $parent_link,
          'name' => $parent_name
        );
      }
    }
    // 投稿自身の表示
    echo '<li><span>'. esc_html( strip_tags( $page_title ) ) .'</span></li>';
  } elseif ( is_post_type_archive() ) {
    // 投稿タイプアーカイブページ ( $wp_obj : WP_Post_Type )
    echo '<li><span>'. esc_html( $wp_obj->label ) .'</span></li>';
  } elseif ( is_date() ) {
    // 日付アーカイブ ( $wp_obj : null )
    $year  = get_query_var('year');
    $month = get_query_var('monthnum');
    $day   = get_query_var('day');
    if ( $day !== 0 ) {
      //日別アーカイブ
      echo '<li>'.
            '<a href="'. esc_url( get_year_link( $year ) ) .'"><span>'. esc_html( $year ) .'年</span></a>'.
          '</li>'.
          '<li>'.
            '<a href="'. esc_url( get_month_link( $year, $month ) ) . '"><span>'. esc_html( $month ) .'月</span></a>'.
          '</li>'.
          '<li>'.
            '<span>'. esc_html( $day ) .'日</span>'.
          '</li>';
    } elseif ( $month !== 0 ) {
      //月別アーカイブ
      echo '<li>'.
            '<a href="'. esc_url( get_year_link( $year ) ) .'"><span>'. esc_html( $year ) .'年</span></a>'.
          '</li>'.
          '<li>'.
            '<span>'. esc_html( $month ) .'月</span>'.
          '</li>';
    } else {
      //年別アーカイブ
      echo '<li><span>'. esc_html( $year ) .'年</span></li>';
    }
  } elseif ( is_author() ) {
    // 投稿者アーカイブ ( $wp_obj : WP_User )
    echo '<li><span>'. esc_html( $wp_obj->display_name ) .' の執筆記事</span></li>';
  } elseif ( is_archive() ) {
    // タームアーカイブ ( $wp_obj : WP_Term )
    $term_id   = $wp_obj->term_id;
    $term_name = $wp_obj->name;
    $tax_name  = $wp_obj->taxonomy;
    $term_link = esc_url( get_term_link( $term_id, $tax_name ) );
    $taxonomy = get_query_var('taxonomy');
    $post_type = get_taxonomy($taxonomy)->object_type[0];
    $post_type_link = esc_url( get_post_type_archive_link( $post_type ) );
    $post_type_label = esc_html( get_post_type_object( $post_type )->label );
    /* タクソノミーに紐づくカスタム投稿タイプを出力 */
    echo '<li>'.
    '<a href="'. $post_type_link .'">'.
      '<span>'. $post_type_label .'</span>'.
    '</a>'.
  '</li>';
    // 親ページがあれば順番に表示
    if ( $wp_obj->parent !== 0 ) {
      $parent_array = array_reverse( get_ancestors( $term_id, $tax_name ) );
      foreach( $parent_array as $parent_id ) {
        $parent_term = get_term( $parent_id, $tax_name );
        $parent_link = esc_url( get_term_link( $parent_id, $tax_name ) );
        $parent_name = esc_html( $parent_term->name );
        echo '<li>'.
              '<a href="'. $parent_link .'">'.
                '<span>'. $parent_name .'</span>'.
              '</a>'.
            '</li>';
        //JSON-LDデータ
        $json_array[] = array(
          'id' => $parent_link,
          'name' => $parent_name
        );
      }
    }
    // ターム自身の表示
    echo '<li>'.
          '<span>'. esc_html( $term_name ) .'</span>'.
        '</li>';
    //JSON-LDデータ
     $json_array[] = array(
      'id' => $term_link,
      'name' => $term_name
    );
  } elseif ( is_search() ) {
    // 検索結果ページ
    echo '<li><span>「'. esc_html( get_search_query() ) .'」の検索結果</span></li>';
  
  } elseif ( is_404() ) {
    // 404ページ
    echo '<li><span>お探しの記事は見つかりませんでした。</span></li>';
  } else {
    // その他のページ(無いはずだが念のため)
    echo '<li><span>'. esc_html( get_the_title() ) .'</span></li>';
  }
  echo '</ul>';
  //JSON-LDの出力(2階層以上であれば)
  if ( !empty( $json_array ) ) :
    $pos = 1;
    $json = '';
    foreach ( $json_array as $data ) : 
      $json .= '{
        "@type": "ListItem",
        "position": '. $pos++ .',
        "item": {
            "@id": "'. $data['id'] .'",
            "name": "'. $data['name'] .'"
          }
      },';
    endforeach;
    echo '<script type="application/ld+json">{
      "@context": "http://schema.org",
      "@type": "BreadcrumbList",
      "itemListElement": ['. rtrim( $json, ',' ) .']
    }</script>';
    endif;
  echo '</div>';  // 冒頭に合わせた閉じタグ
?>

BringFlowerに依頼する

最後にして最も有効な選択肢、それは私に依頼することです(笑

お後がよろしいようで。

まとめ

パンくずリストは、オーガニック検索でそのページに直接たどり着いたサイト閲覧者が、他のページを回遊するきっかけにもなり得るものですので、しっかりと、ここで説明した内容を反映してSEO対策を行いましょう。ご相談があればお気軽にどうぞ。

著者のイメージ画像

BringFlower|稲田 高洋

2003年から大手総合電機メーカーでUXデザインプロセスの研究、実践。UXデザイン専門家の育成プログラム開発。SEOにおいても重要なW3Cが定めるWeb標準仕様策定にウェブアクセシビリティの専門家として関わる。2010~2018年に人間中心設計専門家を保有。その後、不動産会社向けにSaaSを提供する企業の事業開発部で複数サービスを企画、ローンチ。CMSを提供し1000以上のサイトを分析。顧客サポート、サイト運営にも関わる。2022年3月にBringFlowerを開業し、SEOコンサル、デザイン、ウェブ制作を一手に受ける。グッドデザイン賞4件、ドイツユニバーサルデザイン賞2件、米国IDEA賞1件の受賞歴あり。