検索エンジンとSEOに関してAngularJSアプリケーションには2つの問題があります。
1)カスタムタグはどうなりますか?検索エンジンはこれらのタグ内のコンテンツ全体を無視しますか?すなわち私が持っているとします
<custom>
<h1>Hey, this title is important</h1>
</custom>
カスタムタグの中にあっても<h1>
はインデックス付けされますか?
2)インデックス化の検索エンジンが文字通りバインドするのを避ける方法はありますか?すなわち.
<h2>{{title}}</h2>
私は私が好きなことができることを知っています
<h2 ng-bind="title"></h2>
しかし、クローラーに実際にタイトルを「見せる」ようにしたい場合はどうすればよいですか。サーバーサイドレンダリングは唯一の解決策ですか?
2014年5月に更新
Googleクローラ 現在はjavascriptを実行します - あなたのサイトがGoogleによってどのようにレンダリングされるかを理解するために Googleウェブマスターツール を使うことができます。
元の答え
検索エンジン用にアプリを最適化したい場合は、残念ながらクローラに事前にレンダリングされたバージョンを提供する方法はありません。あなたはajaxとjavascriptが多いサイトに対するGoogleの勧告についてもっと読むことができます here 。
これが選択肢であるならば、私は この記事 を読んでサーバーサイドレンダリングでAngularのSEOを行う方法についてお勧めします。
クローラがカスタムタグに遭遇したときの動作がわからない。
これを行うための現在(2015年)の方法は、JavaScriptのpushStateメソッドを使用することです。
PushStateは、ページを再ロードせずにトップブラウザバーのURLを変更します。タブを含むページがあるとしましょう。タブはコンテンツの表示と非表示を切り替えます。コンテンツはAJAXを使用するか、単にdisplay:noneとdisplay:blockを設定することで動的に挿入され、正しいタブコンテンツが表示および非表示になります。
タブがクリックされたら、pushStateを使用してアドレスバーのURLを更新します。ページがレンダリングされたら、アドレスバーの値を使用して表示するタブを決定します。 Angularルーティングは自動的にこれを行います。
PushState Single Page App(SPA)を使用する方法は2つあります。
サイトへの最初のヒットは直接URLを打つことを含みます。それ以降のヒットは、PushStateがURLを更新するにつれて、コンテンツ内でAJAXになります。
クローラはページからリンクを取得し、後で処理するためにそれらをキューに追加します。つまり、クローラーにとって、サーバーへのヒットはすべて直接ヒットであり、Pushstateを介してナビゲートするわけではありません。
プリコンポジションは最初のペイロードを、おそらくJSONオブジェクトとして、サーバーからの最初の応答にまとめます。これにより、検索エンジンはAJAX呼び出しを実行せずにページをレンダリングできます。
GoogleがAJAXリクエストを実行しない可能性があることを示唆する証拠がいくつかあります。これについての詳細はこちら:
Googleはしばらく前からJavaScriptを解析することができました。Googleスパイダーのためのフル機能のヘッドレスブラウザとして機能するために、彼らが最初にChromeを開発したのはそのためです。リンクに有効なhref属性がある場合は、新しいURLにインデックスを付けることができます。他にやることは何もありません。
さらにリンクをクリックするとpushState呼び出しがトリガーされると、ユーザーはPushStateを介してサイトをナビゲートできます。
PushStateは現在GoogleとBingでサポートされています。
SEOのためのPushStateについてのPaul Irishの質問に答えているMatt Cuttsです。
Googleがクモの完全なJavaScriptサポートを発表しました。
http://googlewebmastercentral.blogspot.de/2014/05/understanding-web-pages-better.html
その結果、GoogleはPushStateをサポートし、PushStateのURLにインデックスを付けます。
グーグルウェブマスターツールがグーグルボットとして入手することも参照のこと。あなたはあなたのJavaScript(Angularを含む)が実行されるのを見るでしょう。
2013年3月付けのかわいいPushState URLに対するBingのサポートの発表は、次のとおりです。
http://blogs.bing.com/webmaster/2013/03/21/search-engine-optimization-best-practices-for-ajax-urls/ /
HashbangのURLは、開発者が事前にレンダリングされたバージョンのサイトを特別な場所に提供することを要求する醜い一時停止でした。まだ機能しますが、使用する必要はありません。
HashbangのURLは次のようになります。
domain.com/#!path/to/resource
これは、このようなメタタグとペアになります。
<meta name="fragment" content="!">
グーグルはそれらをこの形式で索引付けするのではなく、代わりに_escaped_fragments_ URLから静的バージョンのサイトを引き出してそれを索引付けします。
プッシュステートURLは、通常のURLのように見えます。
domain.com/path/to/resource
違いは、AngularがJavaScriptでdocument.locationへの変更をインターセプトすることによってそれらを処理することです。
もしあなたがPushState URLを使いたいなら(そしておそらくあなたはそうするでしょう)、古いハッシュスタイルのURLとメタタグをすべて取り除き、あなたの設定ブロックでHTML5モードを有効にしてください。
Googleウェブマスターツールには、URLをGoogleとして取得したり、GoogleがレンダリングしたときにJavaScriptをレンダリングしたりできるツールが含まれるようになりました。
https://www.google.com/webmasters/tools/googlebot-fetch
接頭辞を付けずにAngularで実際のURLを生成するには、$ locationProviderオブジェクトにHTML5モードを設定します。
$locationProvider.html5Mode(true);
実際のURLを使用しているので、すべての有効なURLに対して同じテンプレート(およびいくつかの事前作成されたコンテンツ)がサーバーから出荷されていることを確認する必要があります。これを行う方法は、サーバーのアーキテクチャによって異なります。
あなたのアプリは、ホバーやスクロールなどの変わった形のナビゲーションを使うかもしれません。 Googleがあなたのアプリを動かすことができることを確実にするために、私はおそらくあなたのアプリが応答するすべてのURLの単純なリストであるサイトマップを作成することを勧めます。これをデフォルトの場所(/ sitemapまたは/sitemap.xml)に配置するか、ウェブマスターツールを使用してGoogleに通知することができます。
とにかくサイトマップを用意するのは良い考えです。
プッシュステートはIE10で動作します。古いブラウザでは、Angularは自動的にハッシュスタイルのURLに戻ります。
次のコンテンツは、前処理付きのプッシュステートURLを使用してレンダリングされます。
http://html5.gingerhost.com/london
確認できるように、 このリンク で、コンテンツにインデックスが付けられ、Googleに表示されます。
検索エンジンはリクエストごとに常にサーバーにアクセスするため、サーバーからヘッダーステータスコードを送信してGoogleに表示させることができます。
Google、Yahoo、Bing、およびその他の検索エンジンは、従来のクローラーを使用して従来の方法でWebをクロールします。彼らはロボットを実行し、Webページ上のHTMLをクロールし、途中で情報を収集します。彼らは面白い言葉を保持し、他のページへの他のリンクを探します(これらのリンク、それらの量、およびそれらの数はSEOに関係します)。
答えは、検索エンジンロボットがヘッドレスブラウザーを介して動作し、ほとんどの場合、ページのjavascriptをレンダリングするJavaScriptレンダリングエンジンをnot持っているという事実に関係しています。ほとんどの静的ページはコンテンツが既に利用可能であるため、ほとんどのページでJavaScriptがページをレンダリングすることを考慮していないため、これはほとんどのページで機能します。
幸いなことに、大規模なサイトのクローラーは、JavaScriptサイトをクロール可能にするメカニズムを実装し始めましたが、サイトに変更を実装する必要があります。
hashPrefix
を単に#!
ではなく#
に変更すると、最新の検索エンジンは_escaped_fragment_
ではなく#!
を使用するようにリクエストを変更します。 (HTML5モード、つまりハッシュプレフィックスのないリンクがある場合、バックエンドのUser Agent
ヘッダーを調べることでこの同じ機能を実装できます)。
つまり、次のような通常のブラウザからのリクエストの代わりに:
http://www.ng-newsletter.com/#!/signup/page
検索エンジンは、次を使用してページを検索します。
http://www.ng-newsletter.com/?_escaped_fragment_=/signup/page
Angularアプリのハッシュプレフィックスは、ngRoute
の組み込みメソッドを使用して設定できます。
angular.module('myApp', [])
.config(['$location', function($location) {
$location.hashPrefix('!');
}]);
そして、html5Mode
を使用している場合、メタタグを使用してこれを実装する必要があります。
<meta name="fragment" content="!">
html5Mode()
を$location
サービスで設定できます:
angular.module('myApp', [])
.config(['$location',
function($location) {
$location.html5Mode(true);
}]);
静的HTMLとして検索エンジンにコンテンツを実際に配信する方法を決定する多くの機会があります。バックエンドを自分でホストしたり、サービスを使用してバックエンドをホストしたり、プロキシを使用してコンテンツを配信したりすることができます。いくつかのオプションを見てみましょう。
Phantomjsやzombiejsなどのヘッドレスブラウザーを使用してサイトのクロールを処理するサービスを記述し、レンダリングされたデータを含むページのスナップショットを取得してHTMLとして保存できます。検索リクエストにクエリ文字列?_escaped_fragment_
が表示されるたびに、事前レンダリングされたページの代わりに、JSのみを使用してページの静的なHTMLスナップショットを配信できます。これには、途中で条件付きロジックを使用してページを配信するバックエンドが必要です。 prerender.io's バックエンドのようなものを、これを自分で実行するための出発点として使用できます。もちろん、プロキシ処理とスニペット処理を引き続き行う必要がありますが、良いスタートです。
コンテンツを検索エンジンに取り込む最も簡単で最速の方法は、サービスを使用することです Brombone 、 seo.js 、 seo4ajax 、および prerender.io は、上記のコンテンツレンダリングをホストするこれらの良い例です。これは、サーバー/プロキシの実行に対処したくない場合に適したオプションです。また、通常は非常に高速です。
AngularおよびSEOの詳細については、 http://www.ng-newsletter.com/posts/serious-angular-seo.html で詳細なチュートリアルを作成しました。 and本でさらに詳しく説明しましたng-book:AngularJSの完全な本。 ng-book.com で確認してください。
あなたは本当にmooブログの年にSEOにやさしいAngularJSサイトを構築することに関するチュートリアルをチェックするべきです。彼はAngularのドキュメントで概説されているすべてのステップをあなたに案内します。 http://www.yearofmoo.com/2012/11/angularjs-and-seo.html
この技術を使用して、検索エンジンはカスタムタグの代わりに拡張されたHTMLを見ます。
これは劇的に変わりました。
使用する場合:$ locationProvider.html5Mode(true);あなたは設定されています。
これ以上レンダリングページはありません。
この質問をしてから、状況はかなり変わりました。 GoogleがAngularJSサイトをインデックスに登録するためのオプションがあります。私が見つけた最も簡単なオプションはhttp://prerender.iofreeサービスを使うことでした。ほとんどすべてのサーバーサイドWebプラットフォームでサポートされています。私は最近それらを使い始めました、そしてサポートも優れています。
私は彼らと提携していません、これは幸せなユーザーから来ています。
Angular自身のWebサイトは、検索エンジンに簡略化されたコンテンツを提供します。 http://docs.angularjs.org/?_escaped_fragment_=/tutorial/step_09
Angularアプリが、/api/path/to/resource
のようにNode.js/Express駆動のJSON APIを使用しているとします。 JSONデータを返すのではなく、コンテンツのHTMLテンプレートをレンダリングするために、?_escaped_fragment_
のリクエストを/api/path/to/resource.html
にリダイレクトし、 コンテンツネゴシエーション を使用することができます。
唯一のことは、あなたのAngularルートがあなたのREST APIと1:1でマッチする必要があるということです。
_ edit _ :これはあなたのREST apiを本当に曖昧にする可能性があることに気づいています。自然にフィットします。
代わりに、あなたのロボットにやさしいコンテンツのために全く異なるルートとコントローラーのセットを使うことができます。しかし、その後、Node/Express内のAngularJSのルートとコントローラをすべて複製しています。
ヘッドレスブラウザを使ってスナップショットを生成することにしました。
現在、GoogleはAJAXクロールの提案を変更しました。
tl; dr:[Google]は2009年に作成されたAJAXクロールの提案[Google]を推奨しなくなりました。
ここで他の答えで参照されるようにグーグルのクロール可能なAjax仕様は基本的に答えです。
他のサーチエンジンやソーシャルボットが同じ問題にどのように対処しているかに興味があるなら、私がここで最先端を書いた: http://blog.ajaxsnapshots.com/2013/11/googles-crawlable-ajax-specification .html
私は https://ajaxsnapshots.com 、Crawlable Ajax Specをサービスとして実装している会社で働いています - そのレポートの情報は私たちのログからの観察に基づいています。
私はあなたの拠点のほとんどをカバーするような優雅な解決策を見つけました。私は最初にそれについて書きました ここ そしてそれを参照しているもう一つの同様のStackOverflow質問 ここ に答えました。
参考までに、このソリューションには、Javascriptがクローラーによって検出されない場合のためのハードコードされたフォールバックタグも含まれています。私は明示的には概説していませんが、適切なURLサポートのためにはHTML 5モードを有効にするべきであることを言及する価値があります。
また、注意してください:これらは完全なファイルではなく、関連するそれらの重要な部分だけです。他の場所にあるディレクティブ、サービスなどの定型文を書くのに手助けが必要な場合。とにかく、ここに行きます...
app.js
これはあなたがあなたの各経路(タイトル、説明など)のためのカスタムメタデータを提供するところです。
$routeProvider
.when('/', {
templateUrl: 'views/homepage.html',
controller: 'HomepageCtrl',
metadata: {
title: 'The Base Page Title',
description: 'The Base Page Description' }
})
.when('/about', {
templateUrl: 'views/about.html',
controller: 'AboutCtrl',
metadata: {
title: 'The About Page Title',
description: 'The About Page Description' }
})
metadata-service.js (サービス)
カスタムメタデータオプションを設定するか、フォールバックとしてデフォルトを使用します。
var self = this;
// Set custom options or use provided fallback (default) options
self.loadMetadata = function(metadata) {
self.title = document.title = metadata.title || 'Fallback Title';
self.description = metadata.description || 'Fallback Description';
self.url = metadata.url || $location.absUrl();
self.image = metadata.image || 'fallbackimage.jpg';
self.ogpType = metadata.ogpType || 'website';
self.twitterCard = metadata.twitterCard || 'summary_large_image';
self.twitterSite = metadata.twitterSite || '@fallback_handle';
};
// Route change handler, sets the route's defined metadata
$rootScope.$on('$routeChangeSuccess', function (event, newRoute) {
self.loadMetadata(newRoute.metadata);
});
metaproperty.js (ディレクティブ)
ビューのメタデータサービスの結果をパッケージ化します。
return {
restrict: 'A',
scope: {
metaproperty: '@'
},
link: function postLink(scope, element, attrs) {
scope.default = element.attr('content');
scope.metadata = metadataService;
// Watch for metadata changes and set content
scope.$watch('metadata', function (newVal, oldVal) {
setContent(newVal);
}, true);
// Set the content attribute with new metadataService value or back to the default
function setContent(metadata) {
var content = metadata[scope.metaproperty] || scope.default;
element.attr('content', content);
}
setContent(scope.metadata);
}
};
index.html
JavaScriptを拾うことができないクローラーのために、前述のハードコードされたフォールバックタグを完成させてください。
<head>
<title>Fallback Title</title>
<meta name="description" metaproperty="description" content="Fallback Description">
<!-- Open Graph Protocol Tags -->
<meta property="og:url" content="fallbackurl.com" metaproperty="url">
<meta property="og:title" content="Fallback Title" metaproperty="title">
<meta property="og:description" content="Fallback Description" metaproperty="description">
<meta property="og:type" content="website" metaproperty="ogpType">
<meta property="og:image" content="fallbackimage.jpg" metaproperty="image">
<!-- Twitter Card Tags -->
<meta name="Twitter:card" content="summary_large_image" metaproperty="twitterCard">
<meta name="Twitter:title" content="Fallback Title" metaproperty="title">
<meta name="Twitter:description" content="Fallback Description" metaproperty="description">
<meta name="Twitter:site" content="@fallback_handle" metaproperty="twitterSite">
<meta name="Twitter:image:src" content="fallbackimage.jpg" metaproperty="image">
</head>
これはほとんどの検索エンジンのユースケースで劇的に役立つはずです。ソーシャルネットワーククローラ(Javascriptをサポートしている場合)に完全に動的なレンダリングが必要な場合は、他の回答で説明しているプレレンダリングサービスのいずれかを使用する必要があります。
お役に立てれば!
Angular Universalを使用すると、完全なアプリのように見えるアプリのランディングページを生成し、その背後に自分のAngularアプリを読み込むことができます。
Angular Universalは、サーバーサイドに純粋なHTMLの手段なしのJavaScriptページを生成し、それらを遅延することなくユーザーに提供します。だからあなたは(すでに低いCPUとネットワーク速度を持っている)任意のクローラ、ボットとユーザーに対処することができます。あなたはそれの後ろにすでにロードされているあなたの実際の角度のアプリにリンク/ボタンでそれらをリダイレクトできます。この解決策は公式サイトによって推奨されています。 - SEOとAngular Universalに関する詳細情報 /
PreRenderのようなものを使用してください、それはあなたのサイトの静的なページを作るので検索エンジンがそれを索引付けすることができます。
ここであなたはそれが利用可能なプラットフォームについて調べることができます: https://prerender.io/documentation/install-middleware#asp-net
クローラ(またはボット)はWebページのHTMLコンテンツをクロールするように設計されていますが、非同期データフェッチのためのAJAX操作のため、ページをレンダリングして動的コンテンツを表示するのに時間がかかるため、問題になりました。同様に、AngularJS
も非同期モデルを使用します。これはGoogleクローラーに問題を引き起こします。
実際のデータで基本的なHTMLページを作成し、クロール時にサーバー側からこれらのページを提供する開発者もいます。 _escaped_fragment_
を持つサーバー側でPhantomJS
を使用して同じページをレンダリングすることができます(Googleでは、サイトのURLで#!
を検索し、#!
の後にあるすべてのものを_escaped_fragment_
クエリパラメータに追加しています)。詳しくは blog を読んでください。
クローラは豊富な機能を備えたかわいいスタイルのGUIを必要としません。コンテンツを表示するだけです したがって、人間向けに作成されたページのスナップショットを提供する必要はありません。
私の解決策:/ クローラが欲しいものをクローラに渡す :
クローラーが何を望んでいるのかを考えて、それだけを彼に伝えなければなりません。
ちょっと後ろを台無しにしないでください。同じAPIを使用して、小さなサーバーサイドの正面図を追加するだけです。