XMLサイトマップのおさらいとWorePress標準のXMLサイトマップ出力機能

何かがリストアップされた書類
©いらすとや.

サイトマップのおさらい

ウェブのサイトマップというのは人間向けと検索エンジン向けに提供する2種類があります。 人間向けというのは例えばカテゴリ別に「このような分類の仕方で分けてページをこのように階層化しています」というような階層をどのように辿れば目的のページを見つけられるかという案内図のようなものです。デパートやスーパーでそれぞれの階で何を扱っているかの案内板とか売り場の地図だとかと似たものとなります。このページでは人間向けのサイトマップは扱いません。

検索エンジン向けのサイトマップは、「マップ」という単語を含むものの実際はマップというよりリストです。「私のサイトにある(全ての)ページの一覧を用意しました。検索エンジンさん、サイトマップを見て私のサイトのページをクロールして検索インデックスに登録して下さい」というものです。検索エンジン向けなので人間が認識できなくても構いません。検索エンジンが理解できるフォーマットで書きましょう。

Googleの サイトマップの作成と送信 の解説。

とはいえ、普通は手書きするものではありません。CMSのようなものを使っているのであればCMSにサイトマップ出力機能があるかもしれませんし、無くてもサイトマップを出力する機能を追加すれば対応できます。WordPressであればv5.5.0からはXMLサイトマップを出力する機能が標準装備になりましたし、それ以前のバージョンでもプラグインでサイトマップ出力機能を追加できました。もちろんv5.5.0以降もプラグインによりサイトマップを出力できます。

検索エンジンが理解できるサイトマップのフォーマットで最も簡単なのはテキスト形式で1行に1つページのURLを書くものです。

プレーンテキスト形式のサイトマップ
http://example.com/content1.html
http://example.com/content2.html
http://example.com/content3.html
http://example.com/content4.html

単純明快ですが、これだとURL情報しか送信できません。それはちょっと不都合です。ウェブサイト内のページが全部で数ページ程度でそれぞれのページが設置後に更新されないということであればこれでも構わないでしょうが、その程度の規模であればそもそもサイトマップは必要ありません。

RSSフィードやAtomフィードもGoogleはサイトマップとして扱うようです。しかし、RSSフィードやAtomフィードはサイトの全ページ一覧を扱うのではなく「最近の記事」の一覧です。(普通は10記事以内程度)。なのでウェブサイトオーナーがサイトマップを「私のサイトの全てのページの一覧」という認識をしているのであれば不適でしょう。
ただし、最近新しく公開したページをGoogleなどの検索エンジンに知らせるという意味ではフィードURLを(サイトマップの1つとして)検索エンジンに登録するのは有用です。(Search Consoleでサイトマップの1つとしてフィードURLを登録するのはオススメです。)フィードだけだと最近の記事である数個〜10数個程度のURLしか含まれないため(全URLが含まれる)XMLサイトマップも合わせて登録するべきです。

現在、検索エンジン向けサイトマップの主流はXML形式です。

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>http://example.com/content1.html</loc>
    <lastmod>2022-07-24</lastmod>
  </url>
 <url>
    <loc>http://example.com/content2.html</loc>
    <lastmod>2022-07-27</lastmod>
  </url>
</urlset>

この例ではページのURLとページの更新日時だけを通知していますが、ページの更新頻度や優先度のような情報を追加することができます。
参照: https://www.sitemaps.org/ja/protocol.html

また、サイトマップを小分けにすることができます。中規模以上のウェブサイトだと数千〜数十万のURLがあるでしょうがそれら全てを1つのサイトマップに載せるのは効率が悪いです。CMSなどを使っていればおそらくサイトマップ出力も動的に行われますが全てのURLを出力しようとして処理が重く表示されるまで待たされるのはよくありません。サイトマップの分け方は任意ですが、記事のカテゴリや月別で分けるのが一般的でしょう。

サイトマップを小分けする場合はその子サイトマッブのリストを記載するサイトマップインデックスファイルを作成してそれを親サイトマップということにします。

