1. Home
  2. /
  3. Saltの使い方
  4. /
  5. Hugoでついに外部URLのブログカードを作れるようになった【自作ショートコード】

Hugoでついに外部URLのブログカードを作れるようになった【自作ショートコード】

Hugoでついに外部URLのブログカードを作れるようになった【自作ショートコード】

Hugoでは外部サイトのデータを扱う場合、getJSONgetCSVを使った方法しかありませんでしたが、ついにv0.91でGetRemoteが実装されました。

Release v0.91.0 · gohugoio/hugo

Hugo 0.91.0 is mostly on the boring and technical side. See the list of changes below, but especially note the fix that allows passing falsy arguments to partials with the return keyword (5758c37 #...


Hugo Pipes

Hugo Pipes is Hugo's asset processing set of functions.


GetRemoteの基本

基本の使い方はこんな感じで、html以外にも外部の画像、css、jsといったデータも取得できます。

{{ with resources.GetRemote "https://example.com" }}
    {{ .Content }}
{{ end }}

エラーハンドリング

{{ with resources.GetRemote "https://gohugo.io/images/gohugoio-card-1.png" }}
  {{ with .Err }}
    {{ warnf "%s" . }}
  {{ else }}
    <img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}" alt="">
  {{ end }}
{{ end }}

外部URLブログカードのショートコード

実際のコード

  • unmarshalを使いたかったのですが、多くのサイトでパースエラーが起きてしまうので、正規表現を使うことにしました。

layouts/shortcodes/blog-card.html

{{- $url := (.Get 0) -}}
{{- with try (resources.GetRemote $url) -}}
    {{- with .Err -}}
        {{- warnf "%s" . -}}
    {{- else with .Value -}}
        {{- $result := . -}}
        {{- $title := "" -}}
        {{- $description := "" -}}
        {{- $image := "" -}}
        {{- with $findHead := index (findRE "<head.*?>(.|\n)*?</head>" $result.Content) 0 -}}
            {{- range $meta := findRE "<meta.*?>" (replace $findHead "\n" "") -}}
                {{- $name := replaceRE "<.*name=\"(.*?)\".*>" "$1" $meta -}}
                {{- $property := replaceRE "<.*property=\"(.*?)\".*>" "$1" $meta -}}
                {{- $content := replaceRE "<.*content=\"(.*?)\".*>" "$1" $meta -}}
                {{- if eq $property "og:title" -}}
                    {{- $title = $content -}}
                {{- else if eq $property "og:description" -}}
                    {{- $description = $content -}}
                {{- else if eq $property "og:image" -}}
                    {{- $image = $content -}}
                {{- end -}}
                {{- if and (eq $description "") (eq $name "description") -}}
                    {{- $description = $content -}}
                {{- end -}}
            {{- end -}}
            {{- if eq $title "" -}}
                {{- with index (findRE "<title>(.*?)</title>" $findHead) 0 -}}
                    {{- $title = replaceRE "<title>(.*?)</title>" "$1" . -}}
                {{- end -}}
            {{- end -}}
        {{- end -}}

        {{- $thumbnail_url := "" -}}
        {{- if $image -}}
            {{- with try (resources.GetRemote $image) -}}
                {{- with .Err -}}
                    {{- warnf "%s" . -}}
                {{- else with .Value -}}
                    {{- $thumbnail := . -}}
                    {{- $thumbnail_url = ($thumbnail.Fill (printf "200x200 center q%d webp" $.Site.Params.imageQuality)).Permalink -}}
                {{- else -}}
                    {{- warnf "Unable to get remote resource %q" $url -}}
                {{- end -}}
            {{- end -}}
        {{- else -}}
            {{- $thumbnail := resources.Get $.Site.Params.defaultNoimage -}}
            {{- $thumbnail_url = ($thumbnail.Fill (printf "200x200 center q%d webp" $.Site.Params.imageQuality)).Permalink -}}
        {{- end -}}

        <a href="{{- $url -}}" style="padding: 12px;border: solid 1px #eee;display: flex;text-decoration: none;color: inherit;" onMouseOver="this.style.opacity='0.9'">
            <div style="flex-shrink: 0;">
                <img src="{{- $thumbnail_url -}}" alt="" width="100" height="100">
            </div>
            <div style="margin-left: 10px;">
                <h2 style="margin: 0;padding-bottom: 13px;border: none;font-size: 16px;">
                    {{- $title -}}
                </h2>
                <p style="margin: 0;font-size: 13px;word-break: break-word;display: -webkit-box;-webkit-box-orient: vertical;-webkit-line-clamp: 3;overflow: hidden;">
                    {{- $description | plainify | safeHTML -}}
                </p>
            </div>
        </a>
    {{- else -}}
        {{- warnf "Unable to get remote resource %q" $url -}}
    {{- end -}}
{{- end -}}

使い方

  • 外部のurlを設定するだけ
  • このサイトで使っているHugoブログテーマ「Salt」内でも実装済みです
{{< blog-card "https://hugo-theme-salt.okdyy75.com/" >}}

表示例

エンジニアに関する知識を記録・共有するためのサービス - Qiita

Qiitaは、エンジニアに関する知識を記録・共有するためのサービスです。 プログラミングに関するTips、ノウハウ、メモを簡単に記録 &amp; 公開することができます。


Zenn|エンジニアのための情報共有コミュニティ

Zennはエンジニアが技術・開発についての知見をシェアする場所です。本の販売や、読者からのバッジの受付により対価を受け取ることができます。


はてなブックマーク

はてなブックマークはオンラインにブックマークを無料で保存できるソーシャルブックマークサービスです。みんながブックマークしたインターネット上の旬なニュースや情報が集まります。


Hugoブログテーマ「Salt」

エラー時の表示例

ページが存在しない場合(500系など)

  • その場でエラーメッセージが表示されます
{{< blog-card "https://example.com.invalid/" >}}

ページが見つからない場合(400系など)

  • 何も表示されません
{{< blog-card "https://example.com/test" >}}

metaタグにog:imageがない場合

  • デフォルトのNoImage画像が表示されます
{{< blog-card "https://example.com" >}}

Example Domain


metaタグにog:imageが存在するが、リンク切れを起こしている場合

  • リンク切れで表示されます。(src=""
{{< blog-card "https://hugo-theme-salt.okdyy75.com/test.html" >}}

テストページ

テストページです。

関連記事

Hugo標準のgistショートコードをテーマ内に移行しました

Hugo標準のgistショートコードをテーマ内に移行しました

Hugo公式のgistショートコードはバージョン0.143.0で非推奨となり、将来のリリースで削除されるそうなので Hugo Theme Salt内ののショートコードとして使えるように移行しました

WARN  The "gist" shortcode was deprecated in v0.143.0 and will be removed in a future release. See https://gohugo.io/shortcodes/gist for instructions to create a replacement.

Hugo公式ページでは下記の通り

Gist shortcode

Embed a GitHub Gist in your content using the gist shortcode.


The gist shortcode was deprecated in version 0.143.0 and will be removed in a future release. To continue embedding > GitHub Gists in your content, you’ll need to create a custom shortcode:

Gist ショートコードはバージョン 0.143.0 で非推奨となり、今後のリリースで削除される予定です。コンテンツに GitHub Gist を埋め込むには、カスタムショートコードを作成する必要があります。