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

时间:2012-11-21 17:44:48

标签: html5 angularjs seo search-engine google-search

我看到AngularJS应用程序在搜索引擎和搜索引擎优化方面存在两个问题:

1)自定义标签会发生什么?搜索引擎会忽略这些标签中的所有内容吗?即假设我有

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

尽管在自定义标签内部,<h1>会被编入索引吗?


2)有没有办法避免索引{{}}的搜索引擎直接绑定?即。

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

我知道我可以做类似

的事情
<h2 ng-bind="title"></h2>

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

15 个答案:

答案 0 :(得分:470)

使用PushState和Precomposition

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

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

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

Precomposition

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

  1. 通过PushState,用户单击PushState链接,内容为AJAXed。
  2. 直接点击网址。
  3. 网站上的初次点击将涉及直接点击网址。当PushState更新URL时,后续命中将只是内容中的AJAX。

    Crawlers收集页面中的链接,然后将它们添加到队列中以供以后处理。这意味着对于爬虫来说,服务器上的每次点击都是直接命中,他们不会通过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

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

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

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

    PushState URL的搜索引擎支持

    Google和Bing目前支持PushState。

    谷歌

    Matt Cutts在回答保罗爱尔兰关于推送搜索引擎优化的问题:

    http://youtu.be/yiAF9VdvRPw

    Google宣布对蜘蛛提供完整的JavaScript支持:

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

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

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

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

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

    不要使用HashBangs#!

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

    Hashbang网址如下所示:

    domain.com/#!path/to/resource

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

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

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

    Pushstate网址与任何普通网址类似:

    domain.com/path/to/resource

    不同之处在于Angular通过拦截在JavaScript中处理它的document.location的更改来为你处理它们。

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

    测试您的网站

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

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

    以Angular

    生成PushState URL

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

    $locationProvider.html5Mode(true);
    

    服务器端

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

    网页

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

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

    浏览器支持

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

    演示页

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

    http://html5.gingerhost.com/london

    可以验证,在this link,内容已编入索引并显示在Google中。

    提供404和301标题状态代码

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

答案 1 :(得分:404)

2014年5月更新

Google抓取工具now executes javascript - 您可以使用Google Webmaster Tools更好地了解Google如何呈现您的网站。

原始回答
如果您想针对搜索引擎优化您的应用,很遗憾无法为抓取工具提供预渲染版本。您可以详细了解Google针对ajax和javascript密集网站here的建议。

如果这是一个选项,我建议阅读this article关于如何使用服务器端渲染为Angular做SEO。

我不确定抓取工具在遇到自定义标记时会做什么。

答案 2 :(得分:106)

让我们对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="!">

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

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

处理搜索引擎

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

自托管

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

使用付费服务

将内容引入搜索引擎的最简单,最快捷的方法是使用服务Bromboneseo.jsseo4ajaxprerender.io就是这些的好例子。将为您托管上述内容呈现。对于我们不想处理运行服务器/代理的时候,这是一个很好的选择。此外,它通常超级快。

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

答案 3 :(得分:56)

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

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

答案 4 :(得分:41)

这已经发生了翻天覆地的变化。

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

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

不再有渲染页面。

答案 5 :(得分:17)

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

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

答案 6 :(得分:9)

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

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

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

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

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

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

答案 7 :(得分:8)

答案 8 :(得分:7)

截至目前,Google已经更改了他们的AJAX抓取提案。

  

Times have changed. Today, as long as you're not blocking Googlebot from crawling your JavaScript or CSS files, we are generally able to render and understand your web pages like modern browsers.

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

答案 9 :(得分:6)

Google的Crawlable Ajax Spec,在其他答案中引用,基本上就是答案。

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

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

答案 10 :(得分:4)

我找到了一个优雅的解决方案,可以覆盖你的大多数基地。我最初写了here并回答了另一个引用它的类似StackOverflow问题here

仅供参考,此解决方案还包括硬编码的后备标记,以防抓取工具未获取Javascript。我没有明确地概述它,但值得一提的是,您应该激活HTML5模式以获得正确的URL支持。

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

<强> app.js

您可以在此处为每条路线(标题,说明等)提供自定义元数据

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim lStr1() As String = {"One", "Two", "Three"}
    Dim lStr2() As String = {"EditOne", "EditTwo", "EditThree"}

    Dim res() As String = myAwesomeFunction(lStr1, lStr2)
End Sub

Function MyAwesomeFunction(lStr1() As String, lStr2() As String) As String()
    Dim combos As New List(Of String)
    If lStr1.Length <> lStr2.Length Then Throw New ArgumentException("Arrays must have the same length")
    For Each combo() As Integer In GetPermutations(lStr1.Length, 2)
        Dim elem As New List(Of String)
        For i As Integer = 0 To combo.Length - 1
            elem.Add(If(combo(i) = 0, lStr1(i), lStr2(i)))
        Next
        combos.Add(String.Join(" ", elem))
    Next
    Return combos.ToArray
End Function

Public Iterator Function GetPermutations(choose As Integer, total As Integer) As IEnumerable(Of Integer())
    Dim totals() As Integer = Enumerable.Repeat(Of Integer)(total, choose).ToArray
    Dim value(choose - 1) As Integer

    Do
        Yield value
        For index As Integer = choose - 1 To 0 Step -1
            value(index) += 1
            If value(index) < totals(index) Then Continue Do
            value(index) = 0
        Next
        Exit Do
    Loop
End Function

metadata-service.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' }
   })

metaproperty.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);
});

<强>的index.html

完成前面提到的硬编码后备标记,对于无法获取任何Javascript的抓取工具。

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);
  }
};

这对大多数搜索引擎用例都有很大帮助。如果您想要对社交网络抓取工具进行完全动态渲染(在Javascript支持上不受欢迎),您仍然必须使用其他一些答案中提到的预渲染服务之一。

希望这有帮助!

答案 11 :(得分:2)

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

您可以在此处了解可用的平台:https://prerender.io/documentation/install-middleware#asp-net

答案 12 :(得分:2)

使用Angular Universal,您可以为应用程序生成看起来像完整应用程序的登录页面,然后在其后面加载Angular应用程序。
Angular Universal生成纯HTML表示服务器端的无javascript页面,并在不延迟的情况下为用户提供服务。因此,您可以处理任何爬虫,机器人和用户(已经具有低CPU和网络速度)。然后您可以通过链接/按钮将它们重定向到已经加载到其后面的实际角度应用程序。该解决方案由官方网站推荐。 -More info about SEO and Angular Universal-

答案 13 :(得分:1)

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

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

答案 14 :(得分:0)

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

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

你必须考虑爬虫想要的东西,并且只给他那个。

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