<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <sitemap>
    <loc>https://example.com/child-sitemap-2022-07.xml</loc>
    <lastmod>2022-07-31T23:59:59+00:00</lastmod>
  </sitemap>
  <sitemap>
    <loc>https://example.com/child-sitemap-2022-06.xml</loc>
    <lastmod>2022-06-30T23:59:59+00:00</lastmod>
  </sitemap>
  <sitemap>
    <loc>https://example.com/child-sitemap-2022-05.xml</loc>
    <lastmod>2022-05-31T23:59:59+00:00</lastmod>
  </sitemap>
</sitemapindex>

上の例は月別でサイトマップを分けています。sitempindex内にsitemapタグがありその下のlocタグで子サイトマップの在り処を示しています。
子サイトマップの名前は任意です。子サイトマップは普通のサイトマップの書き方です。

仮に月別でサイトマップを分けたとしてその中のURLの数が100以下程度であればリクエストからサイトマップ出力までほぼ瞬時なので検索エンジンのクローラーがサイトマップを取得失敗するということは無い筈です。

なお、Googleさんによりますとサイトマップの容量の上限は50MB(非圧縮時)とのことです。50MBだと仮に英数字だけで構成するなら単純に50,000,000(5千万)文字です。新聞の朝刊の文字数が(全角かな/漢字も単純に1文字として)15万文字程度であるとすれば333日分(約11ヶ月分)となります。このことから中規模/大規模サイトであったとしてもサイトマップ1つでも賄えるかもしれませんが、その場合はサイトマップは動的出力+キャッシュまたは静的出力が好ましいでしょう。(毎日夜中に出力するなど)

WordPress標準のXMLサイトマップ出力機能

前述のとおり、WordPressはv5.5から標準でXMLサイトマップを出力できる。
テーマ内でサイトマップ出力機能を意図的に無効化しなければXMLサイトマップが出力される。別途XMLサイトマップ出力用のプラグインなどを使用するとそちらが使われる。(もしくはWordPress標準のサイトマップとプラグインのサイトマップの両方が機能します。)

WordPress標準機能のXML Sitemap 1
WordPressのウェブサイトのURLが https://example.com であれば XMLサイトマップのURLは https://example.com/wp-sitemap.xml となる。これはサイトマップでよく使われるURLとは違うが。設定によってはhttps://example.com/sitemap.xml にもできる。
そして、生意気にも子サイトマップ対応。しかし、その子サイトマップの分け方が残念。
WordPressの標準では1つのサイトマップに2000URLまで書き込まれるようになっている。 つまり、「投稿」用の子サイトマップ wp-sitemap-posts-post-1.xmlに2000 URLまで溜まるみたい。「投稿」の数が2000を超えるとwp-sitemap-posts-post-2.xmlが作成されてそこにまた2000URLまで入る。(これの繰り返し)
これだと子サイトマップ1つあたりに多く入りすぎ。
「固定記事」を大量に作成するという使い方はWordPressではあまりないとは思われるが、wp-sitemap-posts-page-1.xmlも2000 URLまで溜まり、超えるとwp-sitemap-posts-page-2.xmlに入るという感じ?

WordPress標準機能のXML Sitemap 2
投稿記事用の子サイトマップ wp-sitemap-posts-post-1.xml を表示してみた。
画像のように、投稿記事のURLが並ぶだけ。これが非常に残念なポイントで、どうして標準状態で更新日時を出力してくれないのか?

WordPress標準のサイトマップ出力機能に更新日時を追加するコード
(v5.5リリース時に出たドキュメントから)
使用中のWordPressテーマディレクトリのfuctions.phpに1行追加。
/WORDPRESS-PATH/wp-content/themes/使用中テーマ/functions.php
function bny_sitemap_lastmod( $entry, $post ) {
    $entry['lastmod'] = $post->post_modified_gmt;
    return $entry;
}
add_filter( 'wp_sitemaps_posts_entry', 'bny_sitemap_lastmod', 10, 2 );

