SEO
パンくずリストとは?SEO効果と設置方法
ホームページの上部に表示することが望まれる「パンくずリスト」。どのように辿ってきたページなのかを示すというメリットもあることから、グリム童話「ヘンゼルとグレーテル」で、兄妹が道に迷わないようにと森の中で落としていったパンくずから名称がつけられています。英語では「breadcrumbs list」「breadcrumbs」と呼ばれています。
具体的にはどういったものなのか、どうして設置するのか、SEOとしての効果と、効果を最大限に発揮する設置方法などをご紹介していきます。
パンくずリストとは?
今ご覧いただいているこのページにも上部に「パンくずリスト」が設置されています。
ホーム > ノウハウ > SEO > パンくずリストとは?SEO効果と設置方法
となってますよね?
これはホームページの階層構造に近いものを示しています。ホームページの階層構造と言うと、専門的にはパソコンのフォルダの階層構造とイコールなので、「それに近い」ということになります。
一番右には現在表示されているページのタイトルが表示されていて、その左の「SEO」は、この記事のカテゴリーが表示されています。その左に表示されている「ノウハウ」をクリックすると、ノウハウ記事のすべてのカテゴリーが表示され、「SEO」をクリックすると「SEO」カテゴリーのノウハウ記事のすべてが表示されるようになっています。「ホーム」をクリックすればトップページが表示されます。
パンくずリストを設置する理由、メリット
では、どうしてパンくずリストが一般的に設置されているのでしょうか?その理由、メリットをご説明します。
ユーザビリティの向上
サイト閲覧者に対して、現在見ているページのサイト全体の中の位置づけが伝わりやすくすると同時に、同じカテゴリーの他のページを見たいと思ったときにそこにたどり着きやすくするという意味合いがまずあります。
SEO対策になる
パンくずリストを効果的に設置すれば、SEO対策になります。つまり、サイトのアクセス数、PV数が向上する方向に動きやすくなるということです。
SEO対策になる理由
では、どうしてSEO対策になるのでしょうか?その理由は、直接的なものではなく、間接的なものです。詳しくご説明します。
正式に定義されたものである
パンくずリストは、Schema.orgというGoogle、Microsoft、Yahooなどが設立した団体によって、定義されたものとなっています。つまり検索エンジンを持つGoogleやMicrosoftは、パンくずリストという形態を正式に認定しているということになるわけです。
ユーザーとGoogleがサイトの構造を把握しやすくなる
先述したように、ユーザーはサイトの構造を把握しやすくなります。ユーザーにとって分かりやすいということは、Googleの評価軸でもありますし、Googleにとっても構造が把握しやすくなります。
構造化マークアップという記述をコーディングの中で施すことで、Googleがパンくずリストをより正しく認識します。それにより、クロールされやすくなるという効果が見込めます。構造化マークアップの全体像はこちらでご説明していますが、パンくずリストの構造化マークアップに特化した部分を後述します。
適切にサイト内リンクが張り巡らされる
本サイトを例にすると、SEOに関連する記事はすべてSEOのカテゴリーページにリンクが張られます。有益なSEOの記事がたくさん書かれているという前提ですが、Googleはこの場合にSEOのカテゴリーページを高く評価する傾向があります。
次の画像は、当サイトが「SEO対策 東京」をターゲットキーワードの一つに定めていた頃に、実際に「SEO対策 東京」と検索したときに表示されていた検索結果画面です。カテゴリーページが評価されているのが分かります。一時はカテゴリーページだけが表示されてしまっていたので、そうならないような対処をしてこの状態です。
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();
//任意
?>
<div class="breadcrumbs">
<ul>
<li>
<a href="<?php echo esc_url( home_url() ) ?>"><span>ホーム</span></a>
</li>
<?php
if ( is_attachment() ) :
/* 添付ファイルページ ( $wp_obj : WP_Post )
添付ファイルページでは is_single() も true になるので先に分岐*/
$post_title = apply_filters( 'the_title', $wp_obj->post_title );
?> <li><span><?php echo esc_html( $post_title ) ?></span></li>
<?php
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' ) :
$tax_slug = ""; //そのサイトに合わせて投稿タイプごとに分岐させて明示的に指定してもよい
// 投稿タイプに紐づいたタクソノミーを取得 (投稿フォーマットは除く)
$taxonomies = get_object_taxonomies( $post_type, 'names');
foreach ($taxonomies as $taxonomy) :
if ( $taxonomy !== 'post_format' ) :
$tax_slug = $taxonomy;
break;
endif;
endforeach;
$post_type_link = esc_url( get_post_type_archive_link( $post_type ) );
$post_type_label = esc_html( get_post_type_object( $post_type )->label );
//カスタム投稿タイプ名の表示
?>
<li>
<a href="<?php echo $post_type_link ?>">
<span><?php echo $post_type_label ?></span>
</a>
</li>
<?php
//JSON-LDデータ
$json_array[] = array(
'id' => $post_type_link,
'name' => $post_type_label
);
else: //通常の投稿の場合、カテゴリーを表示
$tax_slug = 'category';
endif;
// 投稿に紐づくタームを全て取得
$terms = get_the_terms( $post_id, $tax_slug );
// タクソノミーが紐づいていれば表示
if ( $terms !== false ):
$child_terms = array(); // 子を持たないタームだけを集める配列
$parents_list = array(); // 子を持つタームだけを集める配列
//全タームの親IDを取得
foreach ( $terms as $term ):
if ( $term->parent !== 0 ):
$parents_list[] = $term->parent;
endif;
endforeach;
//親リストに含まれないタームのみ取得
foreach ( $terms as $term ):
if ( ! in_array( $term->term_id, $parents_list ) ):
$child_terms[] = $term;
endif;
endforeach;
// 最下層のターム配列から一つだけ取得
$term = $child_terms[0];
if ( $term->parent !== 0 ):
// 親タームのIDリストを取得
$parent_array = array_reverse( get_ancestors( $term->term_id, $tax_slug ) );
foreach ( $parent_array as $parent_id ):
$parent_term = get_term( $parent_id, $tax_slug );
$parent_link = esc_url( get_term_link( $parent_id, $tax_slug ) );
$parent_name = esc_html( $parent_term->name );
?>
<li>
<a href="<?php echo $parent_link ?>">
<span><?php echo $parent_name ?></span>
</a>
</li>
<?php
//JSON-LDデータ
$json_array[] = array(
'id' => $parent_link,
'name' => $parent_name
);
endforeach;
endif;
$term_link = esc_url( get_term_link( $term->term_id, $tax_slug ) );
$term_name = esc_html( $term->name );
// 最下層のタームを表示
?>
<li>
<a href="<?php echo $term_link ?>">
<span><?php echo $term_name ?></span>
</a>
</li>
<?php
//JSON-LDデータ
$json_array[] = array(
'id' => $term_link,
'name' => $term_name
);
endif;
// 投稿自身の表示
?><li><span><?php echo esc_html( strip_tags( $post_title ) ) ?></span></li>
<?php
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 ) );
?>
<li>
<a href="<?php echo $parent_link ?>">
<span><?php echo $parent_name ?></span>
</a>
</li>
<?php
//JSON-LDデータ
$json_array[] = array(
'id' => $parent_link,
'name' => $parent_name
);
endforeach;
endif;
// 投稿自身の表示
?>
<li><span><?php echo esc_html( strip_tags( $page_title ) ) ?></span></li>
<?php
elseif ( is_post_type_archive() ) :
// 投稿タイプアーカイブページ ( $wp_obj : WP_Post_Type )
?><li><span><?php echo esc_html( $wp_obj->label ) ?></span></li>
<?php
elseif ( is_date() ) :
// 日付アーカイブ ( $wp_obj : null )
$year = get_query_var('year');
$month = get_query_var('monthnum');
$day = get_query_var('day');
if ( $day !== 0 ) :
//日別アーカイブ
?>
<li>
<a href="<?php echo esc_url( get_year_link( $year ) ) ?>"><span><?php echo esc_html( $year ) ?>年</span></a>
</li>
<li>
<a href="<?php echo esc_url( get_month_link( $year, $month ) )?>"><span><?php echo esc_html( $month ) ?>月</span></a>
</li>
<li>
<span><?php echo esc_html( $day ) ?>日</span>
</li>
<?php
elseif ( $month !== 0 ) :
//月別アーカイブ
?>
<li>
<a href="<?php echo esc_url( get_year_link( $year ) ) ?>"><span><?php echo esc_html( $year ) ?>年</span></a>
</li>
<li>
<span><?php echo esc_html( $month ) ?>月</span>
</li>
<?php
else:
//年別アーカイブ
?><li><span><?php echo esc_html( $year ) ?>年</span></li>
<?php
endif;
elseif ( is_author() ) :
// 投稿者アーカイブ ( $wp_obj : WP_User )
?><li><span><?php esc_html( $wp_obj->display_name ) ?>の執筆記事</span></li>
<?php
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 );
/* タクソノミーに紐づくカスタム投稿タイプを出力 */
?>
<li>
<a href="<?php echo $post_type_link ?>">
<span><?php echo $post_type_label ?></span>
</a>
</li>
<?php
// 親ページがあれば順番に表示
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 );
?>
<li>
<a href="<?php echo $parent_link ?>">
<span><?php echo $parent_name ?></span>
</a>
</li>
<?php
//JSON-LDデータ
$json_array[] = array(
'id' => $parent_link,
'name' => $parent_name
);
endforeach;
endif;
// ターム自身の表示
?>
<li>
<span><?php echo esc_html( $term_name ) ?></span>
</li>
<?php
//JSON-LDデータ
$json_array[] = array(
'id' => $term_link,
'name' => $term_name
);
elseif ( is_search() ) :
// 検索結果ページ
?><li><span>「<?php echo esc_html( get_search_query() ) ?>」の検索結果</span></li>
<?php
elseif ( is_404() ) :
// 404ページ
?><li><span>お探しの記事は見つかりませんでした。</span></li>
<?php
else:
// その他のページ(無いはずだが念のため)
?><li><span><?php echo esc_html( get_the_title() ) ?></span></li>
<?php endif; ?>
</ul>
<?php
//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対策を行いましょう。ご相談があればお気軽にどうぞ。