"note"がAngularJSでどうやってSEO, Open Graphの対応をしているか
noteはAngularJSを使って、ブラウザで描写している
この"note"というサービスはAngularJSを使って、JavaScript側(つまりブラウザ側)でノートの中身を公開しているみたいなのです。
それはブラウザでソースを見てみるとわかります。
例えばhttps://note.mu/sadaaki/n/nd921f3f7c635のノートのソースをChromeで見てみると・・・
まずhtmlタグにng-appのプロパティがついています。これはAngularJSを使うならば必要なプロパティで、ここからnoteはAngularJSを使っているんだなあと分かります。
AngularJSは簡単に言うと、サーバー側ではなくブラウザ側でHTMLを描写する仕組みです。これを利用するとどんな利点があるのか。僕の理解している範囲内だと、サーバー側はベースとなるHTML(上のソース)と各ノートのJSONを返せばいいだけなので負荷がかかりにくくなります。また、AJAXで通信し、HTMLを描写したい場合でも、サーバー側とJavaScript側両方でテンプレートをもつ必要がなくなりコードがすっきりするのです。
実際にソースを見てみましょう。
まず、titleタグは「note ――つくる、つながる、とどける。」となっており、ノート本来のタイトル「noteをはじめて3日目」ではありません。
更に、og:title, og:urlは「{{ note_ogp_title }}」とAngularJSのテンプレートになっており、実際に記述するべき内容ではありません。
また、ノートの本文もHTML内には記述されていません。
しかし、これがブラウザでJavaScriptが実行されると、次のようになります。
(これはChromeのdeveloper toolで確認したものです)
画像を見るとわかるように、titleタグにはちゃんと「noteをはじめて3日目 | 加藤貞顕 | note」と入り、og:title, og:urlにも必要な情報が入っています。
本文ももちろんHTMLとして描写されています。
ノートの情報は https://note.mu/api/v1/notes/nd921f3f7c635 ここからJSONのAPIを通じて取得しているみたいですね。
これがAngularJSの仕組みで、サーバー側が返すHTMLにはノートの情報は描写されていないけど、JavaScriptを使ってブラウザ側でHTMLを描写する、ということです。
問題はSEO
しかし、これには問題があります。
何が問題なのかというと、JavaScriptでノートのHTMLを描写しているため、SEOにすごく弱いのです。
Googleはインターネット上にあるページの情報を集めるときには、基本的にはただHTMLを取得し、評価するだけで、その中にあるJSを実行しません。この"note"に投稿されているノートも、AngularJSで使われているベースのHTMLが評価されるだけなのです。
つまり、タイトルの情報も本文の情報も、基本的にはGoogleに伝わっていないことになります。
SEOの解決法
しかし、この"note"がSEOに対して全く何もしていないのかというとそうではありません。
escaped_fragmentという仕組みがあり、"note"ではそれを利用しています。
ノートのソースには次のタグが含まれています。
<meta content="!" name="fragment">
これが何を意味するのかというと、すごく簡単にいえば、「サーバー側でJavaScriptを使って描写したHTMLがあるから、このページの情報を取得するときはそっちを見てね」というタグです。
詳細はhttps://support.google.com/webmasters/answer/174992?hl=jaここらへんを見ると良いでしょう。
じゃあ実際にどうやったらそのHTMLを取得できるのか気になりますよね。それには「?_escaped_fragment_=」というクエリをつけると見ることができます。("note"では、UserAgentをGoogleのbotにする必要があります)
実際にコマンドラインでcurlを使って取得するときは下のコマンドになります。
curl --header "user-agent:Mozilla/5.0(compatible;Googlebot/2.1;+http://www.google.com/bot.html)" "https://note.mu/sadaaki/n/n78107c38a0fc?_escaped_fragment_="
このように、"note"ではAngularJSをサーバー側で実行した結果のHTMLをGoogleに返しています。
これで解決と思ったのですが・・・
"note"はちゃんとSEOにも対応してるし問題ない!と思ったのですが、実際には正しく描写できていない気がします・・・
これは上のcurl の結果ですが、titleタグやog:urlなど大事なところでundefinedが入ってしまってますね・・・
noteをはじめて3日目の検索結果で実際のノートがヒットしないのもこの理由かもです。
もし勘違いでしたらすみません。
OpenGraphへの対応
"note"はFacebookのOpenGraphへの対応もしています。FacebookはGoogleがしている、escaped_fragmentのような仕組みは無いので別の方法で対応する必要があります。
"note"では、ユーザーエージェントでFacebookのクローラーならば、open graphタグが埋まったものを返しているようです。
curl --header "user-agent:facebookexternalhit/1.1(+http://www.facebook.com/externalhit_uatext.php)" "https://note.mu/sadaaki/n/n78107c38a0fc"
ちゃんとog:titleもog:urlとかも埋まってますね。
(これができるならGoogleのescaped_fragmentの方もこれを返せばいいのでは・・・?)
サーバー側でJavaScriptを実行する方法
エンジニアの方なら「じゃあどうやってサーバー側でJavaScriptを実行するの」と気になるところでしょう。しかし、"note"がどうやっているのか、そこは今回の調査では分かりませんでした。(サーバー側の話なので当然ですが・・・)
でもたぶん、PhantomJSを使ってそうです。
ここらへんの詳しいことは、"note"の開発陣の方に語ってもらえたらなあと思っています!
_escaped_fragment_=の罠
僕が作っているSTORYS.JPでも、AngularJSを使っていたわけではありませんが、JavaScript側でHTMLを描写し、escaped_fragmentなどの仕組みを利用していました。
しかし、Googleが正式に定めている仕様ではありますが、かなりマイナーな仕様でありGoogleがちゃんとそれを評価しているのか、クロールしているのかは怪しいところではありました。
また、JavaScriptをサーバー側で実行するとは言ってもなかなか難しいものであり、例えば、Google Analyticsのコードまで実行するわけにはいけません。なぜならGoogle Analyticsのコードを実行すると、実際にGoogle Analyticsにトラック情報が送信され、サーバー側で実行するたびにアクセスがあったことになってしまうからです。
そんな「サーバー側では実行したくないコード」を管理するのも一手間ですし、もし起こっていたとしても気づきにくい問題です。ここらへんのことを考えて、STORYS.JPではJavaScriptでの描写を諦め、サーバー側で普通にRailsの仕組みを使って描写することにしました。
まとめ
"note"ではAngularJSを使ってブラウザ側で描写している。
SEOの対策も頑張っている!が、もう少しな感じではある。
それと、JavaScriptで描写する方法は、いばらの道な可能性が高い。