GoogleにSitemap更新を通知するGithubActionsを作成した話
GoogleSearchConsoleへサイトマップを登録しても、更新を自動では確認してくれないので、これを定期的に確認して更新を通知してくれるGithubActions作成しました。
実際に作ったもの
以下がこのサイトで実際に使っている通知用のリポジトリです。
GithubActions の cron を使って、以下の処理を一時間に一度実行させています。
- 指定したURLからサイトマップを所得
- 更新によって追加・削除されたURLがないかを確認
- 更新があった場合は Google に通知を送信
- 更新されたサイトマップを自信のリポジトリへコミット
このサイトは更新頻度が数日~数週間に一回程度なので、頻度はもっと減らしてもいいかもしれません。 まあ、パブリックリポジトリでは GithubActions の使用時間は無制限なので問題ないかなと。
Googleのサイトマップ更新問題
なぜ、これを作成したのかというと、サイトを更新した後に数日経っても新着記事がGoogleへと表示されなかったので調べたことがきっかけでした。 Google Search Console へサイトマップを登録すれば、後は定期的に確認してくれるものだと思っていたのですが、以下の公式のドキュメントを読むと
Google では、サイトがクロールされるたびにサイトマップを確認することはありません。 サイトマップの確認は最初に通知されたときのみであり、その後は変更が Google に通知された場合にのみ確認します。 サイトマップが新規作成された場合、または更新された場合にのみ、Google にサイトマップについて通知してください。
と書いてあるので、サイトを更新するたびに毎回こちらから能動的に通知する必要があるようです。
WordPress では、以下のプラグインを使用すれば更新時に自動で通知してくれるようなので、こういった細かい点のサポートは強いと思わされますね。
Gatsby ではサイトをビルドした後の処理は権限の範囲ではないので、これはユーザーが別途用意する必要がありそうです。
サイトマップのGoogleへの通知方法
公式によると以下の4種類の方法があるとのことです。
- サイトマップ レポートでサイトマップを送信
- ping ツールを使用
- robots.txt にサイトマップへのパスを指定
- WebSub を使用
1はGoogle Search Console で手動で送信する方法です。 毎回手動で操作するのは面倒というレベルではないので除外。
3は『Google は次に robots.txt ファイルをクロールする際に、その変更を検出します。』とあり、自動で検出してくれそうです。 しかし、このサイトでは既に Gatsby のプラグインが robot.txt に自動的に記載してくれていました。 それでも記事を更新してから数日経っても反映されなかったので、ロボットの巡回頻度が低い個人サイトではあまり当てにはできそうにないかなぁという印象。
4は『サイトマップに Atom / RSS を使用しており』と前提があるので xml 形式のサイトマップでは対象外みたいですね。
なので、2の『ping ツールを使用』を採用しました。 これはサイトを更新したら以下の URL に対して GET リクエストを送ればいいというわけです。
https://www.google.com/ping?sitemap=FULL_URL_OF_SITEMAP
手動で送るのは面倒なので、これの処理を GithubActions を使って自動化します。
今回はブラウザで実行する必要がないので、書きやすい Python で書きました。 また、GithubActions で定期実行することを考えて、追加で pip install が必要な外部ライブラリを使わずに標準ライブラリだけで構成することにします。
また、公式の「サイトマップを Google に送信する」には以下のような記述があります。
サイトマップ内のページを更新した場合は、<lastmod> フィールドでマークしてください。
今回の通知機能を実装するために、以下の記事で書いたようにサイトマップへ<lastmod>
タグの追加していたのでした。
サイトのビルド時に通知を行わない理由
このサイトは GithubActions ビルド → Netlify デプロイ という構造なので、このビルド終了時に Google へのサイトマップ通知を同時に行うこともできます。ですが
未変更のサイトマップについて、送信や通知を複数回行わないようにしてください。
と公式ドキュメントにあるので、記事追加のないビルド時に頻繁に送信すると、Google から怒られそうなので、こうして迂遠な方法になりましたWoozy Face Emoji
Pythonで悩んだポイント
xml.etree を使って xml からサイトマップ内の記事の url をリストで所得しようとした時に詰まりました。 xml の名前空間をタグの前につけないと所得できないようです。(面倒くさい)
以下のようなサイトマップから、urlが格納されている<loc>
の中身を全て所得する場合
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
<url>
<loc>https://blog.abarabakuhatsu.com/program/gatsby/use_emotion_react_without_jsx_pragma</loc>
<lastmod>2021-08-14T15:00:00.000Z</lastmod>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
...
</urlset>
以下のような記述では駄目です。
import xml.etree.ElementTree as ET
sitemap = Path('sitemap.xml')
ET.parse(sitemap).iter('loc')
このように、タグ名の前に urlset 内の xmlns の要素をつけてやる必要がありました。
ET.parse(sitemap).iter('{http://www.sitemaps.org/schemas/sitemap/0.9}loc')
GithubActionsの設定
GithubActions での設定方法は以下のような感じになります。
- name: Run notify sitemap updates
run: python notify_sitemap_updates.py
env:
SITEMAP_URL: 'https://foo.com/sitemap/sitemap-0.xml'
SITEMAP_INDEX_URL: 'https://foo.com/sitemap/sitemap-index.xml'
python notify_sitemap_updates.py が os.environ[]
で環境変数を受け取るので、実行時にSITEMAP_URL
とSITEMAP_INDEX_URL
を環境変数として渡してやります。
SITEMAP_URL
は更新を比較するためのURLが含まれているサイトマップ、SITEMAP_INDEX_URL
は Google へと通知されるサイトマップのパスです。
(例ではベタ書きしていますが、記事用にパプリックリポジトリにする予定だったので実際は Actions secrets として設定しています)
このようにしたのは、gatsby-plugin-sitemap で生成されるサイトマップが、実際のURLが記述される sitemap-0.xml と、複数のサイトマップ情報を統合する sitemap-index.xml に分かれる仕様になったからです。
sitemap-index.xml が存在しない場合を考え、SITEMAP_INDEX_URL
が設定されていない場合は、同一のファイルを通知すればいいと判断しSITEMAP_URL
と同じ値を使用しますが、比較対象のSITEMAP_URL
がない場合はエラーで停止するようにしました。
Google への通知が実行されるとThis a commit from Github Actions
というコミットが追加されます。
私は Slack で GitHub のアプリを使って、コミットされると通知を受け取れるようにしています。
完全ではない点
現状の処理だと、比較対象の sitemap-0.xml が一杯になって sitemap-1.xml が自動生成された場合、サイトマップの更新を検知できなくなります。 正確を期すなら、sitemap-index.xml からリンクされているサイトマップのurlを確認して、それら全て所得する処理にするべきでしょう。
ですが Gatsby でサイトマップを生成させている gatsby-plugin-sitemap の、sitemap-0.xml から sitemap-1.xml へと分割される記事数のデフォルト設定は 45000です。 なので、それについては記事を40000件ほど書いた段階で考えればいいと思いました。
ちなみに、このブログのサイトマップに登録されているURLは、現状で記事数が12 + トップページの13件なので、後44987程は余裕がありますね。 (その前にcontentfulの無料枠で使用できるRecord 25000が一杯になります)
おわり
世の中には Gatsby に限らず、静的サイトジェネレーターでサイトを作っている方々が沢山いるので、何か sitemap の通知を簡単に自動化できる便利なサービスがあるのかもしれませんが、どうにも私には見つけられず力業で解決することになりました。 今はこれが精一杯。