(↑)これでも問題ない筈だがが、Googleさんは気に入らないらしくSearch Consoleでは日付形式のエラーが報告されることになる。
直さなくても問題はないがエラーなのが気に入らなければ次。

function bny_sitemap_lastmod( $entry, $post ) {
    $entry['lastmod'] = get_the_modified_time('c', $post);
    return $entry;
}
add_filter( 'wp_sitemaps_posts_entry', 'bny_sitemap_lastmod', 10, 2 );

WordPress標準機能のXML Sitemap 3
タイムゾーンは無いが記事の更新日時が追加されるようになった。

WordPress標準の子サイトマップ1つあたりのURL数の上限を変更するコード
使用中のWordPressテーマディレクトリのfuctions.phpに1行追加。
/WORDPRESS-PATH/wp-content/themes/使用中テーマ/functions.php
function bny_sitemap_maxurls( $max_urls, $object_type ) {
    $max_urls = 10;
    return $max_urls;
}
add_filter( 'wp_sitemaps_max_urls', 'bny_sitemap_maxurls', 10, 2 );

上のコードの $max_urls = 10; の10を希望の上限数にする。10は極端に少ないので100〜500あたりが無難かと。(WordPressの初期値は2000)

WordPress標準機能のXML Sitemap 4
1サイトマップに含められるURL数を10に絞ったので元々の「投稿」用の子サイトマップ wp-sitemap-posts-post-1.xml が1つでは無理になった。溢れたURLを納めるためにサイトマップインデックス(親サイトマップ)にはwp-sitemap-posts-post-2.xml 〜 wp-sitemap-posts-post-11.xmlの子サイトマップが増えている。

WordPress標準機能のXML Sitemap 5
変更前はwp-sitemap-posts-post-1.xmlには108個のURLがあったが、1サイトマップのURL数の上限を10に変更したのでそのとおりになっている。10というのは極端だが、このように上限数を設定できるのは好ましい。

このように、更新日時を出力できて、子サイトマップ内のURL数を変更することもできるのでWordPress標準機能のXMLサイトマップ出力機能の気に入らないことは投稿記事用の子サイトマップを年別や月別で分けられないということ。まぁ、管理者の自己満足の部分だけど、気になるとWordPress標準のXMLサイトマップは嫌ということに。年別/月別などで子サイトマップを出し分けできて更新日時が出力されるXMLサイトマッププラグインを使いたいところ。

WordPress標準のXMLサイトマップ出力はデフォルトで有効だが、他のXMLサイトマップ出力ブラグインを使うとかXML出力機能を自作して組み込んだということであれば標準のXMLサイトマップ出力機能は完全に停めたいかも。

使用中のWordPressテーマディレクトリのfuctions.phpに1行追加。
/WORDPRESS-PATH/wp-content/themes/使用中テーマ/functions.php
add_filter( 'wp_sitemaps_enabled', '__return_false' );

他にもWordPress標準のXMLサイトマップのカスタムに役立つ情報がWordPress v5.5リリース時に出たドキュメントに解りやすく書かれています。
参照: https://make.wordpress.org/core/2020/07/22/new-xml-sitemaps-functionality-in-wordpress-5-5/

関連記事:

WordPressのサイズ別画像出力 テキストエディタ用

絵を飾る
©いらすとや.

最近のWordPressは記事作成支援のブロックエディタが優秀。画像を記事中に埋めるときちんとサイズ別に送信するようになっているので狭い画面向けに小さいサイズの画像が送信できる。これは送信データ量と転送遅延の低減につながるので好ましい。

WordPressでサイズ別画像出力 1
ブロックエディタ(Gutenbergエディタ)を使ってメディアライブラリから画像を選んで記事に入れるだけ。とっても簡単だけど個人的には大嫌い。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<figure class="wp-block-image size-large">
  <img loading="lazy"
       width="1024"
       height="682" 
       src="https://example.com/wp-content/uploads/2022/06/hoge-1024x682.jpg"
       alt="画像の説明"
       class="wp-image-199"
       srcset="https://example.com/wp-content/uploads/2022/06/hoge-1024x682.jpg 1024w,
               https://example.com/wp-content/uploads/2022/06/hoge-300x200.jpg 300w,
               https://example.com/wp-content/uploads/2022/06/hoge-768x512.jpg 768w,
               https://example.com/wp-content/uploads/2022/06/hoge.jpg 1040w"
       sizes="(max-width: 1024px) 100vw, 1024px" />
