WWW Watch

AMP (Accelerated Mobile Pages) HTML を出力するようにしてみたけど面倒くさかった話

Movable Type で運用している Blog の記事を AMP (Accelerated Mobile Pages) HTML で出力するようにしてみたけれど、とある自分の書き方の癖により既存の記事を AMP HTML に自動変換するのは結構面倒くさかったという話。

Google が発表したプロジェクト、Accelerated Mobile Pages (AMP) は、モバイル端末における Web ページの表示を高速化する取り組みで、超簡単に言ってしまえば、オープンソースで公開されるフレームワーク、AMP HTML の仕様に従って Web ページを作ってくれれば、その表示を大幅に高速化できますよという仕組み。

単にファイルサイズ的に軽いよというような単純な話だけではなく、キャッシュなど様々な技術を組み合わせることでページのロード時間を大幅に短縮する仕組みで、制作者側が AMP HTML に則ったページを提供し、Google が (Twitter も初期から対応するとのこと) AMP に対応することで、リンク先のページを高速に表示することが可能になります。

Google からの具体的なアナウンスは去年の 10月。下記のリンク先で確認できます。

で、私の会社の Web サイト上で公開しているニュースの各記事ページは、実験的に、この AMP プロジェクトがアナウンスされたすぐ後くらいから AMP HTML 版も出力するようにしてるんですよ (下記参照)。

AMP HTML 版を出力している例

会社のニュースについては、この Blog と同様に Movable Type で運用していますが、元々そんなに長い文章書いているわけでもないし、動画はもちろん、画像もほとんど使っていないし、JavaScript を使った難しい処理などもしていないため、比較的簡単に AMP HTML 版の出力に対応させることができました。

やったことと言えば、img 要素を、AMP HTML の仕様に則り、amp-img 要素に置換したり、iframe 要素を amp-iframe 要素に置換したりといった簡単な処理のみ。

結構簡単だったので、それに味を占め、年末年始のお休みを使って、この Blog も同じように AMP HTML 版を出力するようにしよーっと思ってやってみたら、やっぱり会社の Web サイトに比べて長い記事や画像を多用した記事が多かったり、記事の数も比べものにならないくらい多いこともあって、結構面倒くさかったよというお話。

AMP HTML の仕様について簡単に

まず最初に簡単にですが、AMP HTML の仕様について触れてみます。

基本的には HTML5 文書なのですが、AMP HTML とするためのお約束や、AMP HTML で使用する際に制約 (AMP HTML 要素への変換が必要なものも含め) がある HTML の要素が一部あります。

AMP HTML で使用できない、もしくは使用に際して制約がある要素の一覧 (HTML Tags : ampproject/amphtml より引用) は下記の通り。

HTML の要素 AMP HTML での扱い
script application/ld+json のみ使用可能。ただし、AMP ラインタイム、及び AMP 拡張コンポーネント用のスクリプト読み込みは可能。
base 使用禁止。
img amp-img 使え。
video amp-video 使え。
audio amp-audio 使え。
iframe amp-iframe 使え。
frame 使用禁止。
frameset 使用禁止。
object 使用禁止。
param 使用禁止。
applet 使用禁止。
embed 使用禁止。
form 使用禁止。
input elements input, textarea, select, option 要素、全部禁止。
button 使用可能。
style 必須の opacity プロパティを指定するのに使用可能。その他、amp-custom 属性を付与した style 要素を、head 要素内で 1つ使用可能。
link rel="canonical" は使用可能。rel="stylesheet" による外部スタイルシートの読み込みは原則不可だが、Web fonts 使用のための例外あり。
meta http-equiv 属性の使用は不可。それ以外は使用可能。
a href 属性を javascript: から始まる値で使用するのは不可。もし、target 属性を使用する場合は値は _blank のみ。それ以外は制約なし。
svg SVG 要素はほぼ使用可能。

上記で言及されている要素以外では、HTML5 仕様にある各要素が使用可能です。AMP HTML で使用可能な要素の一覧は下記にあります。

その他、属性では、onclick や onmouseover 属性は使用不可。style 属性も使えません。その辺の詳細は、上の表組みの引用元である下記のページにまとまっていますので目を通しておくとよいでしょう。

AMP HTML のサンプルソース

で、最小構成の AMP HTML は下記のような感じになります。コメントで解説を追記しています。

