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

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

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

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

<h1>是否会被索引,尽管它在自定义标签中?


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

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

我知道我可以做这样的事

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

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

208644 次浏览

2014年5月更新

Google爬虫现在执行javascript-您可以使用谷歌网站管理员工具更好地了解Google如何呈现您的网站。

原答复
不幸的是,如果您想针对搜索引擎优化您的应用程序,则无法向爬虫提供预渲染版本。您可以阅读有关Google对ajax和javascript重站点这里的建议的更多信息。

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

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

你真的应该看看moo博客上关于构建SEO友好的AngularJS网站的教程。他引导你完成Angular留档中概述的所有步骤。http://www.yearofmoo.com/2012/11/angularjs-and-seo.html

使用这种技术,搜索引擎可以看到扩展的超文本标记语言而不是自定义标记。

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

假设您的Angular应用程序正在使用Node.js/Express-drivenJSON api,例如/api/path/to/resource。也许您可以使用?_escaped_fragment_将任何请求重定向到/api/path/to/resource.html,并使用内容协商呈现内容的超文本标记语言模板,而不是返回JSON数据。

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

编辑:我意识到这有可能会让你的REST API变得混乱,我不建议在非常简单的用例之外这样做,因为它可能是一个自然的选择。

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

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

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

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

让我们明确了解AngularJS和SEO

谷歌、雅虎、必应和其他搜索引擎使用传统爬虫以传统方式抓取网络。他们运行机器人,抓取网页上的超文本标记语言,在此过程中收集信息。他们保留有趣的单词,并寻找指向其他页面的其他链接(这些链接、链接的数量和数量与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);
}]);

处理搜索引擎

我们有很多机会来确定如何将内容作为静态超文本标记语言实际交付给搜索引擎。我们可以自己托管后端,我们可以使用服务为我们托管后端,我们可以使用代理来交付内容,等等。让我们看看几个选项:

自托管

我们可以编写一个服务来处理使用无头浏览器爬取我们自己的站点,例如phantomjs或zombiejs,获取包含渲染数据的页面的快照并将其存储为超文本标记语言。每当我们在搜索请求中看到查询字符串?_escaped_fragment_时,我们就可以仅通过JS传递我们获取的页面的静态超文本标记语言快照,而不是预渲染页面的快照。这需要我们有一个后端,在中间使用条件逻辑交付我们的页面。我们可以使用类似prerender.io的后端作为起点来自己运行它。当然,我们仍然需要处理代理和片段处理,但这是一个好的开始。

通过付费服务

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

有关Angular和SEO的更多信息,我们在http://www.ng-newsletter.com/posts/serious-angular-seo.html上写了一个关于它的广泛教程,我们在我们的书ng-book:关于AngularJS的全书中更详细地介绍了它。在ng-book.com查看它。

Google的Crawlable Ajax Spec,正如这里的其他答案所引用的,基本上就是答案。

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

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

爬虫不需要一个功能丰富的漂亮风格的gui,他们只想看到内容,因此您不需要为他们提供为人类构建的页面的快照。

我的解决方案:到给爬虫想要的东西

你必须想到爬虫想要什么,只给他那个。

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

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

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

如果您使用: $locationProvider.html5Mode(true); 你准备好了

不再渲染页面。

(2022)尽可能使用服务器端渲染,并使用Pushstate生成URL

Google现在可以并且将运行JavaScript,因此只要您创建合理的URL结构,就可以仅使用JavaScript构建网站。然而,页面速度已成为越来越重要的排名因素,通常客户端构建的页面在初始渲染时表现不佳。

服务器端渲染(SSR)可以通过允许您的页面在服务器上预先生成来提供帮助。您的html与将用作页面根目录的div相冲突,但这不是一个空div,它包含JavaScript在允许运行时会生成的html。

客户端下载超文本标记语言并使其提供非常快速的初始加载,然后执行JavaScript,在称为水合的过程中将根div的内容替换为生成的内容。

许多较新的框架都内置了SSR,特别是NextJS。

(2015)使用PushState和预合成

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

PushState在不重新加载页面的情况下更改顶部浏览器栏中的URL。假设您有一个包含选项卡的页面。选项卡隐藏和显示内容,内容是动态插入的,可以使用AJAX或简单地设置显示:无和显示:块来隐藏和显示正确的选项卡内容。

单击选项卡时,使用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/precomposing-a-spa-may-become-the-holy-grail-to-seo

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

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

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

搜索引擎支持PushState URL

PushState目前由Google和必应支持。

谷歌

以下是Matt Cutts回应Paul爱尔兰关于PushState for SEO的问题:

http://youtu.be/yiAF9VdvRPw

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

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

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

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

必应

以下是必应在2013年3月宣布支持漂亮的PushState URL:

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

不要使用HashBangs#!

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

哈希邦URL看起来像这样:

domain.example/#!path/to/resource

这将与这样的metatag配对:

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

Google不会以这种形式索引它们,而是会从escaped_fragments URL中提取站点的静态版本并将其索引。

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

domain.example/path/to/resource

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

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

测试您的网站

Google网站管理员工具现在包含一个工具,它允许您以Google的身份获取URL,并在Google渲染时呈现JavaScript。

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

在Angular中生成PushState URL

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

$locationProvider.html5Mode(true);

服务端

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

站点地图

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

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

浏览器支持

Pushstate在IE10中工作。在旧浏览器中,Angular会自动回退到哈希样式URL

一个演示页面

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

http://html5.gingerhost.com/london

可以验证,在此链接,内容被索引并出现在Google中。

提供404和301标头状态代码

因为搜索引擎总是会为每个请求访问您的服务器,所以您可以从您的服务器提供标头状态代码并期望Google看到它们。

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

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

爬虫(或机器人)旨在抓取网页的超文本标记语言内容,但由于用于异步数据获取的AJAX操作,这成为一个问题,因为在其上呈现页面并显示动态内容需要一些时间。类似地,AngularJS也使用异步模型,这会给Google爬虫带来问题。

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

到目前为止,Google已经改变了他们的AJAX抓取提案。

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

tl; dr:[Google]不再推荐[Google]在2009年提出的AJAX爬行提案。

我找到了一个优雅的解决方案,可以涵盖你的大部分基础。我最初写了关于它的文章这里,并回答了另一个类似的堆栈溢出问题这里,它引用了它。

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

另请注意:这些不是完整的文件,只是相关文件的重要部分。我无法帮助编写指令、服务等的样板。

app.example

这是您为每个路由(标题、描述等)提供自定义元数据的地方。

$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.example" 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在服务器端生成纯超文本标记语言意味着无javascript页面,并毫无延迟地将其提供给用户。所以你可以处理任何爬虫,机器人和用户(已经有低cpu和网速)。然后你可以通过链接/按钮将它们重定向到已经加载的实际角度应用程序。官方网站推荐此解决方案。-更多关于SEO和Angular Universal-