HugoでMobile端末向けのSEO対策を実践していく【画像の適正サイズ】

2022-10-31

イントロダクション

コンテンツの更新頻度もさることながら、せっかく書いた記事をみんなに見てほしい気持ちもありブログのSEO対策を進めます。

広告収入も0円の日々が続いており、ブログ運営も赤字になっていることをなんとかしたいところです。 せっかくHugoに移行してメンテや運用費を低減したんですがSEO対策できてなさすぎて…Wordpressで書いてたときよりの1/100ぐらいのアクセス数に落ち込んでます。 更新頻度も下がってますしね…それもあるんでしょう。

最終的には、不労所得が目標のブログ活動ですのでせっせこやっていきましょう。

PageSpeed Insights

SEOの専門家ではないのでチェックツールにかけて改善ポイントを絞っていきます。 今回使用するのは、Googleが提供しているSEOチェックツールです。スコアが出てくれるのが良いですね。 Hugoで作ってるのにモバイル向けがとても遅いのがわかりますね。

PageSpeed Insights

PageSpeed Insights

適切なサイズの画像を配信して、モバイルデータ量を節約し読み込み時間を短縮してください

Properly size images

このサイトに説明があるようなので確認します。

The main strategy for serving appropriately sized images is called "responsive images". With responsive images, 
you generate multiple versions of each image, and then specify which version to use in your HTML or CSS using media queries, viewport dimensions, and so on. See Serve responsive images to learn more.

レスポンシブ画像戦略が一般的な解決方法のようです。 各画像をスマホやPC、タブレットのような端末向けに用意し、クエリやビューポートディメンションという機能を使用して切り替えるのが良いそうです。

もしくは、Image CDNsを活用する方法があります。

画像CDNを使用して画像を最適化する

調べてみるとCDNをやってくれるサイトはいくつかあるのですが、画像の数が増えると結構いい値段しそうなのがわかってきました。 Hugoを使ってるのでImage Processing機能が備わっているのでそちらを使っていきます。

フォルダ構成の修正

これまではブログ記事をpostフォルダ直下に並べていましたが、Image processing機能を使うためには記事ごとにフォルダを作って詰め込む必要があります。

必要な変換作業としてはこんなところです。

  • 記事フォルダの中のMarkdownファイルを走査
  • 各記事ごとにフォルダを作成
  • 作成したフォルダにMarkdownファイルを移動
  • index.mdにRename
  • index.mdから画像リンクを取得し、同一フォルダにコピー

200記事くらいあり、この作業を人力でやるのは無理があるのでスクリプト書いて実行します。

import glob
from pathlib import Path
import os
import shutil
import re

p = r'images/.*.webp'
coverImage='coverImage:.*.webp'
def getImageFile(file):
    f = open(file, 'r', encoding="utf-8")
    imagelist = []
    while True:
        data = f.readline()
        result = re.search(p,data)
        
        if result != None:
            ret = result.group()
            imagepath = ret.split("\" ")[0]
            imagelist.append(imagepath)
        
        result = re.search(coverImage,data)
        if result != None:
            ret = result.group()
            print(ret)
            imagepath = ret.split(": \"")[1]
            print("★",imagepath)
            imagelist.append("images/"+imagepath)       

        if data == '':
            break
            
    return imagelist

contents = glob.glob('*/*.md')
i = 1
base = "post"
for f in contents:
    path = base + str(i)
    Path(path).mkdir(parents=True, exist_ok=True)
    i = i + 1
    bpath = os.path.dirname(f)
    new_path = shutil.move(f, bpath+"/index.md")
    print(new_path)
    imglist = getImageFile(new_path)

    folda = os.path.dirname(f)
    for l in imglist:
        fname = os.path.basename(l)
        print("../"+l, folda+"/"+fname)
        absp = os.path.abspath("../../static/"+l)
        print(str(absp).replace('\\','/'))
        new_path = shutil.copy2(str(absp).replace('\\','/'), folda)

汚いですがこんなのでどうでしょうか。

treeコマンドで移行後のフォルダ構成を並べるとこうなります。 いい感じに移行できたと思います。

フォルダー パスの一覧:  ボリューム ボリューム
ボリューム シリアル番号は 966D-EB9D です
D:.
│  a.txt
│  mdconvert.py
│  
├─post1
│      cropped-Keyboard__1614449645.webp
│      index.md
│      
├─post10
│      2021-03-15-09-44-58.webp
│      2021-03-15-10-02-48.webp
│      2021-03-15-10-06-53.webp
│      2021-03-15-10-08-03.webp
│      2021-03-15-21-56-21.webp
│      index.md
│      
├─post100
│      index.md
│      markdown-img-paste-20211101021008159-600x312.webp
│      markdown-img-paste-20211101021059290.webp
│      markdown-img-paste-20211101021123739-351x600.webp
│      markdown-img-paste-20211101021354944-412x600.webp
│      markdown-img-paste-20211101021431335-364x600.webp
│      markdown-img-paste-20211101021709541-407x600.webp
│      markdown-img-paste-20211101021912912-422x600.webp
│      markdown-img-paste-2021110102220266-423x600.webp
│      markdown-img-paste-20211101022347847.webp
│      markdown-img-paste-20211101022906479-600x420.webp
│      markdown-img-paste-20211101022906479.webp
│      markdown-img-paste-20211101023555532-600x472.webp
│      markdown-img-paste-20211101023918532-600x447.webp
│      
├─post101
│      index.md

