我发现 AngularJS 应用程序有两个关于搜索引擎和 SEO 的问题:
1) 自定义标签会发生什么?搜索引擎会忽略这些标签中的全部内容吗?即假设我有
<custom>
<h1>Hey, this title is important</h1>
</custom>
尽管在自定义标签中,<h1>
是否会被编入索引?
2) 有没有办法避免索引 {{}} 的搜索引擎按字面意思绑定? IE
<h2>{{title}}</h2>
我知道我可以做类似的事情
<h2 ng-bind="title"></h2>
但是如果我真的想让爬虫“看到”标题怎么办?服务器端渲染是唯一的解决方案吗?
(2022) 尽可能使用服务器端渲染,并使用 Pushstate 生成 URL
Google 现在可以并且将运行 JavaScript,因此只要您创建一个合理的 URL 结构,就可以仅使用 JavaScript 构建一个站点。然而,页面速度已经成为一个越来越重要的排名因素,并且通常在客户端构建的页面在初始渲染时表现不佳。
服务器端渲染 (SSR) 可以通过允许在服务器上预先生成您的页面来提供帮助。您的 html 包含将用作页面根目录的 div,但这不是一个空的 div,它包含 JavaScript 在允许运行时会生成的 html。
客户端下载 HTML 并将其呈现给非常快速的初始加载,然后它执行 JavaScript,在称为水合的过程中用生成的内容替换根 div 的内容。
许多较新的框架都内置了 SSR,尤其是 NextJS。
(2015) 使用 PushState 和预合成
当前(2015 年)执行此操作的方法是使用 JavaScript pushState 方法。
PushState 在不重新加载页面的情况下更改顶部浏览器栏中的 URL。假设您有一个包含选项卡的页面。选项卡隐藏和显示内容,并且内容是动态插入的,可以使用 AJAX 或通过简单地设置 display:none 和 display:block 来隐藏和显示正确的选项卡内容。
单击选项卡时,使用 pushState 更新地址栏中的 URL。呈现页面时,使用地址栏中的值来确定要显示哪个选项卡。 Angular 路由会自动为您执行此操作。
预合成
有两种方法可以访问 PushState 单页应用程序 (SPA)
通过 PushState,用户单击 PushState 链接,内容被 AJAX 处理。通过直接点击 URL。
网站上的初始点击将涉及直接点击 URL。随着 PushState 更新 URL,随后的点击将简单地在内容中进行 AJAX。
爬虫从页面中获取链接,然后将它们添加到队列中以供以后处理。这意味着对于爬虫来说,服务器上的每次点击都是直接点击,它们不通过 Pushstate 导航。
Precomposition 将初始负载捆绑到来自服务器的第一个响应中,可能作为 JSON 对象。这允许搜索引擎在不执行 AJAX 调用的情况下呈现页面。
有一些证据表明 Google 可能不会执行 AJAX 请求。更多关于这里:
搜索引擎可以读取和执行 JavaScript
谷歌已经能够解析 JavaScript 有一段时间了,这就是他们最初开发 Chrome 的原因,作为谷歌蜘蛛的全功能无头浏览器。如果链接具有有效的 href 属性,则可以将新 URL 编入索引。没有什么可做的了。
如果单击链接另外触发了 pushState 调用,则用户可以通过 PushState 导航站点。
PushState URL 的搜索引擎支持
Google 和 Bing 目前支持 PushState。
谷歌
这是 Matt Cutts 对 Paul Irish 关于 PushState for SEO 的问题的回应:
以下是 Google 宣布对蜘蛛的全面 JavaScript 支持:
http://googlewebmastercentral.blogspot.de/2014/05/understanding-web-pages-better.html
结果是 Google 支持 PushState 并将索引 PushState URL。
另请参阅 Google 网站管理员工具的 fetch as Googlebot。你会看到你的 JavaScript(包括 Angular)被执行了。
必应
以下是 Bing 于 2013 年 3 月宣布支持漂亮的 PushState URL:
http://blogs.bing.com/webmaster/2013/03/21/search-engine-optimization-best-practices-for-ajax-urls/
不要使用 HashBangs #!
Hashbang URL 是一个丑陋的权宜之计,要求开发人员在特殊位置提供网站的预渲染版本。它们仍然有效,但您不需要使用它们。
Hashbang URL 如下所示:
domain.example/#!path/to/resource
这将与这样的元标记配对:
<meta name="fragment" content="!">
Google 不会以这种形式将它们编入索引,而是会从 escaped_fragments URL 中提取站点的静态版本并将其编入索引。
Pushstate URL 看起来像任何普通的 URL:
domain.example/path/to/resource
不同之处在于 Angular 通过拦截对在 JavaScript 中处理它的 document.location 的更改来为您处理它们。
如果您想使用 PushState URL(并且您可能会这样做),请删除所有旧的哈希样式 URL 和元标记,并在您的配置块中简单地启用 HTML5 模式。
测试您的网站
谷歌网站管理员工具现在包含一个工具,它允许您以谷歌的身份获取 URL,并在谷歌呈现它时呈现 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
可以验证,在 this link,内容已编入索引并出现在 Google 中。
服务 404 和 301 标头状态代码
因为搜索引擎总是会针对每个请求访问您的服务器,所以您可以从您的服务器提供标头状态代码并期望 Google 看到它们。
2014 年 5 月更新
Google 抓取工具now executes javascript - 您可以使用 Google Webmaster Tools 更好地了解 Google 如何呈现您的网站。
原始答案
如果您想针对搜索引擎优化您的应用,那么很遗憾,没有办法向爬虫提供预渲染版本。您可以阅读更多关于 Google 对 ajax 和 javascript-heavy 网站的建议here。
如果这是一个选项,我建议您阅读 this article,了解如何使用服务器端渲染为 Angular 进行 SEO。
我不确定爬虫在遇到自定义标签时会做什么。
page.content
并返回静态 html。
让我们确定一下 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
我们可以使用 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_
时,我们可以交付我们为页面拍摄的静态 HTML 快照,而不是仅通过 JS 的预渲染页面。这要求我们有一个后端来交付我们的页面,中间有条件逻辑。我们可以使用类似 prerender.io's 的后端作为起点来自己运行它。当然,我们仍然需要处理代理和片段处理,但这是一个好的开始。
有偿服务
将内容放入搜索引擎的最简单、最快捷的方法是使用服务 Brombone、seo.js、seo4ajax 和 prerender.io,这些服务将为您托管上述内容呈现。当我们不想处理运行服务器/代理时,这是一个不错的选择。此外,它通常超级快。
有关 Angular 和 SEO 的更多信息,我们在 http://www.ng-newsletter.com/posts/serious-angular-seo.html 上写了一个详尽的教程,我们在 ng-book: The Complete Book on AngularJS。在 ng-book.com 中查看。
您真的应该在 moo 博客年查看有关构建对 SEO 友好的 AngularJS 网站的教程。他将引导您完成 Angular 文档中列出的所有步骤。 http://www.yearofmoo.com/2012/11/angularjs-and-seo.html
使用这种技术,搜索引擎会看到扩展的 HTML 而不是自定义标签。
这已经发生了翻天覆地的变化。
如果你使用:$locationProvider.html5Mode(true);你已经准备好了。
不再渲染页面。
#!
的建议。来自文章:“Bing 告诉我,虽然他们仍然支持 #! 最初由 Google 推出的可抓取 AJAX 版本,但他们发现它在很多时候都没有正确实现,他们强烈推荐使用 pushState。”您仍然必须呈现静态 HTML 并为 _escaped_fragment_
URL 提供它。 Bing/Google 不会执行 javascript/AJAX 调用。
_escaped_fragment_
并呈现纯 html 页面。这解决不了任何问题。
自从提出这个问题以来,情况发生了很大变化。现在有一些选项可以让 Google 索引您的 AngularJS 网站。我发现最简单的选择是使用 http://prerender.io 免费服务,该服务将为您生成可浏览的页面并将其提供给搜索引擎。几乎所有服务器端 Web 平台都支持它。我最近开始使用它们,支持也很好。
我与他们没有任何从属关系,这是来自一个快乐的用户。
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 路由和控制器。
我已经决定使用无头浏览器生成快照,尽管我觉得这有点不太理想。
截至目前,谷歌已经改变了他们的 AJAX 抓取提议。
时代变了。今天,只要您不阻止 Googlebot 抓取您的 JavaScript 或 CSS 文件,我们通常能够像现代浏览器一样呈现和理解您的网页。
tl;dr:[Google] 不再推荐 2009 年提出的 AJAX 抓取提案 [Google]。
谷歌的 Crawlable Ajax Spec,正如其他答案中所引用的,基本上就是答案。
如果您对其他搜索引擎和社交机器人如何处理相同的问题感兴趣,我在这里写下了最新技术:http://blog.ajaxsnapshots.com/2013/11/googles-crawlable-ajax-specification.html
我在一家 https://ajaxsnapshots.com 工作,这家公司将 Crawlable Ajax 规范作为一项服务来实施 - 该报告中的信息基于我们日志中的观察结果。
我找到了一个优雅的解决方案,可以涵盖您的大部分基础。我最初写了 here 并回答了另一个类似的 Stack Overflow 问题 here,它引用了它。
仅供参考,此解决方案还包括硬编码的后备标签,以防爬虫未拾取 JavaScript。我没有明确概述它,但值得一提的是,您应该激活 HTML5 模式以获得正确的 URL 支持。
另请注意:这些不是完整的文件,只是相关文件的重要部分。我无法帮助编写指令、服务等的样板文件。
应用程序示例
您可以在此处为每个路线(标题、描述等)提供自定义元数据。
$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' }
})
元数据-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);
}
};
索引.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 在服务器端生成纯 HTML 意味着无 javascript 页面并无延迟地将它们提供给用户。因此,您可以处理任何爬虫、机器人和用户(cpu 和网络速度已经很低)。然后您可以通过链接/按钮将它们重定向到已经加载的实际 angular 应用程序。官方网站推荐此解决方案。 -More info about SEO and Angular Universal-
使用 PreRender 之类的东西,它可以制作您网站的静态页面,以便搜索引擎对其进行索引。
您可以在此处了解它适用于哪些平台:https://prerender.io/documentation/install-middleware#asp-net
爬虫(或机器人)旨在爬取网页的 HTML 内容,但由于异步数据获取的 AJAX 操作,这成为一个问题,因为它需要一些时间来呈现页面并在其上显示动态内容。同样,AngularJS
也使用异步模型,这会给 Google 抓取工具带来问题。
一些开发人员使用真实数据创建基本的 html 页面,并在抓取时从服务器端提供这些页面。我们可以在具有 _escaped_fragment_
的服务端使用 PhantomJS
呈现相同的页面(因为 Google 在我们的网站 URL 中查找 #!
,然后将 #!
之后的所有内容添加到 _escaped_fragment_
查询参数中)。有关详细信息,请阅读此blog。
爬虫不需要功能丰富、风格漂亮的 gui,它们只想查看内容,因此您无需向它们提供为人类构建的页面的快照。
我的解决方案:为爬虫提供爬虫想要的东西:
你必须想好爬虫想要什么,然后只给他那个。
提示不要弄乱背部。只需使用相同的 API 添加一点服务器端的前视图