読者です 読者をやめる 読者になる 読者になる

hitode909の日記

趣味はマリンスポーツですの日記です

はてなブログのAMP対応で学ぶウェブサービスのAMP対応

プレゼンモード
再生
← / →で移動
fでフルスクリーン
escでおわる

こんにちは,id:hitode909です.このあと14時から品川のマイクロソフト様のオフィスでおこなわれている,YAP(PはパチモンのP)Cで発表します.
この記事では,発表資料を公開いたします.現地の方は今すぐCルームに来てください.そうでないかたは懇親会でお会いしましょう.
はてなブログのトピックもあるようです.




はてなブログのAMP対応で学ぶウェブサービスのAMP対応

2016/07/03 YAP(achimon)C::Asia Hachioji 2016 mid in Shinagawa
hitode909

自己紹介

  • id:hitode909 @hitode909
  • 京都から来ました
  • はてなではてなブログを作っている

f:id:hitode909:20160703112306p:plain

自己紹介

YAPC 2015でベストトーク賞いただきました
f:id:hitode909:20160628083039p:plain
YAPC::Asia Tokyo 2015 | 写真共有サービス 30days Album

副賞のXboxをオフィスに導入しました

www.instagram.com

この発表について

話したいこと

はてなブログのAMP対応の事例を紹介しつつAMP対応のノウハウを共有したい

話題

  • AMPの紹介
  • 素朴なウェブサービスのAMP対応
    • Quyoの話
  • 複雑なウェブサービスのAMP対応
    • はてなブログの話

みなさま

  • ウェブサービス作ってる人?
  • HTML書いてる人?
  • AMP知ってる人?
  • AMP書いたことある人?
  • AMP書いて本番投入した人?
  • AMP Projectにコントリビュートしている人?

AMP(Accelerated Mobile Pages)の紹介

www.ampproject.org

AMPはどこに表示されるか

スマホ版のGoogleの検索結果

f:id:hitode909:20160629125656p:plain

はてなブックマークのスマホアプリ


左:AMP対応ページ・右:AMP非対応ページ

http://bookmark.hatenastaff.com/entry/2016/06/23/155916

TwitterアプリのMoments

AMPの構成要素

  • AMP HTML
    • HTMLのサブセット
    • これを作ると配信してくれる
    • 高速に表示するための工夫
  • AMP JS
    • AMP HTMLをレンダリングするためのランタイム
  • Google AMP Cache
    • AMP HTMLをキャッシュしてCDN経由で配信する


HTML6 でも CSS4 でもない Web 技術のゆくえ // Speaker Deck

AMP HTML

  • 高速にレンダリングできるよう工夫されたHTMLのサブセット
  • リフロー・リペイントを防ぐ仕組み
  • 使える要素や属性を制限
    • 自由なscriptは書けない
    • styleは1箇所にまとめる
    • 自由なwebfontは使えない
  • AMP用の要素(コンポーネント)が用意されている
    • imgのかわりにimg-srcを使う,など

AMPは静的コンテンツに向いている

向いている

  • アクセスして読む
  • ニュースやブログの配信

向いてない

  • その場で何かできるのは難しい
  • チャットや掲示板
  • ブログを書く機能

AMPの紹介おわり

素朴なウェブサービスで練習

  • ためしにやってみましょう
  • ラボサービスのQuyoで実験
    • 捨てた物の思い出を投稿できるサービス

quyo.hatelabo.jp

QuyoはAMP対応しやすい

  • テキストと,写真1点を投稿できるサービス
  • リンクも貼れない
  • ほぼ固定のテンプレートに,本文を流しこめば完成

AMP対応の手順

簡単5ステップ

  1. エンドポイントを作る
  2. HTMLからlinkタグを設定
  3. テンプレートを作る
  4. メタデータを埋める
  5. CSSを作って完成

1.エンドポイントを作る

  • /amp とかなんか適当に決めましょう
  • /ampとか,?ampとか,?amp=1とか,区別できればなんでもいい
  • 普段はCDNから配信されるので,URL直接打たない限りは表には出ない