<!DOCTYPE html> <!-- 文書型宣言は HTML5 に則って -->
<!-- ↓ html 要素に 「⚡」 もしくは 「amp」 属性を付与。XHTML 形式で書くなら amp="amp" -->
<html ⚡ lang="ja" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Sample AMP HTML</title>
    <!-- ↓ link 要素は rel="canonical" のみ。通常版の URL を記述 -->
    <link rel="canonical" href="https://example.com/article/001/">
    <!-- ↓ 必須 -->
    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
    <!-- ↓ 必須 -->
    <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
    <!-- ↓ 必須 -->
    <script async src="https://cdn.ampproject.org/v0.js"></script>
    <!-- ↓ AMP コンポーネント用のスクリプト読み込み例。amp-youtube 用 -->
    <script async custom-element="amp-youtube" src="https://cdn.ampproject.org/v0/amp-youtube-0.1.js"></script>
    <!-- ↓ amp-custom 属性を付けた style 要素で独自に CSS を指定可能 -->
    <style amp-custom>
      h1 {color: red;}
    </style>
    <!-- ↓ application/ld+json は可能なため、JSON-LD で構造化データを記述 -->
    <script type="application/ld+json">
      {
        "@context": "http://schema.org",
        "@type": "Article",
        "name": "Sample AMP HTML",
        "description": "page-description",
        "headline": "page-headline",
        "datePublished": "2015-02-18T12:33:11+09:00",
        "dateModified": "2015-02-18T12:33:11+09:00",
        "mainEntityOfPage": {
          "@type": "WebPage",
          "@id": "https://example.com/article/001/"
        },
        "image": {
          "@type": "ImageObject",
          "url": "https://example.com/img/image.png",
          "width": 1200,
          "height": 900
        },
        "author": {
          "@type": "Person",
          "name": "Author Name"
        },
        "publisher": {
          "@type": "Organization",
          "name": "Organization Name",
          "logo": {
            "@type": "ImageObject",
            "url": "https://example.com/img/logo.png",
            "width": 600,
            "height": 450
          }
        }
      }
    </script>
  </head>
  <body>
    <h1>Sample AMP HTML</h1>
    <p>
      普通のテキストはそのままで大丈夫。
    </p>
    <p>
      <!-- ↓ img 要素は amp-img に変換。width、height 属性は必須 -->
      <amp-img src="sample.jpg" layout="responsive" width="300" height="300" alt="Sample Photo"></amp-img>
    </p>
      <!-- ↓ 例えば広告 (AdSense) も AMP コンポーネントで挿入可能 -->
    <amp-ad width="300" height="250"
            type="adsense"
            data-ad-client="ca-pub-*********"
            data-ad-slot="*********">
    </amp-ad>
  </body>
</html>

何が面倒だったか

AMP HTML の説明はこの辺にしておいて本題。結論から言うと大きく 2点、面倒くさかった点があります。

  1. amp-img 要素は width / height 属性が必須なんだけど、元記事内の img 要素に width / height 属性付けてなかったどうしましょ (自分のせい)
  2. amp-iframe の出現位置に制約があるとかなんなの (AMP HTML の仕様のせい)

自分のせいで面倒くさかった方

まずこの Blog の記事本文内で頻繁に使う HTML 要素としては下記が挙げられます。

  • p 要素
  • img 要素
  • a 要素
  • ul 要素
  • ol 要素
  • li 要素
  • dl, dt, dd 各要素
  • pre 要素
  • code 要素
  • blockquote 要素
  • iframe 要素 (YouTube の動画を埋め込んだりする時)
  • その他、span, strong, br 各要素あたり

Web の技術的な内容を書くことが多いので、pre とか code の使用頻度が高いですが、それ以外は恐らく、世間の多くの Blog と同じような感じだと思います。

で、この Blog で AMP HTML 版を出力する際に下記の 2点が問題でした。

  1. img 要素に width / height 属性を指定していなかった。
  2. 一部古い記事で本文内に script 要素など、AMP HTML で使えない要素が記述されていた。

2つ目の問題についてはもうどうしようもないので、任意でフラグ立てた記事のみ AMP HTML 版を出力するように実装し、全記事を一律で AMP 対応させるのは諦め。元々ほぼアクセスのない古い記事だけだったので AMP 対応させる必要もないと判断して、特に気にせずでいくことに。

1つ目の方が問題で、これが原因で AMP 用の記事テンプレートを新規で作って再構築すれば終わりっていうのができず、AMP 版を出力する記事の本文内にある img 要素に width / height 属性を全部指定し直すハメになりました。解説記事とかで画像が多いと死ねる感じに。

なお、通常 Movable Type でも WordPress でも、記事編集画面の UI から画像をアップロードして img 要素を挿入すれば自動で width / height 属性が付与されるので、通常は気にする必要ないと思います。

私の場合、記事を Movable Type 上では書かないこともあって、画像はサーバに直でアップロードして、本文には img 要素を手書き (手書きって言っても定型文ですぐ入力できるようにしてあるんですが) するのと、あとで画像を差し替えたりするときに楽なように width / height 属性は基本付けないのでこうなりましたと。