</figure>

ブロックエディタで画像を挿入した場合の画像タグ(HTML)の出力例。ここでは見やすいよう13行に分けているが、実際は1〜3行で出力される。本来はsrcで指定される画像がブラウザで表示されるが、srcsetが指定されている場合はそちらが優先。この例ではスマホのような狭画面では300wで指定された画像が、もう少し幅広のブラウザだと768wで指定された画像が、ブラウザのウィンドウ幅が1024px近くまたはそれ以上であれば1024Wで指定された画像が出力される。

WordPressでサイズ別画像出力 2
「がとらぼ」の中の人はHTML手書き派なのでブロックエディタは性に合わない。旧来のエディタ、それも「ビジュアルエディタ」ではなく「テキストエディタ」を使っている。できるだけ単純化するようにしているので皆が思うほどはHTMLタグを書くことはない感じ。

で、画像タグも単純化して書きたいので
<img src="/dir/imagefile.avif" width="1200" height="1000" alt="画像説明" />
のようにしたい。間違いが少なくて済むし。srcsetで複数の画像のURLを書くのは手間だしコードの視認性が悪くなるし間違いの元。

しかし、この書き方だと大きなサイズの画像があると必ずその大サイズの画像が送信されることになるのでPageSpeed Insightsでの評価が僅かに下がる。画像サイズや数にもよるかとは思われるが微妙に1点とか2点ほど削られることになり100点が取れないことが増える。PageSpeed Insightsで100点取れなくてはならないという訳では全くないが気分が良くない。

WordPressでサイズ別画像出力 3
ケースによっては100点を得られることもあるが、画像のように「適切なサイズの画像」という指摘は間違いなく食らうことになる。

そこで、WordPressのテキストエディタでは <img src="/dir/画像大.avif" width="1200" height="1000" alt="画像説明" /> の様に 書いてWordPressのデータベースに保存。ページ表示時にそれを読み出して「自動変換」して <img src="/dir/imagefile.avif" width="1200" height="1000" alt="画像説明" srcset="/dir/画像小.avif 320w, /dir/画像中.avif 640w, /dir/画像大.avif 800w" /> の様に出力させたい。

WordPressで使用しているテーマのfunctions.phpに追加
/wordpress-path/wp-content/themes/your-theme/functions.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function bny_img_srcset_attachments( $content ) {
    $buf  = '<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /></head><body>';
    $buf .= $content;
    $dom = new DOMDocument();
    @$dom->loadHTML($buf, LIBXML_HTML_NODEFDTD | LIBXML_NOERROR);

    foreach ($dom->getElementsByTagName('img') as $node) {
        $nodes[] = $node;
    }
    if (isset($nodes)){
        foreach ($nodes as $node) {
            $nsrc = $node->getAttribute('src');
            $nwidth = $node->getAttribute('width');
            if (!$node->hasAttribute('srcset')) { 
                if(strpos($nsrc,'wp-content/uploads') === false){
                    $exten = '.' .  pathinfo($nsrc, PATHINFO_EXTENSION);
                    $srcset  = str_replace ($exten , "-320$exten", $nsrc) . ' 320w, ';
                    $srcset .= str_replace ($exten , "-640$exten", $nsrc) . ' 640w, ';
                    if ($node->hasAttribute('width')) {
                        $srcset .= $nsrc . ' ' . $nwidth . 'w';
                    }
                    $node->setAttribute('srcset', $srcset);

                    if ($node->hasAttribute('width')) {
                        $sizeval = '(max-width: 360px) 320px, (max-width: 680px) 640px, ' . $nwidth .'px';
                        $node->setAttribute('sizes', $sizeval);
                    }
                }
            }
        }
    }
    unset($nodes);
    $buf = preg_replace('/^\<html\>.*?\<body\>/', '', $dom->saveHTML());
    $buf = preg_replace('/\<\/body\>.*?\<\/html\>\n/', '', $buf);
    return $buf;
}
add_filter( 'the_content', 'bny_img_srcset_attachments', 10, 2 );