2.HTMLからlinkタグを設定

  • クローラ向けに,このページにはAMP版があるということを教える
<link rel="amphtml" href="http://quyo.hatelabo.jp/items/5476d8e33531320002220000/amp">

3.テンプレートを作る

  • 記事ページだけ作る
  • 普段はCDNから配信されるので,ログインの仕組みは廃止して,記事を読めるだけ
  • HTMLタグをかっこよくする
<html ⚡>

もしくは

<html amp>
  • AMP JSをロード
<script async src="https://cdn.ampproject.org/v0.js"></script>
  • HTML版のテンプレートからコピペしていらないところ消して完成

4.メタデータを埋める

  • クローラ用にJSONを埋めておく
  • 媒体名やロゴなど 検索結果のカルーセルに出てくる

f:id:hitode909:20160702094056p:plain

<script type="application/ld+json">
  {
    "@context": "http://schema.org",
    "@type": "NewsArticle",
    "mainEntityOfPage": "http://quyo.hatelabo.jp/items/54769e6f31663000020b0000",
    "headline": "発売と同時に購入して,2週間くらい使ってた",
    "datePublished": "2014-11-27T03:45:51+00:00",
    "dateModified": "2014-11-27T03:45:51+00:00",
    "description": "発売と同時に購入して,2週間くらい使ってたのだけど,やっぱりケース付けない派なので友達にあげた その後さらに人に渡ったようで,いま誰が使ってるんだろ",
    "author": {
      "@type": "Person",
      "name": "hitode909"
    },
    "publisher": {
      "@type": "Organization",
      "name": "Quyo",
      "logo": {
        "@type": "ImageObject",
        "url": "http://quyo.hatelabo.jp/images/amp-logo.png",
        "width": 163,
        "height": 60
      }
    },
    "image": {
      "@type": "ImageObject",
      "url": "http://instagram.com/p/nUgAQeRGie/media/?size=l",
      "height": 640,
      "width": 640
    }
  }
</script>

5.CSSを作る

  • 必要そうなところをコピペ
  • 使えないスタイルあるので注意
    • インラインなスタイルは使えない
    • !importantは使えない
    • 50KBまで
    • ユニバーサルセレクタ(*)は使えない
    • Accelerated Mobile Pages Project
  • linkタグではなく,styleタグ内に直接展開する
<style amp-custom>ここにスタイルを全部書く</style>

完成

  • 1時間半くらいで完成

12:55

Quyoやりませんかとか話す

15:29

リリース

リリースしたら

  • Googlebotにクロールされるのを待つ
  • Search ConsoleからAMPのクロール数やエラーを見れる

エラー出てる様子


エラー直した様子

開発中に気をつけること

  1. バリデーションを通す
  2. 画像のサイズをサーバーサイドで調べる

1. バリデーションを通す

  • AMP HTMLとしてvalidじゃないと,CDNから配信してもらえない
    • 検索結果にも出ない
  • 本番環境にデプロイしなくても,localhostで起動したサーバーをブラウザで開いて確認できる
  • #development=1をつけるとAMPとしてvalidかバリデーションをおこなってくれる

エラーが出ている

f:id:hitode909:20160628105558p:plain

直すとバリデーション通る

jQueryとかロードしていたのをやめた
f:id:hitode909:20160628105613p:plain

2. 画像のサイズをサーバーサイドで調べる

<amp-img src="http://instagram.com/p/nUgAQeRGie/media/?size=l" layout="responsive" width="640" height="640" />

作戦

  • AMP HTMLを配信するさいに,サーバーサイドで画像を取得
  • テンプレートに@item_widthと@item_heightを渡す
  • QuyoはRubyとSinatraを使っているので,fastimageを利用
  • 画像は1記事に1つだけなので同期的に取得
@item_width, @item_height = *FastImage.size(@item.image_url)

QuyoをAMP対応してわかったこと

  • ふだんのウェブサービス開発と近いフローで進められる
  • 画像のサイズをサーバーサイドで埋めるのが手間
    • 記事内に画像をたくさん貼れると不穏な予感がする