前述したとおり、AMP HTML の仕様のせいとかではなく、自分の HTML の書き方の癖が原因というオチなんですけどね......

逆に言えば、img に width / height 属性を付与していないみたいな問題がないのであれば、あとは img 要素と iframe 要素をそれぞれ、amp-img / amp-iframe 要素に変換してあげれば、ほとんどの Blog で AMP HTML への対応はできるんじゃないかなと。

この Blog の場合もありましたが、script 要素みたいに AMP HTML では使用不可の要素が本文内に混ざっていないかは要確認です。Flash コンテンツの埋め込みによく使われる object, embed, param 要素あたりも AMP HTML では使えないので注意しましょう。

特に長いこと運用されている Blog は古い記事にそういうのが残ってるケースがあるので全記事をまとめて AMP 対応させたい場合は要注意です。

ちなみに、最近はレスポンシブ Web デザインの都合上、img に CSS で max-width: 100%; みたいな指定をしている場合があると思いますが、width / height 属性を付与した場合は、height: auto; みたいに高さの方も何らか指定しておかないと画像が縮小されたときに縦サイズだけ height 属性値のままになりますので注意が必要です。

img / iframe 要素の変換

具体的に Movable Type で記事本文内にある img / iframe 要素を AMP HTML に変換するなら、下記のような方法でできます。

<mt:Ignore>regex_replace モディファイア用の変換リストをセット</mt:Ignore>
<mt:SetVarBlock name="regex1a">/<img(.*)? \/>/g</mt:SetVarBlock>
<mt:SetVarBlock name="regex1b"><amp-img layout="responsive"\1></amp-img></mt:SetVarBlock>
<mt:SetVarBlock name="regex2a">/<iframe(.*)?><\/iframe>/g</mt:SetVarBlock>
<mt:SetVarBlock name="regex2b"><amp-iframe layout="responsive" sandbox="allow-scripts allow-popups allow-same-origin allow-top-navigation"\1></amp-iframe></mt:SetVarBlock>
 
<mt:Ignore>EntryBody、EntryMore に regex_replace モディファイアを指定して変換</mt:Ignore>
<$mt:EntryBody regex_replace="$regex1a","$regex1b" regex_replace="$regex2a","$regex2b"$>
<$mt:EntryMore regex_replace="$regex1a","$regex1b" regex_replace="$regex2a","$regex2b"$>

上記はなるべく簡単な書き方にとどめていますので、実際には調整が必要かも。

例えば、layout="responsive" を付与された画像は、画面サイズに応じてサイズが可変になりますが、サイズを固定したい画像には付かないようにしないといけません。

また、amp-iframe 要素に指定されている sandbox 属性値のうち、allow-same-origin は同一オリジンのリソース (簡単に言えば自サイト上にあるファイル) を読み込む際に付与されているとエラーになりますから、src 属性値などからもう少し細かい条件分岐をしてあげる必要があるかもしれません。

AMP HTML の仕様のせいで面倒くさかった方

amp-iframe 要素の仕様に下記のような記述があるんですよね。

amp-iframe may not appear close to the top of the document (except for click-to-play iframes as described below). They must be either 600px away from the top or not within the first 75% of the viewport when scrolled to the top - whichever is smaller. NOTE: We are currently looking for feedback as to how well this restriction works in practice.

amphtml/amp-iframe : ampproject/amphtml から引用

(Click-To-Play iframe を除いて) amp-iframe は ViewPort の上端から一定距離以上、下に離れた場所にしか表示できない。一定距離って言うのは、600px、もしくは ViewPort の縦サイズの 75% (例えば ViewPort の縦サイズが 667px だった場合、約 500px) に該当する数値のいずれか小さい方。

なので、ViewPort の縦サイズが 667px の iPhone 6 の場合、amp-iframe 要素の出現位置が、Web ページの最上部から 500px 以上離れていないと駄目ってことになります。

実は記事の内容がほぼ、スライド (SlideShare から埋め込み) だけっていうページがあって、それを AMP HTML で出力してデカイ画面で見ていた時にエラーがでて気がついたんですけども、ページの構成によっては注意が必要かもしれません。

AMP HTML のバリデーションは開発者ツールで

出力した AMP HTML が正しいかについては、該当ページの URL 末尾に #development=1 を付与した上で、Chrome や Firefox の開発者ツールを開くと AMP HTML のランタイムを通じてバリデーションを実行し、コンソールに出力してくれます。

下は Firefox での実行例 (エラーなし) ですが、問題があると、エラー表示が出ます。AMP HTML を出力したら、エラーがないか確認しておきましょう。

AMP HTML を Firefox の開発者ツールで検証した例