画像レスポンシブするためのショートコードの作成

Hugo Markdown Render Hooks 入門

この記事を参考に用意します。 image processing機能を使って複数サイズの画像を用意し、読み出す際にレスポンシブル画像となるように実装すれば良いとのことです。

ShortCodeとして用意してあげます。 これでMarkdownの画像貼り付けを行うとこのショートコードが動作していい感じにしてくれます。

layouts/shortcode/render-image.html

{{ $image := .Page.Resources.GetMatch (printf "%s" (.Destination | safeURL)) }}
{{ $small := $image.Resize "480x png" }}
{{ $medium := $image.Resize "768x png" }}
{{ $large := $image.Resize "1024x png" }}
{{ $xlarge := $image.Resize "1920x png" }}
{{ $alt := .PlainText | safeHTML }}

<picture>
  <source media="(max-width: 480px)" srcset="{{ $small.RelPermalink }} 480w">
  <source media="(max-width: 768px)" srcset="{{ $medium.RelPermalink }} 768w">
  <source media="(max-width: 1024px)" srcset="{{ $large.RelPermalink }} 1024w">
  <img src="{{ $image.RelPermalink }}" alt="{{ $alt }}" decoding="async" loading="lazy">
</picture>

適当な画像を出力したらこのような記載になります。

<picture>
  <source media="(max-width: 480px)" srcset="/blog/jp/post/hugo%E3%81%A7mobile%E7%AB%AF%E6%9C%AB%E5%90%91%E3%81%91%E3%81%AEseo%E5%AF%BE%E7%AD%96%E3%82%92%E5%AE%9F%E8%B7%B5%E3%81%97%E3%81%A6%E3%81%84%E3%81%8F%E7%94%BB%E5%83%8F%E3%81%AE%E9%81%A9%E6%AD%A3%E3%82%B5%E3%82%A4%E3%82%BA/2022-10-31-23-34-50_hu9dbd29f0e468f865739d4a570c239a7f_147375_480x0_resize_box_3.png 480w">
  <source media="(max-width: 768px)" srcset="/blog/jp/post/hugo%E3%81%A7mobile%E7%AB%AF%E6%9C%AB%E5%90%91%E3%81%91%E3%81%AEseo%E5%AF%BE%E7%AD%96%E3%82%92%E5%AE%9F%E8%B7%B5%E3%81%97%E3%81%A6%E3%81%84%E3%81%8F%E7%94%BB%E5%83%8F%E3%81%AE%E9%81%A9%E6%AD%A3%E3%82%B5%E3%82%A4%E3%82%BA/2022-10-31-23-34-50_hu9dbd29f0e468f865739d4a570c239a7f_147375_768x0_resize_box_3.png 768w">
  <source media="(max-width: 1024px)" srcset="/blog/jp/post/hugo%E3%81%A7mobile%E7%AB%AF%E6%9C%AB%E5%90%91%E3%81%91%E3%81%AEseo%E5%AF%BE%E7%AD%96%E3%82%92%E5%AE%9F%E8%B7%B5%E3%81%97%E3%81%A6%E3%81%84%E3%81%8F%E7%94%BB%E5%83%8F%E3%81%AE%E9%81%A9%E6%AD%A3%E3%82%B5%E3%82%A4%E3%82%BA/2022-10-31-23-34-50_hu9dbd29f0e468f865739d4a570c239a7f_147375_1024x0_resize_box_3.png 1024w">
  <img src="/blog/jp/post/hugo%E3%81%A7mobile%E7%AB%AF%E6%9C%AB%E5%90%91%E3%81%91%E3%81%AEseo%E5%AF%BE%E7%AD%96%E3%82%92%E5%AE%9F%E8%B7%B5%E3%81%97%E3%81%A6%E3%81%84%E3%81%8F%E7%94%BB%E5%83%8F%E3%81%AE%E9%81%A9%E6%AD%A3%E3%82%B5%E3%82%A4%E3%82%BA/2022-10-31-23-34-50.png" alt="PageSpeed Insights" decoding="async" loading="lazy">
</picture>

at <$image.Resize>: error calling Resize: image

上のようなShortCodeを書いて実行しようとすると見事にハマったエラーです。

hugo server -D
Start building sites … 
hugo v0.101.0-466fa43c16709b4483689930a4f9ac8add5c9f66 windows/amd64 BuildDate=2022-06-16T07:09:16Z VendorInfo=gohugoio
Error: Error building site: "D:\src\Hugo\withgithubaction\content\post\post141\index.md:1:1": "D:\src\Hugo\withgithubaction\themes\hugo-theme-cleanwhite\layouts\_default\_markup\render-image.html:2:19": execute of template failed: template: _default/_markup/render-image.html:2:19: executing "_default/_markup/render-image.html" at <$image.Resize>: error calling Resize: image "D:\\src\\Hugo\\withgithubaction\\content\\post\\post141\\2022-03-120-94d312ac-600x536.webp": this feature is not available in your current Hugo version, see https://goo.gl/YMrWcn for more information     
Built in 1653 ms

webp画像を使っている場合、拡張版Hugoを使用しないと駄目らしいです。

chocolateを使ってインストールできます。

choco install hugo-extended -confirm

これでレスポンシブ画像になるはずです。

まとめ

Hugoは標準でいろんな機能がついてていいですね。