素朴なウェブサービスのAMP対応

おわり

もうちょっと複雑なウェブサービスのAMP対応の事例

はてなブログの事例のご紹介

はてなブログについて

  • 2011年夏から開発しているブログサービス
  • もうすぐ5周年

HTMLなんでも書ける

  • 本文に任意のタグを書ける
  • scriptも書ける
  • インラインなstyleも書ける

はてなブログをAMPに対応したい

Quyoとの比較

Quyoの場合

  • 本文はテキストだけ
    • 各行をpで挟んで配信すればよい
  • 画像のURLを入力する欄がある
    • 指定したURLの画像を取得してamp-imgに入れればよい

f:id:hitode909:20160628145434p:plain

はてなブログの場合

  • 本文にはなんでも書ける
    • 使えない要素や属性は取り除く必要がある
  • 画像は本文中にimgタグとなって登場する
    • imgを探してamp-imgに書き換える必要がある
  • 本文にtwitterとかinstagramとか貼れる
    • twitterやinstagramを貼ってそうなところを探してamp-twitterやamp-instagramに書き換えたい

f:id:hitode909:20160628150017p:plain

先に完成品

f:id:hitode909:20160629120747p:plain

対応している

  • いろんなサイズの画像がちゃんともとのサイズで出ている
  • インラインのスタイルを書いても,AMP版では消してくれる
  • 動画や音楽を貼れている
  • ブログカード(他の記事への言及)も出ている

対応していない

  • ログイン機能はなくて,読めるだけ
  • コメントを書いたりスターをつけたりできない

本文置き換え作戦

  • 記事本文のHTMLを渡すとAMP HTMLに変換して返してくれるコンバータを作る
  • 書き換えポリシーをあらわすクラスをいろいろと実装

本当はもっといっぱいある

  • ImgToAmpImg
    • imgをamp-imgに変換
  • AmpTwitter
    • twitter貼り付けを探してamp-twitterに変換する
  • AmpYouTube
    • YouTube貼り付けを探してamp-youtubeに変換する
  • AmpSoundCloud
    • SoundCloud貼り付けをamp-soundcloudに変換する
  • ValidationRules
    • AMPバリデータのバリデーションルールを使ってHTMLを正規化する

imgをamp-imgに変換

  • DOMツリーを作ってimgを探して置き換える
  • 画像のサイズは,画像のトリミングなどを行っている社内マイクロサービスに調べさせる
  • GET /api/dimensions?url=http://***.jpg&api_key=*** でJSONが返ってくる
  • 結果はマイクロサービスとの間のプロキシでキャッシュさせる
  • 画像を取得して解析するのは重い処理なので,変な画像を解析しようとして巻き込まれて一緒に落ちるのを防ぐ

twitter貼り付けをamp-twitterに変換する

  • 構造が複雑


before

<blockquote class="twitter-tweet" data-lang="ja">
  <p lang="ja" dir="ltr">早く鴨川ビールしないと暖かくなってしまう!!!!!</p>&mdash; 趣味はマリンスポーツです (@hitode909) 
  <a href="https://twitter.com/hitode909/status/710100679805644800">2016年3月16日</a>
</blockquote>

after

<amp-twitter width=486 height=657 layout="responsive" data-tweetid="710100679805644800" data-cards="hidden">
  <blockquote class="twitter-tweet" data-lang="ja" placeholder="">
    <div lang="ja" dir="ltr" class="paragraph">早く鴨川ビールしないと暖かくなってしまう!!!!!</div>&mdash; 趣味はマリンスポーツです (@hitode909) 
    <a href="https://twitter.com/hitode909/status/710100679805644800">2016年3月16日</a>
  </blockquote>
</amp-twitter>

やること

  • ヒューリスティックにtwitter貼ってそうなところを探している
  • twitter-tweetクラスのついたblockquote中のaを探してtweet idを抜き出す
  • data-tweetidつきで全体をamp-twitterで囲む

