提问



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


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如何呈现您的网站。 [38] [39]


原始回答

如果您想针对搜索引擎优化您的应用,很遗憾无法为抓取工具提供预渲染版本。您可以在此处详细了解Google对ajax和javascript-heavy网站的建议。[40]


如果这是一个选项我建议阅读本文关于如何使用服务器端渲染为Angular做SEO。[41]


我不确定爬虫在遇到自定义标签时会做什么。

其它参考1


使用PushState和Precomposition



目前(2015)的方法是使用JavaScript pushState方法。


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


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


Precomposition



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



  1. 通过PushState,用户单击PushState链接,内容为AJAXed。

  2. 直接点击网址。



网站上的初始点击将涉及直接点击URL。随着PushState更新URL,后续命中将只是内容中的AJAX。


抓取工具从页面中获取链接,然后将它们添加到队列中以供稍后处理。这意味着对于爬虫来说,服务器上的每次点击都是直接命中,他们不会通过Pushstate导航。


预合成将初始有效负载捆绑到服务器的第一个响应中,可能作为JSON对象。这允许搜索引擎在不执行AJAX调用的情况下呈现页面。


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


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


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



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


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


PushState URL的搜索引擎支持



PushState目前由Google和Bing支持。


谷歌



这里的Matt Cutts回应了Paul Irish关于PushState for SEO的问题:


http://youtu.be/yiAF9VdvRPw[43]


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


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


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


另请参阅Google网站管理员工具获取Googlebot。您将看到您的JavaScript(包括Angular)已执行。




以下是Bing发布的支持2013年3月发布的漂亮PushState URL的消息:


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


不要使用HashBangs#!



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


Hashbang网址如下所示:


domain.com/#!path/to/resource


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


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


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


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


domain.com/path/to/resource


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


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


测试您的网站



Google网站站长工具现在包含一个工具,可以让您以谷歌的形式获取网址,并在Google呈现时呈现JavaScript。


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


以Angular

生成PushState URL

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


$locationProvider.html5Mode(true);


服务器端



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


网页



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


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


浏览器支持



Pushstate适用于IE10。在旧版浏览器中,Angular会自动回退到哈希样式的URL


演示页



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


http://html5.gingerhost.com/london[47]


可以验证,在此链接中,内容已编入索引并显示在Google中。 [48]


提供404和301标头状态代码



由于搜索引擎始终会针对每个请求点击您的服务器,因此您可以从服务器提供标题状态代码,并希望Google能够看到它们。

其它参考2


让我们对AngularJS和SEO

有所了解

Google,Yahoo,Bing和其他搜索引擎使用传统抓取工具以传统方式抓取网络。他们运行 robots ,在网页上抓取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


我们可以使用ngRoute中的内置方法设置Angular应用程序的哈希前缀:


angular.module('myApp', [])
.config(['$location', function($location) {
  $location.hashPrefix('!');
}]);


而且,如果我们使用html5Mode,我们需要使用元标记来实现它:


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


提醒一下,我们可以用$location服务设置html5Mode():


angular.module('myApp', [])
.config(['$location', 
function($location) {
  $location.html5Mode(true);
}]);


处理搜索引擎



我们有很多机会来确定我们将如何处理实际向搜索引擎提供内容作为静态HTML。我们可以自己托管后端,我们可以使用服务为我们托管后端,我们可以使用代理提供内容等。让我们看看几个选项:


自托管



我们可以编写一个服务来处理使用无头浏览器(如phantomjs或zombiejs)爬行我们自己的站点,使用呈现的数据拍摄页面的快照并将其存储为HTML。每当我们在搜索请求中看到查询字符串?_escaped_fragment_时,我们就可以通过JS传递我们对页面而不是预呈现页面的静态HTML快照。这要求我们有一个后端,在中间提供带有条件逻辑的页面。我们可以使用像prerender.io的后端这样的东西作为自己运行的起点。当然,我们仍然需要处理代理和代码片段处理,但这是一个良好的开端。[49]


使用付费服务



将内容引入搜索引擎的最简单,最快捷的方法是使用服务Brombone,seo.js,seo4ajax和prerender.io就是这些的好例子,它将为您托管上述内容呈现。对于我们不想处理运行服务器/代理的时间,这是一个很好的选择。而且,它通常超级快。[50] [51] [52] [53]