その他、気がついた点

仕様上は特に言及がないにもかかわらず、バリデーターを通すとエラーになるものとして、

  • blockquote 要素の cite 属性
  • q 要素の cite 属性

がありました。問題ないはずなんだけどなぜかエラーが出るので、仕方ないから AMP HTML 版を書き出す際には cite 属性だけ削除するようにしました。AMP ラインタイムの問題な気もしますが面倒くさいから調べてません。

広告表示やアクセス解析

AMP HTML でもコンポーネントによって実現可能です。両方とも標準のコンポーネントなので AMP HTML を書くだけ。

AdSense

例えば、広告表示用の amp-ad 要素の仕様 を見てみると、AdSense なら下記のような記述で広告が表示されます。

<amp-ad width="300" height="250"
        type="adsense"
        data-ad-client="ca-pub-********"
        data-ad-slot="********">
</amp-ad>

当たり前ですが、data-ad-clientdata-ad-slot の値は、自分の広告コードに合わせます。

Google Analytics

Google Analytics のトラッキングコードを設置したい場合は、amp-pixel 要素を使用します。仕様は下記。

JavaScript が使えないので、Measurement Protocol を使用してデータを送る手法です。

エンドポイントの URL、及び必須のパラメータは下記の通りになります。それぞれ Measurement Protocol のリファレンスから引用します。

エンドポイント URL

  • https://ssl.google-analytics.com/collect

必須パラメータ

名前 パラメータ 説明
プロトコルのバージョン v v=1 プロトコルのバージョンです。この値は 1 にする必要があります。
トラッキング ID tid tid=UA-123456-1 データの送り先の Google アナリティクス プロパティを識別するための ID です。
クライアント ID cid cid=xxxxx 個々のユーザーに固有の ID です。
ヒットタイプ t t=pageview 個々のユーザーについて収集された操作の種類です。

上記を踏まえて、最低限必要なパラメータを付与した amp-pixel 要素のソースコードは下記のようになります。

<amp-pixel src="https://ssl.google-analytics.com/collect?v=1&amp;tid=UA-00000000-1&amp;t=pageview&amp;cid=****"></amp-pixel>

tid=UA-00000000-1 の部分は自分のプロパティ ID を反映します。

cid=**** のところだけどうしましょうってなると思いますが、AMP HTML URL Variable Substitutions に amp-pixel で使用可能な変数のリファレンスがありますので、そこを見ると、$RANDOM という変数があります。これを使って、

<amp-pixel src="https://ssl.google-analytics.com/collect?v=1&amp;tid=UA-00000000-1&amp;t=pageview&amp;cid=$RANDOM"></amp-pixel>

とすれば、必須パラメータはそろいますね。

その他、キャッシュの無効化 を指定する z=**** も Google のドキュメントでは送っておいた方がよいとのことですので、ここにも $RANDOM 変数を使って、下記のように足してみます。

<amp-pixel src="https://ssl.google-analytics.com/collect?v=1&amp;tid=UA-00000000-1&amp;t=pageview&amp;cid=$RANDOM&amp;z=$RANDOM"></amp-pixel>

これが最小構成になるかと。このソースを body 要素の閉じタグ直前などに入れておけばよいと思います。

その他、変数には下記のようなものもあります。

  • $CANONICAL_URL
  • $CANONICAL_HOST
  • $CANONICAL_PATH
  • $TITLE

Measurement Protocol のパラメータで対応するのは、それぞれ下記のような感じになると思いますので、必要に応じて指定してあげればよいかと。

例えば、dl=$CANONICAL_URLdt=$TITLE を足すと下記のようになります。

<amp-pixel src="https://ssl.google-analytics.com/collect?v=1&amp;tid=UA-00000000-1&amp;t=pageview&amp;cid=$RANDOM&amp;dl=$CANONICAL_URL&amp;dt=$TITLE&amp;z=$RANDOM"></amp-pixel>

amp-analytics コンポーネントを使用した AMP HTML への Google Analytics 導入方法について下記に新しく記事を書いていますので参考まで。

その他 コンポーネント色々

AMP HTML Components のページにまとまっていますので必要に応じて使ってみるとよいと思います。

まとめ

通常の HTML と、AMP HTML を別々に管理できる場合は全く問題ないんですが、この Blog 等でやっているように、通常版の記事を書いたら、自動的に AMP HTML 版も出力したいという場合は、要素の変換などを含めて少し面倒なことがありますね。

WordPress 向けには AMP HTML を出力するプラグインも公開されていますので、AMP HTML がより一般化すると、ツールも色々そろってきて、より便利になるかもしれません。

関連エントリー

Recent Entry

全ての記事一覧を見る

Hot Entry

逆引きおすすめエントリー