なんとなくできてきた

  • なんとなくできてきたので,実際にクロールさせて様子を見たい
  • スタッフ限定機能としてリリースしてクロールさせてみる
  • 社内でフィードバックをもらって,ちまちま直すけど,きりがない

AMPバリデータのバリデーションルールを使ってHTMLを正規化する

  • 「禁止されている属性: style」みたいなエラーがたくさん
  • 「禁止されている属性: stye」みたいなtypoもエラー
  • きりがないので,AMPバリデータと同じ情報源を使ってバリデーションしたい

AMPバリデータを読む

AMPバリデータのバリデーションルールを使ってHTMLを正規化する

  • validator-main.protoasciiを見て,入力されたinvalidかもしれないAMP HTMLをvalidなAMP HTMLに変換する
  • 許可されていない要素は消す
    • fontはバリデーションルールに載ってないので消す
  • 許可されていない属性は消すか,デフォルト値に直す
    • amp-imgはsrcを持てるけど,divはsrcを持てない
    • aのtarget=_parentは許可されていないので,_blankに書き換える

amp-imgの定義

tags: {  # <amp-img>
  tag_name: "AMP-IMG"
  # <amp-img> is whitelisted for <amp-sidebar>, otherwise:
  # disallowed_ancestor: "AMP-SIDEBAR"
  attrs: { name: "alt" }
  attrs: { name: "attribution" }
  attrs: { name: "placeholder" }
  attr_lists: "extended-amp-global"
  attr_lists: "mandatory-src-or-srcset"
  spec_url: "https://www.ampproject.org/docs/reference/amp-img.html"
  amp_layout: {
    supported_layouts: FILL
    supported_layouts: FIXED
    supported_layouts: FIXED_HEIGHT
    supported_layouts: FLEX_ITEM
    supported_layouts: NODISPLAY
    supported_layouts: RESPONSIVE
  }
}

なにがよいか

  • AMP HTMLの仕様そのものを手で実装すると,AMPバリデータの変更をアプリケーション側で都度再現することになる
  • 「fontタグは使えない」といった条件はアプリケーション本体には登場せず,validator-main.protoasciiに書かれているだけ
  • 将来的にfontタグを使えるようになったら,validator-main.protoasciiを更新すれば追従できる

バリデータとの違い

  • バリデータは,validかどうかを判定する
  • これは,invalidなところを消したり直したりする
    • invalidだと分かったらなんとかしてvaidに直そうとする

だいたいできた

  • ちょっとずつ作ってちょっとずつデプロイ
  • デプロイする前に定量的に様子を見たい

デプロイする前に様子を見たい

  • クロールを待ってSearch Consoleを見るのは時間がかかる
  • 手元で起動して,まとめてバリデーションかけて様子を見たい
  • 指定したURLをクロールして順番にバリデーションをかけてvalid率をレポートしてくれるツールを作ることにした

amp-error-collector

  • Rubyで書かれたコマンドラインツール
  • sitemap.xmlを指定して実行
  • sitemap.xmlに載ってるURLを全て見て,AMPを探す
  • AMPのページを全てバリデーションして,結果をMarkdownで出力する

github.com

実行

  • sitemap.xmlのURLを指定して実行
    • 開発中はlocalhostで起動したアプリケーション向きに実行する
