evilbloodydemon Умный парень

SEO для сайта на AngularJS своими руками

Одностраничные приложения на AngularJS всем хороши, кроме того, что поисковые системы пока что не могут (или не хотят) нормально индексировать их страницы. Часть ботов не умеет яваскрипт полностью, другие - в очень ограниченном объёме.

В наличии имеется AJAX сайт за nginx на CentOS 6, хочется обойтись минимальными телодвижениями для обеспечения его индексации. Основная идея - исполнять приложение в каком-нибудь headless браузере и отдавать результат поисковику.

Подготовка к сканированию

В секцию head главной страницы вставляем <meta name=”fragment” content=”!” />. Увидев такое, поисковики понимают, что на этой странице есть содержимое, которое генерируется яваскриптом, но при этом доступное по запросу, сформированному специальным образом. Подробности и полная спецификация есть у гугла и яндекса.

Вкратце, происходит вот что:

Рендеринг HTML

Для получения хтмл будем использовать PhantomJS. Проще всего его установить через npm, у меня он уже был установлен, если у вас нет, то yum install npm или что-то этом роде, а потом

npm install phantomjs

PhantomJS управляется яваскриптом и к тому же имеет встроенный веб-сервер, чем мы и воспользуемся. Вот простейший скрипт для отрисовки страниц:

var server = require('webserver').create();
var port = 19003;

var getPage = function(url, callback) {
    var page = require('webpage').create();

    // запрашиваем страницу, ждём 200 мс, чтобы ангуляр успел завершить работу
    // можно попросить сайт отмечать готовность самостоятельно, но это потребует изменения приложения
    // а у нас все-таки простое решение
    page.open(url, function() {
    	setTimeout(function() {
            page.evaluate(function() {
                // удаляем мета-тэг фрагмента, иначе контент не будет проиндексирован
                // заодно удалим скрипты, потому что отдаём готовый html
                $('meta[name=fragment], script').remove()
            });

            callback(page.content);
            page.close();
    	}, 200);
    });
};

// запускаем встроенный в phantomjs веб-сервер
server.listen(port, function(request, response) {
    response.headers = {
        'Content-Type': 'text/html'
    };

    var regexp = /_escaped_fragment_=(.*)$/;
    var fragment = request.url.match(regexp);

    var url = 'http://localhost:19002/#!' + decodeURIComponent(fragment[1]);

    // получаем хтмл и отдаём роботу
    getPage(url, function(content) {
        response.statusCode = 200;
        response.write(content);
        response.close();
    })
});

Запускаем скрипт

phantomjs renderbot.js

Проверяем результат работы

wget http://localhost:19003/?_escaped_fragment_=/movies/1

Можно и из браузера. Должен вернуться html полноценной страницы.

Отправлем решение на живой сервер

В nginx внесем небольшие изменения

if ($args ~* _escaped_fragment_) {
    proxy_pass http://localhost:19003; #phantomjs
}

proxy_pass http://127.0.0.1:19002; #оригинальный сайт

В общем, алогритм такой:

Что дальше?

В посте представлено решение в общих чертах, его уже можно применять в продакшене. Но вожможно, потребуется добавить кэширование отрисованных страниц и обработку ошибок (таймауты и тому подобное) в скрипте phantomjs. Можно сделать это самому, а можно воспользоваться различными сервисами, которые все сделают сами за некоторое вознаграждение, например, prerender.io сотоварищи. It's up to you.