Делаем Angular.js сайт доступным для роботов. Часть 1

Здравствуйте, уважаемые хабровчане.

Многие из вас, уверен, не раз сталкивались с проблемой, когда сайт, полностью работающий на JS, например, связка Angular.js + UI-router, для роботов извне (индексатор Google'а, краулеры Facebook'a или Twitter'a) доступен в самом что ни на есть неподобающем виде.

В нескольких статьях я постараюсь вам рассказать, как с помощью нехитрых приемов можно все-таки отбросить «non-SEO-friendly» как причину не разрабатывать с учетом тенденций в мире веб-разработки.

В этой части я постараюсь описать как я боролся с краулерами Facebook'a и Twitter'a, чтобы мои ссылки в постах в этих соцсетях выглядели привлекательно и люди заходили.

Если эта тема вам интересна, прошу под кат.

Конечно же Вы видели в своей ленте в Facebook большие и привлекающие внимание сообщения и очень себе такое хотели. Вроде таких:



Недружелюбность SEO к JavaScript'у, зачастую, отталкивает разработчиков от разработки сайта, использующего полную силу JavaScript'a в том виде, в котором он сейчас есть. Использование HTML5 History API, переходы по ссылкам без перезагрузки страницы и прочее, и прочее остается лишь для веб-приложений.

Очень жаль, но прослеживается такая тенденция, что сайты завязанные на JavaScript'e не пригодны для ресурсов, которые зависят от SEO довольно сильно, как то: интернет-магазины, новостные, информационные ресурсы и так далее.

Почему так происходит?
Потому что роботы не исполняют JavaScript.
То есть, скормив ссылку на страницу на нашем сайте краулеру Facebook'a, мы получим результат аналогичный как и для самого index.html, который лежит в корне.

Допустим, Вы, как и я решили создать на Angular.js с использованием ui-router что-то похожее на блог. Допустим, в добавок мы хотим, чтобы только браузер знал, что переходы по Вашему сайту осуществляются с помощью AJAX'a и включим так называемый HTML5 Mode для ui-router. Как в таком случае будет выглядеть index.html? Да примерно так:

<!doctype html>
<html class="no-js">
  <head>
    <meta charset="utf-8">
    <base href="/">
    <title></title>
    <!-- Перечень стилей -->
  </head>
  <body ng-app="app">
    <div ui-view=""></div>
    <!-- И тут простыня из подключаемых javascript файлов -->
  </body>
</html>

Для работы с HTML5 History API нужно во время конфигурации модуля основного приложения указать это:
angular.module('app', [
    ...
    'ui.router',
    ...
])
    .config(function($locationProvider) {
        ...
        $locationProvider.html5Mode(true);
        ...
    })

Приступаем.

Для дебага мы можем использовать готовые инструменты:

Пытаемся скормить какую-либо страницу Facebook debugger'у и понимаем, что все-таки да, JS не выполняется.
Как быть?

Первое, что приходит в голову — редирект. Краулеры соцсетей имеют свои User-Agent'ы, которые мы сможем использовать для перенаправления на уже _готовую_ страницу. Т.е. нам нужно рендерить страницу не у клиента, а на сервере, причем не все, а только те, что являются для нас важными, в случае с блогом — это посты. В данном случае нам поможет PHP.

Для этого мы напишем своеобразный прокси, назовем его crawler_proxy.php
<?php
$SITE_ROOT = "http://example.com/";

$jsonData = getData($SITE_ROOT);
makePage($jsonData, $SITE_ROOT);

function getData($siteRoot) {
    $id = ctype_digit($_GET['id']) ? $_GET['id'] : 1;
    $rawData = file_get_contents($siteRoot.'blog/api/get_post/?id='.$id);
    return json_decode($rawData);
}

function makePage($data, $siteRoot) {
    ?>
    <!DOCTYPE html>
    <html>
        <head>
            <meta property="og:title" content="<?php echo $data->post->title; ?>" />
            <meta property="og:description" content="<?php echo strip_tags($data->post->excerpt); ?>" />
            <meta property="og:image" content="<?php echo $data->post->attachments[0]->images->full->url; ?>" />
            <meta property="og:site_name" content="My Blog"/>
            <meta property="og:url" content="http://example.com/posts/<?php echo $data->post->id ?>" />
            <meta property="og:type" content="article"/>
            <!-- etc. -->
        </head>
        <body>
            <h1><?php echo $data->post->title; ?></h1>
            <p><?php echo $data->post->content; ?></p>
            <img src="">
        </body>
    </html>
    <?php
}
?>


В теле тега head прописываются мета-теги с указанием свойства Open Graph'a.

Open Graph — это протокол, набор правил, по которому социальная сеть может построить социальный граф, определить, кто автор статьи и так далее. Подробнее можно прочитать на сайте самого Open Graph'a.

Итак, в моем случае веб-сервером выступает Apache, поэтому можем просто перечислить User-Agent'ы и отправить вместе с ID поста на наш прокси, который вернет нам «сухую» страничку.

В конфигурацию прописываем следующее:

# ------------------------------------------------------------------------------
# | Redirect crawlers to PHP proxy                                            |
# ------------------------------------------------------------------------------
<IfModule mod_rewrite.c>

    RewriteEngine On
    # redirect crawlers
    RewriteCond %{HTTP_USER_AGENT} (Facebot|Google.*snippet|Twitterbot|facebookexternalhit/*)
    RewriteRule posts/(\d+)$ /crawler_proxy.php?id=$1 [L]

</IfModule>


Итак, после этого снова пробуем нашу ссылку с постом отправить в Facebook debugger.

Вы можете увидеть какие-либо предупреждения и исправить их, либо, если все прошло как по маслу — увидеть как будет выглядеть ваша ссылка в постах на Facebook'е.

В случае с Twitter'ом просто необходимо дополнительные мета-теги описать. Их список можно найти на странице самого валидатора: Twitter Card validator.

Если тема окажется интересной — в следующей части я расскажу про индексацию сайта, написанного на JavaScript'е.
Поделиться публикацией

Комментарии 15

    +1
    Такое решение вполне имеет право на сущестование, но существует более универсальный способ в виде рендеринга целевых страниц с помощью PhantomJS и отдачи роботам результирующего HTML. Результат рендеринга, конечно, хорошо бы закешировать.
      0
      Да, об этом стоило упомянуть, my fault.

      Сказать по правде, этот способ был выбран из-за определенных ограничений хостера. В частности, это обычный хостинг, а не VPS, куда можно поставить, что душе угодно. Потому этот способ будет особенно интересен тем, кто хочет пощупать это без приобретения VPS.
      +2
      prerender.io решает эти проблемы(самому в деле, правда, никак не до ведётся испробовать). Автор, кстати, хабровчинин.
        0
        Как я говорил выше, решения со сторонними пакетами не всегда возможны.
          0
          Про сторонние пакеты я упустил из виду.
          Prerander это ещё и сервис, так что его можно и не ставить на свой сервер, а пускать через их сервера. Правда тогда будет иметь место финансовые ограничения :) но бесплатного плана для небольших проектов должно хватать.
        +2
        Есть ещё интересные варианты. Многие (а может даже почти все) поисковики знают, что людям нужна ajax-навигация. И поэтому в целом есть некоторое соглашение (простите меня, может даже спецификация есть нужная, я не вдавался в подробности) о том, что поисковик, видя #!fragment в урле, пытается постучать на сервер по тому же адресу, но с заменой фрагментной части на ?_escaped_fragment_=fragment (Google, Yandex, Bing). И вам просто по этому адресу надо располагать статичный контент страницы.
        Если вы ленивый (или у вас другие причины, как у меня), и не хотите генерировать статичный контент, и, кроме того, вам вообще никак не испытать прелести Phantom JS (например вас жестоко заперли на шаред хостинге), то можно поюзать вот эту штуку ajaxsnapshots.com (не сочтите за рекламу, сам только недавно нашёл). Обычно хотя бы .htaccess вам доступен и его как раз оказывается достаточно.
        P.S. Ой, оказывается выше уже написали про аналогичный Prerender. Ну да ладно, пусть полежит тут альтернативный вариант.
          0
          Потому что роботы не исполняют JavaScript.

          С 2011 года исполняют и яндекс и гугл. Если поискать, можно найти кучу экспериментов по этому поводу. Это если в общем говорить, а не об angular именно, где действительно с этим много проблем. Небольшой пруф.
          Там не так всё хорошо, но, тем не менее javascript исполняется и страницы рендерятся. Этим занимается специальный умный бот, который ходит очень редко и не на все сайты.
          Вот здесь яндекс неоднократно описывал как они это делают и зачем им это нужно помимо обычной индексации. В частности там много механизмов и метрик для борьбы со спамом (чтобы люди не подделывали навигацию, добавляли скрытый контент и т.д.).

          Как раз в 2010-2011 году был бум злоупотребления javascript, когда seo оптимизаторы создавали разную навигацию для поисковиков и людей переправляя внутренние веса от ссылок на определённые страницы.

          Я этот вопрос плотно изучал, поэтому если что, то могу подсказать.
            0
            Ах да, забыл спросить, вы там боролись с facebook и twitter за «SEO-friendly», какое отношение их роботы к SEO имеют? :)
              0
              Вот в случае с Angular все получается достаточно печально в плане поисковиков либо мне так «везло» и ни разу их специальный умный бот ко мне не заходил.

              И, да, это не совсем SEO, но тоже оптимизация, ближе к SMO, но не совсем оно, спасибо.
                +3
                Этому автору бессмысленно задавать вопросы по содержанию «его» постов. Он не знает ответа. Поскольку просто пересказывает своими словами чужие статьи, добавляя немного отсебятины. В данном случае это www.michaelbromley.co.uk/blog/171/enable-rich-social-sharing-in-your-angularjs-app и там про сео как раз ничего нет.

                Кстати, я в сео не специалист, но слышал, что поисковики нервно реагируют, если отдаётся разный контент роботу с обычным юзер-агентом и роботу, маскирующемуся под браузер. Если это так, то решение с редиректом принесет больше вреда, чем пользы. Хотелось бы послушать мнение специалиста.
                  0
                  В данном контексте ни слова не было про оптимизацию под поисковые системы, это во-первых. Во-вторых, «я в сео не специалист, но слышал», нет, никто нервно не реагирует, если делать все по-человечески. В данном случае мы просто подменяем искомый контент для робота и про само существование редиректа никто ничего не знает. И контент в случае с уже отрендеренными страницами будет идентичный тому, что будет видеть пользователь.
                    0
                    Реакция будет зависеть от других факторов, и в некоторых случаях это будет учитываться. Это они раньше плохо относились, лет 8 назад, а сейчас с этим всё сложно.
                    На сайте контент может меняться в зависимости от вашего местонахождения, браузера, реферера в абсолютно безобидных целях и тут сложно грань найти.
                0
                Использование HTML5 History API, переходы по ссылкам без перезагрузки страницы и прочее, и прочее остается лишь для веб-приложений.
                Это в вашем понимании оно так. Тут все зависит от требований и возможностей разработчика.

                Почему так происходит?
                Потому что роботы не исполняют JavaScript.
                Скорее потому что разработчики сайтов не задумываются об этом заранее. Не думаю что уместно тут винить JavaScript, себя нужно винить что пишется все это не обдумав.
                  0
                  А где продолжение?
                    0
                    Коллеги. Статья очень старая. Но вопрос остается актуальным. Хотелось бы узнать мнение профессионалов насчет изменений в 2015. Например depricating ajax crowling от google. Если сайт новый и на ангуларе, как заставить его видеть страницы полностью формируемые фронэндом?

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое