用于静态网页的AngularJS SEO(S3 CDN)

时间:2014-04-13 13:30:05

标签: javascript angularjs amazon-web-services amazon-s3 seo

我一直在研究如何改进托管在像Amazon S3这样的CDN上的angularJS应用程序的SEO(即没有后端的简单存储)。大多数解决方案PhantomJSprerender.ioseo.js等都依赖于后端来识别抓取工具生成的?_escaped_fragment_网址,然后获取相关网页来自其他地方即使您提前生成快照页,即使grunt-html-snapshot最终也需要您这样做。

solution基本上依赖于使用cloudflare作为反向代理,这似乎有点浪费,因为他们的服务提供的大多数安全设备等对于静态站点来说是完全冗余的。根据建议here自己设置反向代理也似乎有问题,因为它需要i)路由所有AngularJS应用程序我需要静态html通过一个代理服务器,这可能会妨碍性能或ii)设置一个单独的代理每个应用程序的服务器,此时我也可以设置一个后端,这在我工作的规模上是不可承受的。

无论如何这样做,或者是静态托管AngularJS应用程序与伟大的SEO基本上是不可能的,直到谷歌更新他们的爬虫?


在John Conde的评论之后转发webmasters

5 个答案:

答案 0 :(得分:3)

实际上这是一项确实非常麻烦的任务,但我已经设法让我的AngularJS SPA站点(托管在AWS S3上)在http://www.jobbies.co/上很好地工作。主要思想是预先生成内容并将其填充到HTML中。当页面加载并且预渲染的内容将被替换时,模板仍将被加载。

您可以在http://www.ericluwj.com/2015/11/17/seo-for-angularjs-on-s3.html详细了解我的解决方案,但请注意有很多条件。

答案 1 :(得分:2)

如果你以有趣的方式使用ng-cloak,可能会有一个很好的解决方案。

我自己没有尝试过,但它应该在理论上起作用

