搜索引擎如何处理AngularJS应用程序?


697

我看到了有关搜索引擎和SEO的AngularJS应用程序两个问题:

1)自定义标签会怎样?搜索引擎会忽略这些标签中的全部内容吗?即假设我有

<custom>
  <h1>Hey, this title is important</h1>
</custom>

<h1>尽管位于自定义标签中仍会被索引?


2)有没有一种方法可以避免索引{{}}的搜索引擎按字面绑定?即

<h2>{{title}}</h2>

我知道我可以做类似的事情

<h2 ng-bind="title"></h2>

但是,如果我想让搜寻器“看到”标题怎么办?服务器端渲染是唯一的解决方案吗?


17
所有这些“解决方案”只是让我想摆脱AngularJS之类的技术,至少在google et都拥有更智能的搜寻器之前。
Codemonkey

22
@Codemonkey:是的,一个人会奇怪为什么Google的所有AngularJS都没有为此提供内置解决方案。.实际上是Wierd ..
Roy MJ

11
实际上,Misko在为Google工作之前曾写过Angular。Google现在赞助该项目,但他们不是发起者。
superluminary 2014年

2
也许这里的某人可以/应该更新SPA上的Wikipedia文章,其中指出:“在搜索引擎索引不是必需的还是期望的情况下,通常不使用SPA。” zh.wikipedia.org/wiki/Single-page_application [#搜索引擎优化]上有一段很大的段落涉及一个名为IsNat的(模糊的)基于Java的框架,但没有暗示Angularjs可以解决SEO。
linojon 2014年

3
@Roy MJ-为什么没人看到意图?PageSpeed,Angular等都是SERP上自然有机列表的敌人。故意。如果您有一个基于“按点击数付费”的庞大业务模型,那么与强迫创建一个别无选择的完整工具箱相比,如何迫使人们为自己的商家信息付款要更好呢?现在,该行业没有建立起充斥着有价值内容的高质量网站,而是到处充斥着无法实现或深蹲的作弊和解决方案。
史蒂文·文蒂米利亚

Answers:


403

2014年5月更新

Google搜寻器现在执行javascript-您可以使用Google网站站长工具更好地了解Google如何呈现您的网站。

原始答案
如果您想针对搜索引擎优化应用程序,那么很遗憾无法为搜寻器提供预渲染的版本。您可以在此处阅读有关Google针对ajax和javascript繁重网站的建议的更多信息。

如果这是一个选项,我建议阅读这篇文章,了解如何通过服务器端渲染对Angular进行SEO。

我不确定搜寻器在遇到自定义标签时会做什么。


13
这不再是当前的。您现在应该改用pushState。无需提供单独的站点静态版本。
superluminary 2014年

3
即使使用Google更新,也无法正确呈现ng-view,正如我在Google网站站长工具中
所见

10
是的,仅因为它们执行javascript并不意味着您的页面将被正确索引。最安全的方法是检测google bot用户代理,使用像phantomjs这样的无头浏览器,获取page.content并返回静态html。
测试人员

6
我意识到这个问题是针对SEO的,但请记住,其他抓取工具(Facebook,Twitter等)尚无法评估JavaScript。例如,如果没有服务器端渲染策略,则在社交媒体网站上共享页面仍然是一个问题。
Stephen Watkins 2014年

3
请问有人可以在不实施Google抓取方案规范的情况下提供正确索引AngularJS网站的示例吗?
check_ca 2015年

470

使用PushState和预合成

当前(2015年)的实现方法是使用JavaScript pushState方法。

PushState更改顶部浏览器栏中的URL,而无需重新加载页面。假设您有一个包含标签的页面。选项卡隐藏和显示内容,并且可以使用AJAX或通过简单地设置display:none和display:block动态隐藏内容,以隐藏和显示正确的选项卡内容。

单击选项卡后,使用pushState更新地址栏中的URL。呈现页面后,使用地址栏中的值确定要显示的选项卡。角路由将自动为您执行此操作。

预合成

有两种方法可以打入PushState单页应用程序(SPA)

  1. 通过PushState,用户单击PushState链接,然后将内容AJAXed到其中。
  2. 通过直接点击URL。

网站上的最初匹配将涉及直接匹配URL。当PushState更新URL时,随后的命中内容将只是AJAX。

抓取工具从页面中收集链接,然后将其添加到队列中以供以后处理。这意味着对于爬虫而言,服务器上的每个命中都是直接命中,它们不会通过Pushstate进行导航。

预组合将初始有效负载捆绑到服务器的第一个响应中,可能作为JSON对象。这使搜索引擎无需执行AJAX调用即可呈现页面。

有证据表明Google可能不会执行AJAX请求。有关此的更多信息:

https://web.archive.org/web/20160318211223/http://www.analog-ni.co/precomposed-a-spa-may-become-the-holy-grail-to-seo

搜索引擎可以读取和执行JavaScript

Google能够解析JavaScript已有一段时间了,这就是他们最初开发Chrome的原因,以充当Google蜘蛛的全功能无头浏览器。如果链接具有有效的href属性,则可以为新URL编制索引。没什么可做的了。

如果另外单击链接触发了pushState调用,则用户可以通过PushState导航该站点。

搜索引擎对PushState URL的支持

Google和Bing当前支持PushState。

谷歌

这是马特·卡茨(Matt Cutts)回应保罗·爱尔兰(Paul Irish)有关PushO for SEO的问题:

http://youtu.be/yiAF9VdvRPw

以下是Google宣布对蜘蛛的完整JavaScript支持:

http://googlewebmastercentral.blogspot.de/2014/05/understanding-web-pages-better.html

结果是Google支持PushState并将索引PushState URL。

另请参阅以Googlebot身份获取Google网站站长工具。您将看到您的JavaScript(包括Angular)已执行。

ing

以下是Bing宣布支持日期为2013年3月的漂亮PushState URL:

http://blogs.bing.com/webmaster/2013/03/21/search-engine-optimization-best-practices-for-ajax-urls/

不要使用HashBangs#!

Hashbang网址是一个丑陋的权宜之计,要求开发人员在特定位置提供网站的预渲染版本。它们仍然有效,但是您不需要使用它们。

Hashbang URL如下所示:

domain.com/#!path/to/resource

它将与这样的元标记配对:

<meta name="fragment" content="!">

Google不会以这种形式将它们编入索引,而是会从_escaped_fragments_ URL中提取网站的静态版本并对其进行索引。

Pushstate URL看起来像任何普通URL:

domain.com/path/to/resource

区别在于Angular通过拦截JavaScript中对document.location的更改来为您处理这些更改。

如果您想使用PushState URL(并且您可能会这样做),请删除所有旧的哈希样式URL和元标记,然后在配置块中启用HTML5模式。

测试您的网站

Google网站管理员工具现在包含一个工具,该工具可让您以google形式获取网址,并以Google呈现的方式呈现JavaScript。

https://www.google.com/webmasters/tools/googlebot-fetch

在Angular中生成PushState URL

要在Angular中生成真实的URL,而不是前缀的#,请在$ locationProvider对象上设置HTML5模式。

$locationProvider.html5Mode(true);

服务器端

由于您使用的是真实网址,因此您需要确保服务器为所有有效的网址提供了相同的模板(加上一些预组合的内容)。如何执行此操作将取决于服务器体系结构。

网站地图

您的应用可能会使用异常的导航形式,例如,悬停或滚动。为了确保Google能够驱动您的应用程序,我可能建议您创建一个站点地图,这是您的应用程序响应的所有URL的简单列表。您可以将其放置在默认位置(/ sitemap或/sitemap.xml),或使用网站管理员工具将其告知Google。

无论如何,拥有一个站点地图是一个好主意。

浏览器支持

Pushstate可在IE10中使用。在较旧的浏览器中,Angular将自动回退到哈希样式的URL

演示页面

以下内容是使用带有预先组成的pushstate URL呈现的:

http://html5.gingerhost.com/london

可以验证的是,在此链接上,内容已被索引并显示在Google中。

提供404和301标头状态代码

由于搜索引擎将始终为每个请求命中服务器,因此您可以从服务器提供标头状态代码,并期望Google看到它们。


我必须调查一下-感谢您的解释。我一直想知道的一件事是,谷歌现在是否在索引页面之前运行javascript?
jvv 2014年

1
“ PushState在不重新加载页面的情况下更改了顶部浏览器栏中的URL ...单击选项卡时,请使用pushState更新地址栏中的URL。呈现页面时,请使用地址栏中的值来确定哪个标签以显示。角度布线将自动为您执行此操作。” 灯泡!
atconway 2014年

1
@superluminary,能否请您进一步解释该主题?特别是“服务器端”部分。我正在使用angularjs + angularjs-route + locationProvider.html5Mode + api +动态导航(不是像html5.gingerhost.com那样的静态导航。URL可以很好地显示,但是内容似乎没有被编入索引。我是否必须在通过直接url访问页面时以某种方式提供静态内容吗?我实际上对以下消息感到困惑:>>您将需要确保服务器为所有有效URL都提供了相同的模板,请您解释一下吗?提前。
Sray

1
@sray-如果您站点上的每个URL都提供相同的模板,则浏览器将能够获取该模板,而Angular将能够通过检查URL并呈现正确的内容从那里获取该模板。如果直接在服务器上点击该URL返回404或500,则您有问题,直接链接将不起作用,书签将不起作用,并且您也不会被索引。现在看到了吗?
superluminary 2014年

1
@ user3339411-您的站点将响应的每个页面都应该有一个URL。如果您的站点只需要使用一组内容来响应一个URL,则根本不需要任何路由。这对于简单的网站来说很好。但是,如果您的站点为不同的URL引入了不同的数据(通过JSON)(使用JSON),则可以使用路由。由于Github静态页面是基于文件的,因此您需要一个实际的html文件来支持此实例中的每个URL。虽然没有网站必须基于文件的规则,但是如果您使用其他平台,则可以为多个URL提供相同的模板。
superluminary

107

让我们对AngularJS和SEO有了明确的认识

Google,Yahoo,Bing和其他搜索引擎使用传统的搜寻器以传统方式搜寻网络。他们运行的机器人会抓取网页上的HTML,并一路收集信息。他们保留了有趣的单词,并寻找到其他页面的其他链接(这些链接,链接的数量和数量在SEO中起作用)。

那么,为什么搜索引擎不处理javascript网站呢?

答案与以下事实有关:搜索引擎机器人通过无头浏览器工作,并且它们通常没有用于呈现页面JavaScript的JavaScript呈现引擎。这适用于大多数页面,因为大多数静态页面不关心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标记实现此目标:

<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的后端之类的内容作为自己运行该文件的起点。当然,我们仍然需要处理代理和代码段处理,但这是一个好的开始。

有偿服务

将内容带入搜索引擎的最简单,最快的方法是使用服务Bromboneseo.jsseo4ajaxprerender.io都是很好的例子,它们将为您提供上述内容呈现。对于我们不想处理运行服务器/代理的情况,这是一个很好的选择。而且,它通常超级快。

有关角和搜索引擎优化的更多信息,我们写了一篇关于它的广泛的教程在http://www.ng-newsletter.com/posts/serious-angular-seo.html 我们详细介绍它在我们的书更是NG-书:完整的AngularJS书籍。在ng-book.com上进行检查。


1
SEO4Ajax也是付费服务的一个很好的例子(测试版中免费)。不幸的是,似乎不允许我编辑此响应以将其添加到列表中。
check_ca 2013年

1
@auser您是否仍然推荐这种方法?最新的最高票数评论似乎不鼓励这种方法。
Lycha

这是一个很好的例子,说明了为什么我们永远不要在CS中说“权威指南”之类的话。现在,主要的搜索引擎都执行Javascript,因此该答案必须完全重写或删除。
勒布

1
@seb仍然是需要的,例如,当机器人抓取它时,需要在页面中打开的图形标签。例如,Facebook或Twitter卡需要它。但是,应将此答案更新为关注HTML5 pushstate,而不是现在不推荐使用的hashbang。
adriendenat

@Grsmto你是对的!然后我猜它应该被重写,因为它说主要的搜索引擎不执行JS,这不再是事实了。
勒布

57

您应该在moo博客这一年真正查看有关构建SEO友好型AngularJS网站的教程。他将带您完成Angular文档中概述的所有步骤。http://www.yearofmoo.com/2012/11/angularjs-and-seo.html

使用此技术,搜索引擎将看到展开的HTML而不是自定义标签。


@Brad Green即便如此,问题仍然存在(无论出于何种原因),您都可以回答。我想我一定是失去了一些东西:stackoverflow.com/questions/16224385/...
克里斯托夫

41

这已经彻底改变了。

http://searchengineland.com/bing-offers-recommendations-for-seo-friendly-ajax-suggests-html5-pushstate-152946

如果您使用:$ locationProvider.html5Mode(true); 你已经定了。

没有更多的渲染页面。


3
这应该是现在的最佳答案。我们在2014年,@ joakimbl的回答不再是最佳选择。
2014年

11
这是不正确的。该文章(从2013年3月开始)没有提及Bing执行javascript。Bing只是给出了使用pushstate的建议,而不是他们先前使用的建议#!。摘自文章:“必应告诉我,尽管他们仍然支持最初由Google发布的#!版本的可爬网AJAX,但他们发现很多时候并没有正确地实现它,因此强烈建议使用pushState。” 您仍然必须呈现静态HTML并将其提供给_escaped_fragment_URL。Bing / Google不会执行javascript / AJAX调用。
Prerender.io 2014年

2
您仍然需要_escaped_fragment_并呈现纯HTML页面。这解决不了任何伴侣。
斯坦(Stan)2014年

谷歌机器人仍然看不到我网站的动态内容,只有空白页面。
镇静鸟2014年

搜索网站:mysite.com显示{{staff}},而不显示通过AngularJS加载的内容。好像Google搜寻器从未听说过JavaScript。我能做什么?
工具包2014年

17

自从提出这个问题以来,事情已经发生了很大的变化。现在有一些选项可以让Google为您的AngularJS网站建立索引。我发现最简单的选择是使用http://prerender.io免费服务,该服务将为您生成可破解的页面并将其提供给搜索引擎。几乎所有服务器端Web平台都支持它。我最近开始使用它们,并且支持也很棒。

我与他们没有任何从属关系,这来自一个愉快的用户。


6
prerender.io的代码位于github(github.com/collectiveip/prerender)上,因此任何人都可以在自己的服务器上运行它。
user276648 2014年

现在这也已经过时了。请参阅下面的@ user3330270的答案。
Les Hazlewood

2
这不是过时的。@ user3330270的答案不正确。他们链接到的文章只是说要使用pushstate而不是#!。您仍然必须为搜寻器呈现静态页面,因为它们不执行javascript。
Prerender.io 2014年

9

Angular自己的网站向搜索引擎提供简化的内容: http //docs.angularjs.org/? tutorial/step_09

假设您的Angular应用正在使用Node.js / Express驱动的JSON api,例如/api/path/to/resource。也许您可以将任何请求重定向?_escaped_fragment_/api/path/to/resource.html,并使用内容协商来呈现内容的HTML模板,而不是返回JSON数据。

唯一的是,您的Angular路由需要与REST API匹配1:1。

编辑:我意识到这有可能使您的REST api更加混乱,我不建议您在很自然的非常简单的用例之外进行操作。

相反,您可以对机器人友好的内容使用一组完全不同的路由和控制器。但是,然后您在Node / Express中复制了所有AngularJS路由和控制器。

我已经决定使用无头浏览器生成快照,尽管我觉得这比理想情况要差一些。



7

截至目前,Google已更改了其AJAX抓取建议。

时代变了。今天,只要您不阻止Googlebot抓取JavaScript或CSS文件,我们通常就可以像现代浏览器一样呈现和理解您的网页。

tl; dr:[Google]不再推荐早在2009年提出的AJAX抓取建议[Google]。


@Toolkit是什么意思?
2016年

1
Googlebot无法解析Angular网站
Toolkit

4
@Toolkit你说的是绝对箍,我的整个Angular网站都被Google索引,并带有动态元数据,没有任何问题
twigg '16

@twigg您的逻辑有误,您的意思是如果一个(您的)Angular网站被索引,则全部索引。好吧,我为你感到惊讶。没有我的被索引。可能是因为我使用角度ui路由器或谁知道原因。甚至没有主页都没有任何ajax数据的
工具包

@Toolkit如果甚至没有为您的静态html页面建立索引,这也与Google抓取JS文件的能力无关。如果您说Google无法正确抓取任何内容,那么我认为您错了
phil294 '16

6

此处其他答案中所引用的Google的Crawlable Ajax Spec基本上就是答案。

如果您对其他搜索引擎和社交机器人如何处理相同的问题感兴趣,请在此处写下最新技术水平:http : //blog.ajaxsnapshots.com/2013/11/googles-crawlable-ajax-specification.html

我为https://ajaxsnapshots.com工作,该公司将Crawlable Ajax Spec作为服务实施-该报告中的信息基于我们日志中的观察结果。


链接在列出的blog.ajaxsnapshots.com-
凯文(Kevin)

4

我找到了一个优雅的解决方案,可以解决您大多数的问题。我写了一篇关于它最初在这里,并回答了另一个类似的StackOverflow问题,在这里它引用它。

仅供参考,该解决方案还包括硬编码的后备标记,以防爬虫无法获取Javascript。我没有明确概述它,但是值得一提的是,您应该激活HTML5模式以获得适当的URL支持。

另请注意:这些文件不是完整的文件,而只是相关文件的重要部分。如果您需要帮助来编写可在其他地方找到的指令,服务等的样板。无论如何,这里...

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支持不太满意),则仍然必须使用其他一些答案中提到的一种预渲染服务。

希望这可以帮助!


我也遵循此解决方案,并在此之前以为如此,但我想问一问搜索引擎将读取自定义标签的内容。
拉温德·佩亚尔

@RavinderPayal您可以使用seoreviewtools.com/html-headings-checker
vijay


2

使用Angular Universal,您可以为应用程序生成看上去像完整应用程序的登录页面,然后将Angular应用程序加载到其后。
Angular Universal生成纯HTML,意味着在服务器端没有JavaScript页面,并且无需延迟即可将其提供给用户。因此,您可以与任何爬虫,机器人和用户(已经具有较低的CPU和网络速度)打交道,然后可以通过链接/按钮将它们重定向到已经在其后面加载的实际角度应用程序。该解决方案是官方网站推荐的。-有关SEO和Angular Universal的更多信息-


1

爬网程序(或漫游器)旨在爬网网页的HTML内容,但是由于AJAX用于异步数据提取的操作,这成为一个问题,因为它需要一些时间来呈现页面并在其上显示动态内容。同样,AngularJS也请使用异步模型,这会对Google搜寻器造成问题。

一些开发人员使用实际数据创建基本的html页面,并在进行爬网时从服务器端提供这些页面。我们可以PhantomJS在服务端渲染具有相同页面_escaped_fragment_(因为Google会#!在我们的网站网址中查找,然后在后面获取所有内容#!并将其添加到_escaped_fragment_查询参数中)。有关更多详细信息,请阅读此博客


截至2017年10月,情况已不再如此,此所得税计算器income-tax.co.uk 是使用纯AngularJs构建的(甚至这些小标题都像<title> Tax Calculator的£{{{earningsSliders.yearly | number:0}}薪水</ title>,就像“税收计算器30000英镑的薪水”一样,Google对其进行索引,将它们排在首页上,列出了数百个关键字。只需为您的用户建立网站,让它们变得很棒,其余的一切由Google负责;)
Kaszoni Ferencz

0

搜寻器不需要功能丰富,风格漂亮的gui,它们只想查看内容,因此您无需为它们提供为人类构建的页面的快照。

我的解决方案:为搜寻器提供所需的内容

您必须考虑爬虫想要什么,并且只给他。

提示不要弄乱背面。只需使用相同的API添加一些服务器端的前视图

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.