今までPukiWikiにFacebookやTwitterといったSNSボタンを設置してソーシャルメディアに接続し、XMLサイトマップを組み込んだが、SEOに関しては特に何もしてなかった。
これではせっかくPukiWikiでサイトを構築しても片手落ちであるため、私なりにPukiWiki用にSEO対応プラグインを開発してみた。このプラグインを導入するにあたって、サイト構成も若干変更する必要があるので、その辺も含めて記事にしようと思う。
前回の記事と同様にサブドメインを連番で用意してあるので、今回はmuseum-18サブドメインの内容をmuseum-19サブドメインにコピーし、検証しながら作業を進めた。メニューページが「MenuBar」ではなく「メニューバー」になっている(日本語ページ名でPukiWiki用URL短縮ライブラリにより短縮される)ので要注意。
ザックリSEO対策とは?
PukiWiki用SEO対応プラグインの開発をする際に、改めて「SEO対策とは何か?」を考えてみた。その上でPukiWikiに実装するプラグインの要件を決めて開発をしたのだが、本稿を起こす上でも「ザックリSEO対策とは?」について触れておこうと思う。
statcounterの統計によると、2019年10月から直近12ヶ月の日本でのサーチエンジンのシェア(全デバイス)は次の通りだ。
1位 | 74.96% | |
2位 | Yahoo! | 21.01% |
3位 | Bing | 3.25% |
4位 | Baidu | 0.31% |
5位 | Naver | 0.04% |
国別ドメインも同一サーチエンジンとカウントして上位5位までのシェアを計算すると、上記の表になる。
ほぼGoogle一強なのがグラフからもランキングからも分かると思う。また、日本ではYahoo!のシェアは21%以上あるが、Yahoo!はGoogleからサーチエンジンを供与されているため、GoogleのSEO対策が出来ていればYahoo!も対策したことになる。つまり「SEO対策とはGoogle対策」であり、実に95.97%のサーチエンジンシェアに対して有効となる。
そこで、「Googleが掲げる10の事実」を引用しておこう。
- ユーザーに焦点を絞れば、他のものはみな後からついてくる。
- 1つのことをとことん極めてうまくやるのが一番。
- 遅いより速いほうがいい。
- ウェブ上の民主主義は機能する。
- 情報を探したくなるのはパソコンの前にいるときだけではない。
- 悪事を働かなくてもお金は稼げる。
- 世の中にはまだまだ情報があふれている。
- 情報のニーズはすべての国境を越える。
- スーツがなくても真剣に仕事はできる。
- 「すばらしい」では足りない。
上記の「事実」をGoogleのSEOとして捉えた場合、PukiWikiはシンプルなシステムであるため、そのままで3.に合致する。そして今までPukiWikiを一連の記事で改造しているため、5.に関してはレスポンシブデザインに対応しているし、8.に関してはGoogleサイト翻訳プラグインの開発と設置でクリアしていなくもない(?)。
流石に6.と9.に関してはSEOと関係ないが、それ以外の項目に関しては「質の高いコンテンツ」を発信しているサイトであるべきだと考えられるし、4.に関しては未だにサイトの被リンクが有効であると言えるだろう。サーチエンジンとしてみれば、検索するユーザが欲しいと思う情報を記載しているサイトの検索順位を上位にする必要があるし、誰もが欲しいと思う情報を記載しているサイトは、必然的に様々なサイトから被リンクされるからだ。
要するに、Google神に対して正しくサイトをアピールし、内容(コンテンツ)で勝負をかけるしかないだろう。そう考えると、サイトの「仕組み」としてSEOで実現できることは、そう多くない。
SEO対応プラグインの設定と設置
SEO対応プラグインで実現可能なSEO対策は、ページ単位で次の出力を可能としている。
- descriptionメタタグ出力
- og:description(Facebookへのページ概要OGP)メタタグ出力
- twitter:description(Twitterへのページ概要OGP)メタタグ出力
- keywordsメタタグ出力
- タグページ出力
上記1.に関しては、SEOとしてページ概要をサーチエンジンに知らせるために最低限やっておかなければならないタグ出力で、2.と3.は1.に付随して出力する機能である。4.に関しては、Googleがメタキーワードを採用していないようなので、直接SEO効果はないと思うが、ページのキーワードをメタタグで指定して悪いことはない、というレベルで機能としては用意しておいた。
最後の5.だが、これも実はSEOに直接関係はない。ページに付加する「タグ」は「内部リンク」として実現するので、分かりやすく言えば「タグクラウドを実現するか否か」という問題になる。そしてタグクラウド自体は往々にして「キーワードスタッフィング」(キーワードの詰め込み)になり勝ちであり、Googleとしても積極的に評価はしていないようだ。
私の見解としては、ページのタグ付けはユーザによるサイト内の回遊性が高まるし、内部リンクを適度に増やすことは(運営サイドのものでしかないが)被リンクの増加になるので、悪いことではないと考えている。
しかし、PukiWikiでプラグインによるタグクラウドの実現となると、ページの運用(更新)によってタグの追加・修正・削除があった際に、内部的に非常に面倒な処理を作り込む必要がある。そもそもSEOに直接関係ないのに、「SEO対応プラグインでそこまでやるべきか?」で考えると、タグクラウドまで実現する必要はない。だが、タグクラウドまで実現しなくとも、その仕組みがなければならない。
詳細は後述するが、PukiWikiのページ階層化機能とカテゴリー機能を応用し、ページ単位にタグ付けを可能とした。これはサイトの運用によってはタグではなく「カテゴリー」として運用も可能なように考えて実装している。自動的なページのタグ付け処理ではないため、積極的に利用するかどうかはサイト管理者に判断を委ねる。
結局、プラグインとして直接SEOに関係する機能は、1.の「descriptionメタタグ出力」しかないのが実情だ。それでも、FacebookやTwitterにページを連動する際に有効であるし、少ない労力でページにタグ付けが可能になるので、プラグインを導入するメリットはあると思う。
SEO対応プラグインは本サイトのダウンロードページにアップロードしているので、ダウンロードしてローカルの作業フォルダに解凍する。
seo.inc.phpファイルの21行目に、次の通りプラグインの設定がある。
seo.inc.php
// Facebook OGPタグ出力設定
define('PLUGIN_SEO_FACEBOOK_OGP', 1); // 1:有効 0:無効
// Twitter OGPタグ出力設定
define('PLUGIN_SEO_TWITTER_OGP', 1); // 1:有効 0:無効
// タグページ名(階層構造のトップページ名)
define('PLUGIN_SEO_TAG_PAGE', ''); // タグページを出力しない(初期値)
//define('PLUGIN_SEO_TAG_PAGE', 'Tag'); // 「Tag」ページ名で階層構造でタグページを出力する
FacebookやTwitter用にOGPタグを出力しない場合はそれぞれ「0」を設定し、「Tag」ページ名で階層構造でタグページを出力する場合はコメントを外して保存する。
初期値としてタグページを出力しないようにしているのは上述の理由からだが、出力する場合でもページ名は「Tag」でなくても良い。例えばタグではなくカテゴリーとして運用したい場合は、「Category」に変更するのも良いだろう。
seo.inc.phpファイルをサーバの「plugin」フォルダにFTPし、ファイル属性(パーミッション)を「644」に設定すれば、プラグインの設定と設置が完了する。
サイト構成を変更する
以前の記事「PukiWiki1.5.2をソーシャルメディアに接続してFacebookコメントを実装する!」で、「skin」フォルダのpukiwiki.skin.phpファイル内容を修正しているが、SEO対応プラグインでFacebookとTwitterへのページ概要タグを出力するため、再度修正する必要がある。
一応、当該記事での修正内容を「修正前」として掲載し、その上で修正内容を「修正後」としてソースを掲載する。当該記事でファイルを修正していない場合は、「修正後」のソースのみを参照して欲しい。
skin\pukiwiki.skin.php(修正前)
<!-- ここからFacebook OGPタグ -->
<meta property="fb:app_id" content="【FacebookのアプリID】" />
<meta property="og:title" content="<?php
if ($title == "" || $title == $defaultpage) {
echo $page_title;
} else {
echo $title;
}
?>" />
<meta property="og:type" content="article" />
<meta property="og:url" content="<?php echo 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; ?>" />
<meta property="og:description" content="<?php echo $str = str_replace(array("\r\n", "\r", "\n", " ", " "), '', mb_substr(strip_htmltag($body, $all = TRUE), 126, 160, "UTF-8")); ?>" />
<meta property="og:site_name" content="<?php echo $page_title ?>" />
<meta property="og:image" content="<?php
$url = 'https://' . $_SERVER['HTTP_HOST'] . '/';
preg_match_all('/<img.*?src=(["\'])(?!image\/face\/)(.+?)\1.*?>/', $body, $img_array);
$chk = mb_substr($img_array[2][0], 2);
if ($chk == "") {
echo $url . 'image/eye-catch.png';
} else {
echo $url . $chk;
}
?>" />
<!-- ここまでFacebook OGPタグ -->
<!-- ここからTwitter OGPタグ -->
<meta name="twitter:site" content="@LeijiMuseum" />
<meta name="twitter:title" content="<?php
if ($title == "" || $title == $defaultpage) {
echo $page_title;
} else {
echo $title;
}
?>" />
<meta name="twitter:description" content="<?php echo $str = str_replace(array("\r\n", "\r", "\n", " ", " "), '', mb_substr(strip_htmltag($body, $all = TRUE), 126, 160, "UTF-8")); ?>" />
<meta name="twitter:image" content="<?php
$url = 'https://' . $_SERVER['HTTP_HOST'] . '/';
preg_match_all('/<img.*?src=(["\'])(?!image\/face\/)(.+?)\1.*?>/', $body, $img_array);
$chk = mb_substr($img_array[2][0], 2);
if ($chk == "") {
echo $url . 'image/eye-catch.png';
} else {
echo $url . $chk;
}
?>" />
<meta name="twitter:card" content="summary_large_image" />
<!-- ここまでTwitter OGPタグ -->
skin\pukiwiki.skin.php(修正後)
<?php echo $head_tag ?>
<!-- ここからFacebook OGPタグ -->
<meta property="fb:app_id" content="【FacebookのアプリID】" />
<meta property="og:title" content="<?php
if ($title == "" || $title == $defaultpage) {
echo $page_title;
} else {
echo $title;
}
?>" />
<meta property="og:type" content="article" />
<meta property="og:url" content="<?php echo 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; ?>" />
<?php if (stripos($head_tag, 'og:description') === false) { ?>
<meta property="og:description" content="<?php echo $str = str_replace(array("\r\n", "\r", "\n", " ", " "), '', mb_substr(strip_htmltag($body, $all = TRUE), 0, 160, "UTF-8")); ?>" />
<?php } ?>
<meta property="og:site_name" content="<?php echo $page_title ?>" />
<meta property="og:image" content="<?php
$url = 'https://' . $_SERVER['HTTP_HOST'] . '/';
preg_match_all('/<img.*?src=(["\'])(?!.*image\/face\/)(?!.*google\.com\/)(.+?)\1.*?>/', $body, $img_array);
$chk = mb_substr($img_array[2][0], 2);
if ($chk == "") {
echo $url . 'image/eye-catch.png';
} else {
echo $url . $chk;
}
?>" />
<!-- ここまでFacebook OGPタグ -->
<!-- ここからTwitter OGPタグ -->
<meta name="twitter:site" content="" />
<meta name="twitter:title" content="<?php
if ($title == "" || $title == $defaultpage) {
echo $page_title;
} else {
echo $title;
}
?>" />
<?php if (stripos($head_tag, 'twitter:description') === false) { ?>
<meta name="twitter:description" content="<?php echo $str = str_replace(array("\r\n", "\r", "\n", " ", " "), '', mb_substr(strip_htmltag($body, $all = TRUE), 0, 160, "UTF-8")); ?>" />
<?php } ?>
<meta name="twitter:image" content="<?php
$url = 'https://' . $_SERVER['HTTP_HOST'] . '/';
preg_match_all('/<img.*?src=(["\'])(?!.*image\/face\/)(?!.*google\.com\/)(.+?)\1.*?>/', $body, $img_array);
$chk = mb_substr($img_array[2][0], 2);
if ($chk == "") {
echo $url . 'image/eye-catch.png';
} else {
echo $url . $chk;
}
?>" />
<meta name="twitter:card" content="summary_large_image" />
<!-- ここまでTwitter OGPタグ -->
FacebookとTwitterのOGPタグ部分を上記の通り修正し、SEO対応プラグインでメタタグ出力がない場合のみページの内容から概要を採用するように変更する。従来までのコードだと、「忍者おまとめボタン」のJavaScriptコード部分を避けてページ概要を取得していたが、思い切ってこの辺も手を入れて修正することにする。
具体的には「lib」フォルダのpukiwiki.phpファイルで設定している「忍者おまとめボタン」の次のコードを「skin」フォルダのpukiwiki.skin.phpファイルに移管(50行目付近の適切な場所にコピー&ペースト)する。
lib\pukiwiki.php
// 「忍者おまとめボタン」スクリプトコード
$sns_button = <<< EOM
<div class="ninja_onebutton">
<script type="text/javascript">
//<![CDATA[
(function(d){
if(typeof(window.NINJA_CO_JP_ONETAG_BUTTON_【忍者おまとめボタンのユニークコード】)=='undefined'){
document.write("<sc"+"ript type='text\/javascript' src='\/\/omt.shinobi.jp\/b\/【忍者おまとめボタンのユニークコード】'><\/sc"+"ript>");
}else{
window.NINJA_CO_JP_ONETAG_BUTTON_【忍者おまとめボタンのユニークコード】.ONETAGButton_Load();}
})(document);
//]]>
</script><span class="ninja_onebutton_hidden" style="display:none;"></span><span style="display:none;" class="ninja_onebutton_hidden"></span>
</div>
EOM;
さらに、pukiwiki.phpファイルで「忍者おまとめボタン」を出力している次の箇所を削除するかコメントアウトしておく。
lib\pukiwiki.php
prepare_display_materials();
$body = convert_html(get_source($base));
// 「忍者おまとめボタン」をページボディ部の上下に追加
//$body = $sns_button . $body . "<br />" . $sns_button;
再度「skin」フォルダのpukiwiki.skin.phpファイルを修正する。今まで修正していたコードを「修正前」とし、さらに修正する内容を「修正後」として掲載するので、内容を確認して修正して欲しい。
skin\pukiwiki.skin.php(修正前)
<?php if ($menu !== FALSE) { ?>
<!-- ボディ部の構成を変更 2019/05/19 -->
<div id="contents">
<!-- メニューページ部 2019/07/12 -->
<div id="menubar">
<?php echo $menu ?>
</div>
<!-- コンテンツページ部 2019/07/12 -->
<div id="body">
<?php echo $body ?>
<!-- コンテンツページに「注釈」を移動 2019/07/12 -->
<?php if ($notes != '') { ?>
<div id="note"><?php echo $notes ?></div>
<?php } ?>
</div>
</div>
<?php } else { ?>
<div id="body"><?php echo $body ?></div>
<?php } ?>
skin\pukiwiki.skin.php(修正後)
<?php if ($menu !== FALSE) { ?>
<!-- ボディ部の構成を変更 2019/05/19 -->
<div id="contents">
<!-- メニューページ部 2019/07/12 -->
<div id="menubar">
<?php echo $menu ?>
</div>
<!-- コンテンツページ部 2019/07/12 -->
<div id="body">
<!-- パンくずリスト追加・SNSボタン移設 2019/11/10 -->
<div class="topicpath">
<?php require_once(PLUGIN_DIR . 'topicpath.inc.php'); echo plugin_topicpath_inline(); ?>
</div>
<?php echo $sns_button ?>
<?php echo $body ?>
<!-- コンテンツページに「注釈」を移動 2019/07/12 -->
<?php if ($notes != '') { ?>
<div id="note"><?php echo $notes ?></div>
<?php } ?>
<br />
<?php echo $sns_button ?>
</div>
</div>
<?php } else { ?>
<div id="body"><?php echo $body ?></div>
<?php } ?>
上述のようにファイルを修正したら、それぞれのフォルダにFTPしておく。
パンくずリストの調整
サイト構成を変更するにあたり、今回パンくずリストを追加している。
図の赤枠部分がパンくずリストで、PukiWiki標準のtopicpathプラグインが出力しているものの、標準のままだとイマイチなので調整する。
「plugin」フォルダのtopicpath.inc.phpファイルの20行目の設定を「/」から一般的だと思われる「>」に変更してみよう。
plugin\topicpath.inc.php(修正前)
// Separetor / of / topic / path
define('PLUGIN_TOPICPATH_TOP_SEPARATOR', '<span class="topicpath-slash">/</span>');
plugin\topicpath.inc.php(修正後)
// Separetor / of / topic / path
define('PLUGIN_TOPICPATH_TOP_SEPARATOR', '<span class="topicpath-slash">></span>');
次に、CSSを修正するため「skin」フォルダのpukiwiki.cssファイルの618行付近を次のように修正する。
skin\pukiwiki.css(修正前)
/* topicpath.inc.php */
span.topicpath-slash {
margin:0 0.2em;
}
span.topicpath-top {
user-select:none;
}
skin\pukiwiki.css(修正後)
/* topicpath.inc.php */
div.topicpath {
margin: 0px 0px 20px 0px;
}
span.topicpath-slash {
/* margin:0 0.2em; */
margin: 0 0.5em;
}
span.topicpath-top {
user-select:none;
}
上述のようにファイルを修正したら、それぞれのフォルダにFTPしておく。
図のようにパンくずリストを調整してみたが、修正前と比較してみて欲しい。
サイトのレイアウトやデザインは各人の好みであるが、PukiWiki標準デザインから大きく変更する際に気を付けていただきたいのが、図に書いておいたが「h1タグ」の部分だ。SEO的にページの開始はh1タグで、ページに1つだけ存在するのが「お約束」なので、サイトをどんなレイアウトやデザインに変更した場合でも、h1タグを省略しないようにして欲しい。
なお、サイトのトップページ(この場合は「FrontPage」)にはパンくずリストを出力しないので、誤解のないように。
SEO対応プラグインの使用例
「お問い合わせ」ページを例に、SEO対応プラグインの使い方を説明しよう。
図の赤枠のようにページ上部にSEO対応プラグインを記述し、タグとして内部リンクを埋め込む形で利用する。SEO対応プラグインに関するコードは次の通りとなる。
SEO対応プラグインの使用例
#setlinebreak(off)
&seo(description){「松本零士私設博物館」お問い合わせ};
&seo(keywords){管理人,お問い合わせ};
&seo(tag){管理人,お問い合わせ};
#setlinebreak(on)
----
タグ:[[管理人>Tag/管理人]], [[お問い合わせ>Tag/お問い合わせ]]
----
1行目:setlinebreakプラグインで改行コード出力を抑制
2行目:ページ概要メタタグ出力
3行目:メタキーワード出力
4行目:タグページ「Tag/管理人」「Tag/お問い合わせ」出力
5行目:setlinebreakプラグインで改行コード出力抑制を解除
7行目:4行目でプラグインが出力するタグページに内部リンクを張る
上記コードの2~4行目がSEO対応プラグインの記述例で、2行目と3行目の内容で<head>タグ内にメタタグを出力し、4行目でタグページを(ページが存在しない場合に)出力する。
SEO対応プラグインは記述したページ(この場合は「お問い合わせ」ページ)に何かを出力するプラグインではないし、プログラム内部でもヌル文字を返却しているのだが、どういうワケか無意味に改行コード(HTMLの<br>コード)が出力されてしまう。そこで、1行目と5行目でsetlinebreakプラグインにて改行コードの出力制御をしている。
7行目がミソになるが、SEO対応プラグインで出力したタグページに内部リンクを張ることで、タグページには自分自身のページが参照されるように仕組んである。これはPukiWiki特有のページ階層化機能とカテゴリー機能の応用だが、この例の場合「Tag/管理人」ページと「Tag/お問い合わせ」ページに「Tag利用ページ一覧」として「お問い合わせ」が列挙される。
図は「Tag/管理人」ページの画面だが、「管理人」というTagは「お問い合わせ」ページで使われている、という意味だ(relatedプラグインの仕様で、タグページのトップ階層である「Tag」ページも列挙されてしまう)。
SEO対応プラグインでは、ページのタグ付けをPukiWikiのページ階層化機能を利用しているため、視覚的に分かりやすいようにパンくずリストを出力するようにサイト構成を変更する必要があったのである。
「お問い合わせ」ページに戻って説明すると、SEO対応プラグインでページのタグをいくら追加しようが、すでにあったタグを修正したり削除したりしようが、自ページ(この場合は「お問い合わせ」ページ)から内部リンクをしない限りプラグインで出力したタグページと関連付けは行われない。
逆に言えば、関連付けがされていない空っぽのタグページが発生してしまうことになるが、それはプラグインでどうにかする問題ではなく、PukiWikiの運用でカバーする問題である。自分自身でタグページに内部リンクを張る仕組みであるため、ページに不用意かつ大量にタグを追加しない抑止力になることを想定している。
ちなみにSEOチェキ!で調べると、図のようにSEO対応プラグインで設定したページ情報が正しく反映されていることが分かる。
ここまでの改造の成果
ここまでの改造の成果であるPukiWikiはこちらなので、「実際どうなのか?」を確認したい人は参照して欲しい。
今回も前回と同様にゲストアカウントを用意したので、スパムや破壊行為は困るが(スパムフィルタspam_filter.phpは導入済みだが)、ユーザー名とパスワードは次の通りだ。
ユーザー名:guest
パスワード:guest
ファイルの添付やページの凍結・解除は行なえないが、ほぼ全てのPukiWikiの作業が可能なので、色々と試してみて欲しい。「案外PukiWikiって使えるな」と思っていただければ、私としても嬉しく思う。
なお、ページのタグ付けに関して有用性が分かりにくいと思うので、私が運営している太宰治真理教のサイトも参考にしていただきたい。
おわりに
PukiWikiに何らかのSEOの仕組みが必要だとは考えていたが、プラグインの開発を含め本稿を書くのにずいぶんと時間がかかった。個人的にSEOに関して「正解」を理解しているとは考えていないので、心理的に苦手意識が働いているのだと思う。
そもそもSEOはすぐに効果が得られるとは言えないし、Google SearchConsoleでサイトマップを送信するとGoogleに反映されるのに中5日、Bing web マスターだと同様にBingに反映されるのに中1日かかるようだ。そこから検索順位がどのように変化するのかまで細かく追跡したことがないし、専門的にSEO対策をしたことがないので、確信を持って進めることが出来なかった。
そうかと言って放置して何もしないワケにも行かないし、サイトの構成も変更する面倒もあるしで、本稿を書くのも遅々として進まない。記事のボリュームも大きくなったが、「やっとここまで書いた」という感じだ。
PukiWikiのSEOに関する記事は私が探した範囲では無かったので、開発したSEO対応プラグインと本稿が多少でも参考になり、お役に立てば嬉しく思う。
Warning: strpos() expects parameter 1 to be string, array given in /home/eware/dajya-ranger.com/public_html/wp-includes/compat.php on line 498
Warning: preg_match_all() expects parameter 2 to be string, array given in /home/eware/dajya-ranger.com/public_html/wp-includes/shortcodes.php on line 155
Array