解决方案高度依赖于CSS,但它应该非常好。 例如,角度应用程序中有三种状态: - index(路径名:#/) - about(路径名:#/ about) - 联系(路径名:#/联系人)

索引的基本情况也可以添加,但是会很棘手所以我现在就把它留下来。

让您的HTML看起来像这样:

<body>
    <div ng-app="myApp" ng-cloak>
        <!-- Your whole angular app goes here... -->
    </div>
    <div class="static">
        <div id="about class="static-other">
            <!-- Your whole about content here... -->
        </div>
        <div id="contact" class="static-other">
            <!-- Your whole contact content here... -->
        </div>
        <div id="index" class="static-main">
            <!-- Your whole index content here... -->
        </div>
    </div>
</body>

(重要的是你把你的索引案例放在最后,如果你想让它更棒的话)

下一步让你的CSS看起来像这样: -

[ng-cloak], .static { display: none; }
[ng-cloak] ~ .static { display: block; }

无论如何,这对你来说可能会运作得很好。 mg-cloak指令将在未加载角度时隐藏您的角度应用程序,并将显示您的静态内容。 Google会在HTML中获取您的静态内容。 作为奖励,最终用户还可以在角度加载时看到样式静态内容。

如果您开始使用CSS中的目标伪选择器,则可以获得更多创意。您可以在静态内容中使用实际链接,但只需将它们链接到各种ID即可。所以在#index div中确保你有#about和#contact的链接。请注意链接中缺少的“/”。 HTML id不能以斜杠开头。

然后让你的CSS看起来像这样:

[ng-cloak], .static { display: none; }
[ng-cloak] ~ .static { display: block; }
.static-other {display: none;}
.static-other:target {display: block;}
.static-other:target ~ .static-main {display: none;}

现在你有一个功能齐全的静态应用程序WITH ROUTINg在角度启动之前有效。

作为额外奖励,当角度启动时,它足够聪明,可以自动将#about转换为#/约,并且体验甚至根本不会破坏。

另外,当然,不要忘记SEO问题已经完全解决了。我还没有使用过这种技术,因为我总是有一台服务器可供配置,但我对你的工作方式非常感兴趣。

希望这有帮助。

答案 2 :(得分:2)

以下是如何使您的应用程序在S3等存储服务上具有搜索引擎优化功能的完整概述,其中包含很好的网址(没有#),以及在构建完成后执行简单命令的所有内容:

grunt seo

它仍然是一个解决方法的难题,但它的工作和它是你能做的最好的。感谢@ericluwj和他的博客,他激励了我。

概述

目标&amp;网址结构

目标是在角度应用中为每个州创建1个html文件。唯一的主要假设是你删除了#&#39;#&#39;通过使用html5history(您应该这样做)来自您的网址,并且您的所有路径都是绝对的或使用角度状态。有很多帖子解释了如何这样做。

Urls以这样的尾部斜杠结束     http://yourdomain.com/page1/

就个人而言,我确保http://yourdomain.com/page1没有尾随斜杠)也到达目的地,但这里的主题不在此处。我还确保每种语言都有不同的状态和不同的URL。

SEO逻辑

我们的目标是当有人通过http请求到达您的网站时:

  • 如果它是搜索引擎抓取工具:将他留在包含所需html的页面上。该页面还包含角度逻辑(例如,启动您的应用程序),但爬虫无法读取,因此他故意卡住您为他提供的HTML并将其编入索引。
  • 对于普通人和智能机器:确保角度被激活,删除生成的html并正常启动你的应用程序

笨拙的任务

这里我们讨论了咕噜声的任务:

  //grunt plugins you will need:
  grunt.loadNpmTasks('grunt-prerender');
  grunt.loadNpmTasks('grunt-replace');
  grunt.loadNpmTasks('grunt-wait');
  grunt.loadNpmTasks('grunt-aws-s3');

  //The grunt tasks in the right order
  grunt.registerTask('seo', 'First launch server, then prerender and replace', function (target) {
    grunt.task.run([
      'concurrent:seo' //Step 1: in parrallel launch server, then perform so-called seotasks
    ]);
  });

  grunt.registerTask('seotasks', [
    'http', //This is an API call to get all pages on my website. Skipping this step in this tutorial.
    'wait', // wait 1.5 sec to make sure that server is launched
    'prerender', //Step 2: create a snapshot of your website
    'replace', //Step 3: clean the mess
    'sitemap', //Create a sitemap of your production environment
    'aws_s3:dev' //Step 4: upload
  ]);

步骤1:使用concurrent:seo

启动本地服务器

我们首先需要启动本地服务器(如grunt服务),以便我们可以拍摄我们网站的快照。

//grunt config
concurrent: {
  seo: [
    'connect:dist:keepalive', //Launching a server and keeping it alive
    'seotasks' //now that we have a running server we can launch the SEO tasks
  ]
}

第2步:使用grunt prerender创建网站快照

grunt-prerender插件允许您使用PhantomJS拍摄任何网站的快照。在我们的例子中,我们想要拍摄我们刚刚推出的localhost网站的所有页面的快照。

//grunt config
prerender: {
  options: {
    sitePath: 'http://localhost:9001', //points to the url of the server you just launched. You can also make it point to your production website.
    //As you can see the source urls allow for multiple languages provided you have different states for different languages (see note below for that)
    urls: ['/', '/projects/', '/portal/','/en/', '/projects/en/', '/portal/en/','/fr/', '/projects/fr/', '/portal/fr/'],//this var can be dynamically updated, which is done in my case in the callback of the http task
    hashed: true,
    dest: 'dist/SEO/',//where your static html files will be stored
    timeout:5000,
    interval:5000, //taking a snapshot of how the page looks like after 5 seconds.
    phantomScript:'basic',
    limit:7 //# pages processed simultaneously 
  }
}

步骤3:使用grunt replace

清理混乱

如果打开预渲染文件,它们将适用于爬虫,但不适用于人类。对于使用chrome的人来说,你的指令会加载两次。因此,您需要在角度激活之前(即,在头部之后)将智能浏览器重定向到您的主页

//Add the script tag to redirect if we're not a search bot
replace: {
  dist: {
    options: {
      patterns: [
        {
          match: '<head>',
          //redirect to a clean page if not a bot (to your index.html at the root basically).
          replacement: '<head><script>if(!/bot|googlebot|crawler|spider|robot|crawling/i.test(navigator.userAgent)) { document.location = "/#" + window.location.pathname; }</script>'
          //note: your hashbang (#) will still work.
        }
      ],
      usePrefix: false
    },
    files: [
      {expand: true, flatten: false, src: ['dist/SEO/*/**/*.html'], dest: ''} 
    ]
  }

还要确保在你的ui-view元素的index.html中有这个代码,它会在角度开始之前清除所有生成的html指令。

<div ui-view autoscroll="true" id="ui-view"></div>

<!-- this script is needed to clear ui-view BEFORE angular starts to remove the static html that has been generated for search engines who cannot read angular -->
<script> 
  if(!/bot|googlebot|crawler|spider|robot|crawling/i.test( navigator.userAgent)) { document.getElementById('ui-view').innerHTML = ""; }
</script>

第4步:上传到aws

首先上传包含您的版本的dist文件夹。然后用你预先渲染和更新的文件覆盖它。

aws_s3: {
  options: {
    accessKeyId: "<%= aws.accessKeyId %>", // Use the variables
    secretAccessKey: "<%= aws.secret %>", // You can also use env variables
    region: 'eu-west-1',
    uploadConcurrency: 5, // 5 simultaneous uploads
  },
  dev: {
    options: {
      bucket: 'xxxxxxxx'
    },
    files: [
      {expand: true, cwd: 'dist/', src: ['**'], exclude: 'SEO/**', dest: '', differential: true},
      {expand: true, cwd: 'dist/SEO/', src: ['**'], dest: '', differential: true},
    ]
  }
}

就是这样,你有解决方案!人类和机器人都可以阅读您的网络应用

答案 3 :(得分:2)

由于AWS提供Lambda @ Edge作为服务,我们可以在不使用grunt或其他任何内容的情况下处理此问题。 (至少基本的东西)

我尝试了Lambda @ Edge并按预期工作,在我的情况下,我只是将所有路由设置为Lambda @ Edge中的“/”(除了文件存在于s3中,如css,图像等)。

我设置的Lambda事件是“viewerRequest”,以下是代码。

'use strict';

exports.handler = (event, context, callback) => {
    console.log("Event received is", JSON.stringify(event));
    console.log("Context received is", context);
    const request = event.Records[0].cf.request;
    if (request.uri.endsWith(".rt")) {
        console.log("URI is matching with .rt, the URI is ", request.uri);
        request.uri = "/";
    } else {
        console.log("URI is not ending with rt so letting it go URI is", request.uri);
    }
    console.log("Final request URI is", request.uri);
    callback(null, request);
};

登录云计算很难检查,因为日志填充在云计算区域,该区域更靠近处理请求的边缘位置。

对于前。虽然这个Lambda是为我们东部署/编写的,但是当我从新加坡访问云端时,我在ap-south地区看到了这个。 在Google网站管理员工具的“以谷歌搜索获取”选项中进行了检查,页面正在按预期呈现和查看。

答案 4 :(得分:0)

我一直在寻找能找到解决方案的日子。据我所知,问题没有很好的解决方案。我希望firebase最终能够启用用户代理重定向。如果你有钱,你可以使用MaxCDN企业。它们提供边缘规则,其中包括用户代理的重定向。

https://www.maxcdn.com/features/rules/