% bundle exec -- ruby collect-errors.rb http://tsukurioki.hatenablog.com/sitemap.xml
Error Report of http://tsukurioki.hatenablog.com/sitemap.xml
I, [2016-06-23T15:10:27.721369 #79725]  INFO -- : validate http://tsukurioki.hatenablog.com/entry/2016/06/20/090000?amp=1
I, [2016-06-23T15:10:27.924578 #79725]  INFO -- : validate http://tsukurioki.hatenablog.com/entry/2016/06/20/033324?amp=1
I, [2016-06-23T15:10:27.934522 #79725]  INFO -- : validate http://tsukurioki.hatenablog.com/entry/2016/06/22/092016?amp=1
I, [2016-06-23T15:10:28.086394 #79725]  INFO -- : validate http://tsukurioki.hatenablog.com/entry/2016/06/19/095649?amp=1
I, [2016-06-23T15:10:30.955057 #79725]  INFO -- : success
I, [2016-06-23T15:10:31.060164 #79725]  INFO -- : validate http://tsukurioki.hatenablog.com/entry/2016/06/19/005512?amp=1
I, [2016-06-23T15:10:33.135304 #79725]  INFO -- : success
I, [2016-06-23T15:10:33.227255 #79725]  INFO -- : success
I, [2016-06-23T15:10:33.247395 #79725]  INFO -- : validate http://tsukurioki.hatenablog.com/entry/2016/06/18/213528?amp=1
I, [2016-06-23T15:10:33.287214 #79725]  INFO -- : success
I, [2016-06-23T15:10:33.352698 #79725]  INFO -- : validate http://tsukurioki.hatenablog.com/entry/2016/06/17/082458?amp=1
I, [2016-06-23T15:10:33.406264 #79725]  INFO -- : validate http://tsukurioki.hatenablog.com/entry/2016/06/13/234510?amp=1

待つこと数分

Error Report of http://tsukurioki.hatenablog.com/sitemap.xml

# Errors
- http://tsukurioki.hatenablog.com/entry/2016/05/16/010134?amp=1
 - `94:4` Invalid URL protocol 'chrome:' for attribute 'href' in tag 'a'.

# Result
308 / 309 = 99% success

----
Generated by [hitode909/amp-error-collector](https://github.com/hitode909/amp-error-collector)

便利

  • コンバーターを変更したときには,このツールでリグレッションテストできる
  • 定期的に実行すれば日々の監視にも使えそう
    • ウェイトなしで並列に実行するので,他人に向けて実行してはいけない

はてなブログをAMP対応してわかったこと

  • 複雑なことをしても,バリデータなど,開発を進めるためのツールは揃っているのでストレスなく進められる
  • オープンソースなので,既存のツールで足りないときは,サイト全体をまとめてバリデーションするやつとか作れる

リリース

  • 企業用のはてなブログMedia向けにリリース

business.hatenastaff.com

  • はてなブログPro向けにベータリリース

staff.hatenablog.com

編集画面でのプレビュー機能

staff.hatenablog.com

はてなでの他のAMP対応

はてなブックマークのアプリでもAMP版が表示される

  • 速くて便利

bookmark.hatenastaff.com
bookmark.hatenastaff.com

匿名ダイアリーもAMPに対応

  • 高速に読める

labo.hatenastaff.com

日々の暮らし

こういうことをやっています

Search Consoleを眺める

  • エラー出てないか見る

Chrome拡張をインストールする

  • 見てるページのAMP版があるか教えてくれる
  • AMPを開いてバリデーションできる

chrome.google.com

AMPの最新情報をチェックする

GitHubを眺める
AMPの公式のWordPress
ampタグのついたブックマークを眺める

複雑なウェブサービスのAMP対応

はてなブログのように複雑なアプリケーションでもAMP対応できる

まとめ

  • AMPの紹介
  • 素朴なウェブサービスのAMP対応
    • Quyoの話
  • 複雑なウェブサービスのAMP対応
    • はてなブログの話

おまけ

このプレゼンツールについて

はてなブログでプレゼン

  • 見出しごとにスライド化
  • ちょっとJSを書いて記事をプレゼンに
  • #presentation でプレゼン開始
  • fでフルスクリーン

はてなブログの便利機能からそのままプレゼン

  • リンクそのまま押せる
  • 他人のSpeakerDeckをそのままiframeで引用
  • 画像をペーストでfotolifeにアップロード
  • シンタックスハイライト可能
  • 予約投稿して自動ツイート
  • スマホでお手元で閲覧可能
  • 右上の<progress>要素で進捗チェック

はてなグループには2006年から

10年前!!
hatena.g.hatena.ne.jp

まとめ

  • AMPの紹介
  • 素朴なウェブサービスのAMP対応
    • Quyoの話
  • 複雑なウェブサービスのAMP対応
    • はてなブログの話

反応