記事本文(HTML)をXMLのDOMとして読み込みimgタグにsrcset属性を付けるという処理。単純な文字列の置換だとどうしてもヘンに漏れる可能性があるのでXMLの要素に属性を付ける方が確実(と思っている)。
ただし、記事本文の中でタグの閉じ忘れがあると勝手に補完されることがあるので注意。
通常は補完されて困ることはないだろうが意図的にイレギュラーなコードにしていると勝手に補完されて造り手の意図と違う表示になる。造り手のミスで不完全なXML(HTML)になっていた場合でも望まれない箇所で補完されることでレイアウト崩れが発生する可能性も無くはない。

2022年9月11日修正:
上のPHPコードで一部修正を行いました。WordPress標準が出すのを真似たsizesを付けていたところ、PageSpeed Insightsではサイズの最適化がなされていないという評価になることがあったので画面幅が360px以下であれば320w用、画面幅が680px以下であれば640w、それ以上であれば元の画像幅というクドい指定にした。元の画像幅が360px, 680px以下である可能性もあるだろうが問題無いはず。

もちろん、少サイズ, 中サイズの画像は別途用意する必要がある。ImageMagickが入っているシステム用のスクリプトを用意した。

img_small.sh (新規)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/usr/local/bin/bash

#引数にpathを指定して実行のこと。
if [ $# -ne 1 ]; then
  echo "利用方法: $0 path" 1>&2
  echo "実行するには引数としてpathが必要です。" 1>&2
  exit 1
fi

#WebP  -  width 640px
find ${1}/ -type f -name "*.webp" -not -name '*-640.webp' -not -name '*-320.webp' -print0 | xargs -0 -I {} sh -c 'convert -resize "640x>" `echo "$(dirname "{}")/$(basename "{}")  $(dirname "{}")/$(basename "{}" .webp)-640.webp"`'

#WebP  -  width 320px
find ${1}/ -type f -name "*.avif" -not -name '*-640.avif' -not -name '*-320.avif' -print0 | xargs -0 -I {} sh -c 'convert -resize "320x>" `echo "$(dirname "{}")/$(basename "{}" .avif).webp  $(dirname "{}")/$(basename "{}" .avif)-320.avif"`'
$ img_small /path

これで、/pathディレクトリの中の下層にある(○○-320.webpと○○-640.webpを除く)「全て」のWebP画像が変換される。
画像があるディレクトリで実行するなら img_small ./ ね。
file.webpからfile-320.webpfile-640.webpが出力される。もちろん元のfile.webpは残る。
また、元画像の横幅が320pxまたは640px以下の場合はサイズは変換されない。(ようにしたつもり)。
なお、ちょっと手抜きなのでディレクトリ名やファイル名に"_"(アンダーバー)が含まれるとファイル名の出力が失敗して{}-320.webpや{}-640.webpというファイル名になってしまうことがある。(スミマセン)
上の例だとWebP画像しか変換しないがもちろん拡張子を変えることでJpegやPNGにも対応する。
ImageMagickのバージョンによってはAVIF画像の扱いに対応していないかもしれないのでその場合はavifは失敗する。

なお、「がとらぼ」ではブログ用記事ではWordPressのメディアライブラリは使っていなくて/imagesディレクトリを作ってそこに独自のルールで画像ファイルを置いている。この記事はこの構成に対応している。
WordPressのメディアライブラリを使っている場合はこの記事の方法では対応が難しいかも。

まぁ、WordPressを素の状態で普通に使うならこの記事は全然参考にならないということで。

関連記事:
Up