有关Angular和SEO的更多信息,我们在http://www.ng-newsletter.com/posts/serious-angular-seo.html 上写了一篇关于它的详尽教程,我们详细介绍了它在我们的书 ng-book:AngularJS的完整书中。请在ng-book.com上查看。[54] [55]

其它参考3


您应该在moo博客年度查看有关构建SEO友好的AngularJS网站的教程。他将引导您完成Angular文档中列出的所有步骤.http://www.yearofmoo.com/2012/11/angularjs-and-seo.html [56]


使用此技术,搜索引擎会看到扩展的HTML而不是自定义标记。

其它参考4


这已经发生了巨大的变化。


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


如果您使用:
$ locationProvider.html5Mode(真);
你被设定了。


没有更多的渲染页面。

其它参考5


自从提出这个问题以来,情况发生了很大的变化。现在有选项让Google为您的AngularJS网站编制索引。我找到的最简单的选择是使用 http://prerender.io 免费服务,它将为您生成crwalable页面并将其提供给搜索引擎。它几乎在所有服务器端Web平台上都受支持。我最近开始使用它们,支持也非常好。 [58]


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

其它参考6


Angular自己的网站为搜索引擎提供简化的内容:http://docs.angularjs.org/?_ escaped_fragment_ =/tutorial/step_09 [59]


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


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


编辑:我意识到这有可能让您的REST api变得非常混乱,我不建议在非常简单的用例之外进行,这可能是一个很自然的选择。


相反,您可以为机器人友好的内容使用完全不同的路由和控制器集。但是,您将重新复制Node/Express中的所有AngularJS路由和控制器。


我已经决定使用无头浏览器生成快照,即使我觉得这有点不太理想。

其它参考7


这里有一个很好的做法:


http://scotch.io/tutorials/javascript/angularjs-seo-with-prerender-io?_escaped_fragment_=tag[61]

其它参考8


截至目前,谷歌已经改变了他们的AJAX抓取建议。



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



tl;博士:[[Google]]不再推荐2009年制作的AJAX抓取提案。

其它参考9


谷歌的可抓取Ajax规范,在其他答案中引用,基本上就是答案。


如果您对其他搜索引擎和社交机器人如何处理相同问题感兴趣,我在这里写下了现状:http://blog.ajaxsnapshots.com/2013/11/googles-crawlable-ajax-specification.html [63]


我在https://ajaxsnapshots.com工作,这是一家将Crawlable Ajax Spec作为服务实现的公司 - 该报告中的信息基于我们日志中的观察结果。[64]

其它参考10


我找到了一个优雅的解决方案,涵盖了你的大部分基地。我最初在这里写了这篇文章并回答了另一个类似的StackOverflow问题,引用它。[65]


仅供参考,此解决方案还包括硬编码的后备标签,以防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支持上不受欢迎)的完全动态渲染,您仍然必须使用其他一些答案中提到的预渲染服务之一。


希望这可以帮助!

其它参考11


使用类似PreRender的东西,它会生成您网站的静态页面,以便搜索引擎可以为其编制索引。


在这里,您可以找到可用的平台:https://prerender.io/documentation/install-middleware#asp-net [67]

其它参考12


使用Angular Universal,您可以为应用程序生成看起来像完整应用程序的登录页面,然后在其后面加载Angular应用程序。

Angular Universal生成纯HTML意味着服务器端的无javascript页面,并在不延迟的情况下为用户提供服务。因此,您可以处理任何爬虫,机器人和用户(已经具有较低的CPU和网络速度)。然后,您可以通过链接/按钮将它们重定向到已经加载到其后面的实际角度应用程序。该解决方案由官方网站推荐。 - 关于SEO和Angular Universal的更多信息 - [68]

其它参考13


爬虫(或机器人)旨在抓取网页的HTML内容,但由于异步数据获取的AJAX操作,这成为一个问题,因为它需要一些时间来呈现页面并在其上显示动态内容。同样,AngularJS也使用异步模型,这会为Google抓取工具带来问题。


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

其它参考14


抓取工具不需要功能丰富的漂亮风格的gui,他们只想看内容,因此您无需为他们提供为人类构建的页面的快照。


我的解决方案:为抓取工具提供抓取工具想要的内容:


你必须考虑爬虫想要的东西,并只给他一点。


提示不要惹恼后面。只需使用相同的API添加一个服务器端的前端视图