AngularJS SEO

ATUALIZAÇÃO
Conforme observado por birkof nos comentários, em maio de 2014 o Google anunciou um melhor suporte para Javascript ( http://googlewebmastercentral.blogspot.de/2014/05/understanding-web-pages-better.html ). Isso torna esta postagem um tanto obsoleta, embora ainda se aplique ao Bing e outros mecanismos de pesquisa.

Postagem original aqui:

Este é um relatório da minha pesquisa sobre o estado da arte do AngularJS SEO em janeiro de 2014.

Problema :

  • Os bots de busca não executam o javascript e, portanto, tudo o que veem é o template vazio do aplicativo. Não há problema se o aplicativo for protegido por senha (as páginas não seriam indexadas de qualquer maneira), mas é inaceitável se quisermos que as páginas de nosso aplicativo sejam indexadas em mecanismos de pesquisa

Soluções :

  • A solução mais fácil é não fazer um SPA em primeiro lugar . Temos que levar em conta esse problema ao decidir se faremos um SPA ou um aplicativo MVC tradicional do lado do servidor.

  • A alternativa a esta solução é ter algum componente do lado do servidor para servir o conteúdo renderizado aos robôs de pesquisa

Servir o conteúdo renderizado para os bots de pesquisa criará dois novos problemas para nós:

  • Como o bot de pesquisa pode descobrir os URLs a serem indexados
  • Como podemos servir o DOM final em vez do modelo vazio para o bot de pesquisa.

Dizendo ao robô de pesquisa o que indexar

Um bot de pesquisa possui duas maneiras de determinar as URLs a serem indexadas em um site da web: uma é rastrear o site (seguindo os links) e a outra é ler a lista de URLs de um arquivo (um arquivo robotx.txt ou sitemap.xml). Essas abordagens não são mutuamente exclusivas e podem ser usadas juntas.

Ajax rastejando

Podemos tornar nossos aplicativos ajax rastreáveis seguindo algumas convenções e, em seguida, exibindo um instantâneo da página para nossos usuários. Se seguirmos as convenções, o rastreador da web enviará uma solicitação quando encontrar um url para que possamos detectar uma solicitação de bot de pesquisa no servidor. Claro, ainda precisamos de uma maneira de resolver a solicitação, mas esse é outro problema.http://www.example.com?_escaped_fragment_=/products/123http://www.example.com/#!/products/123_escaped_fragment_

NOTA: Como ylesaout aponta em seu comentário, o! na URL é importante porque é o que informa ao robô de pesquisa que essa URL é rastreável (ou seja, que pode solicitar a fragment_ URL com escape do servidor). Caso contrário, o bot irá apenas ignorar o URL.

Mapas do site

Também podemos ter um arquivo de mapa de site se quisermos um controle mais preciso sobre o comportamento do bot de pesquisa (em vez de seguir cegamente os links, ele indexará tudo o que decidimos colocar no arquivo de mapa de site). Também podemos fazer coisas interessantes como indexar uma página que não está vinculada a nenhum lugar (por exemplo, porque ela é acessada depois de clicar em um botão) ou ter algum controle sobre a url que está listada nos resultados da pesquisa.

O problema dessa abordagem é que temos que gerar esse arquivo de mapa do site e ele vai depender da nossa lógica de roteamento e, portanto, é uma solução totalmente ad-hoc que será diferente para cada projeto. Podemos automatizar isso, entretanto, até certo ponto.

Renderização do lado do servidor

Embora esteja no roteiro , não temos nada como Rendr para AngularJS e, até onde eu sei, a maneira mais fácil de renderizar as páginas no servidor sem duplicar o código é executar o aplicativo dentro de um navegador sem cabeça (como PhantomJS ), navegue até o url apropriado, deixe o angular fazer a coisa certa e, em seguida, leia o DOM resultante e envie-o para o rastreador.

Esta é a abordagem utilizada no tutorial yearofmoo angularjs seo e neste projeto do github . Outra possibilidade (discutida aqui ) é usar jsdom em vez de PhantomJS, mas a ideia é mais ou menos a mesma (execute o aplicativo no servidor e tire um instantâneo do DOM resultante).

Essa geração pode ser feita sob demanda sempre que o servidor detecta uma solicitação de um bot de busca, mas acho que o desempenho dessa abordagem será muito ruim (temos que iniciar um navegador, carregar o aplicativo e navegar até a visualização) e, assim , minha recomendação é gerar previamente os instantâneos e armazená-los como arquivos html.

A maneira mais fácil de gerar e armazenar esses instantâneos é usar um serviço como prerender.io ou BromBone (há mais aqui ) e todos funcionam de maneira semelhante: apontamos o serviço para nosso url e ele rastreia nosso aplicativo em intervalos regulares , armazena o DOM gerado em algum lugar e, em seguida, configura nosso servidor da web para servir os instantâneos gerados sempre que uma solicitação de um bot de pesquisa chegar.

Se não quisermos usar um serviço externo, podemos usar um script para rastrear nosso site (ou ler nosso arquivo sitemap.xml), renderizar os instantâneos e armazená-los em um local onde possamos, mais tarde, recuperá-los do servidor (como o sistema de arquivos ou um cache compartilhado).

Encontrei dois plug-ins Grunt para fazer isso como parte de nossa construção (poderíamos fazer isso em nosso servidor de CI):

  • grunt-html-snapshots lerá seu arquivo robots.txt ou sitemap.xml (você ainda precisa gerá-los) e os renderizará em um navegador phantomjs. Também está disponível como uma biblioteca
  • grunt-html-snaphot (sem os s finais). Parece-me que este fará o rastreamento para você, em vez de depender do arquivo sitemap.xml, mas preciso verificar isso.

Você também deve verificar este projeto no github

Trabalho posterior

  • Configure um aplicativo de prova de conceito para a abordagem de instantâneo grunt
  • Pesquise mais sobre a possibilidade de usar esses snapshots para fazer o aplicativo inicializar mais rápido (a la twitter ) e também como cache.

ATUALIZAR

Depois de configurar um aplicativo de prova de conceito, descobri que:

  • Este artigo ( http://www.ng-newsletter.com/posts/serious-angular-seo.html ) explica as diferentes opções muito bem

  • Nenhum dos plug-ins Grunt que mencionei faria a parte de rastreamento, embora não seja muito difícil implementá-lo sozinho (consulte https://gist.github.com/joseraya/8547524 )

  • O problema com a geração de instantâneos durante a construção é que pode levar muito tempo (especialmente se você tiver muitas páginas) porque você tem que esperar que as páginas sejam renderizadas (e não é fácil ter certeza de que uma página foi completamente renderizada) . Eu prefiro fazer a geração do instantâneo como uma etapa independente

  • Zombie.js não é necessariamente mais rápido do que phantomjs (pelo menos prerender.io com seu serviço fantasma é rápido o suficiente em comparação com meu script zombie.js)

  • É muito fácil implementar a solução do prerender.io (com seu próprio servidor ou com a solução hospedada) e a solução hospedada não é muito cara (100.000 páginas custam mais ou menos o mesmo que o dinamômetro heroku que você pode usar para executar o seu próprio servidor)

  • O middleware Prerender-node tem uma API muito boa e parece ser fácil de implementar seus próprios plug-ins (se você precisar fazer isso).

Para o projeto para o qual eu estava fazendo a pesquisa, definitivamente tentaremos o prerender como nossa solução para o problema de otimização de SEO no AngularJS.