Ajax
JavaScript
Node.JS
Angular
July 2016 28

CRAWL динамических страниц для Google и Яндекс поисковиков (snapshots, _escaped_fragment_, ajax, fragment)

Tutorial
image

Всем мир!

Содержание статьи:

1. Что такое CRAWL
2. Динамический CRAWL
3. Задачи, инструменты, решение
4. Почитать
5. Выводы



1. Что такое CRAWL



Это сканирование страниц сайта поисковыми системами с целью получить необходимую информацию. Результат выполнения данного сканирования является html представление в конечной точке (у каждой поисковой системы свои настройки, а именно грузить или нет js (с запуском или без), css, img и т.д.) или как это ещё называют «снимок» сайта.

2. Динамический CRAWL



Здесь речь будет идти о динамическом CRAWL страницы, а именно когда у вас сайт имеет динамический контент (или как это называют Ajax контент). У меня проект с использованием Angular.js + HTML5 Router (это когда без domain.ru#!path, а вот так domain.ru/path ), весь контент меняется в <ng-view></ng-view>и один единственный index.php и специальные настройки .htaccess, для того, чтобы после обновления страницы всё отобразилось как надо.

Это прописано в настройках роутера angular:
 $locationProvider.html5Mode({
            enabled: true,
            requireBase: false
        });


Это прописано в .htaccess:
RewriteEngine on

        # Don't rewrite files or directories
        RewriteCond %{REQUEST_FILENAME} -f [OR]
        RewriteCond %{REQUEST_FILENAME} -d
        RewriteRule ^ - [L]

        # Rewrite everything else to index.html to allow html5 state links
        RewriteRule ^ index.php [L]


3. Задачи, инструменты, решение



Задачи:

1. Отдавать динамический контент страницы такой, какой он становится после окончания рендеринга и инициализации приложения
2. Формируем, Оптимизируем и Сжимаем html снимок страницы
3. Отдаём поисковой системе html снимок

Инструменты:

1. Установленная NPM (npm — это пакетный менеджер node.js. С его помощью можно управлять модулями и зависимостями.)
2. Установленный модуль html-snapshots с помощью команды:
 npm install html-snapshots

3. Правильная конфигурация

Решение:

Для быстродействия рекомендую выполнять «кравлинг» на localhost (локальном веб-сервере)

Для начала нужно добавить в главный index.php в meta tag в head:
<meta name="fragment" content="!">


Пример sitemap.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!--	created with www.mysitemapgenerator.com	-->
<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
        xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
    <url>
        <loc>http://localhost/domain.ru/www/product/30</loc>
        <lastmod>2016-07-22T19:47:25+01:00</lastmod>
        <priority>1.0</priority>
    </url>
</urlser>


Конфигурация server.js:

var fs = require("fs");
var path = require("path");
var util = require("util");
var assert = require("assert");
var htmlSnapshots = require("html-snapshots");
var minify = require('html-minifier').minify;

htmlSnapshots.run({

    //#1 С использованием SITEMAP
    //input: "sitemap",
    //source: "sitemap_localhost.xml",

    //#2 Массив ссылок на страницы сайта
    input: "array",
    source: ["http://localhost/domain.ru/www/product/30"],
    //protocol: "https",

    // setup and manage the output
    outputDir: path.join(__dirname, "./tmp"),

    //Чистит директорию перед сохранением новых копий
    outputDirClean: false,
    // Селектор любого блока, который находится внутри <ng-view></ng-view> и отображается после инициализации приложения
    selector: "#product",
    //Ограничить время загрузки до 12 секунд, а можно и больше
    timeout: 120000,
    //Настройки помогающие отображать контент быстрее для CRAWL
    phantomjsOptions: [
        "--ssl-protocol=any",
        "--ignore-ssl-errors=true",
        "--load-images=false"
    ]

}, function (err, snapshotsCompleted) {

    var body;

    console.log("completed snapshots:");

    assert.ifError(err);

    snapshotsCompleted.forEach(function(snapshotFile) {

        body = fs.readFileSync(snapshotFile, { encoding: "utf8"});

        //Убираем стили и их содержание
        var regExp = /<style[^>]*?>.*?<\/style>/ig;
        var clearBody = body.replace(regExp, '');

        //Производим замену доменного имени
        var domain = /http:\/\/localhost\/domain.ru\/www/ig;
        clearBody = clearBody.replace(domain, '//domain.ru');

        //Производим оптимизацию html файла
        clearBody = minify(clearBody, {
            conservativeCollapse: true,
            removeComments: true,
            removeEmptyAttributes: true,
            removeEmptyElements: true,
            collapseWhitespace: true
        });
        //Записываем в файл
        fs.open(snapshotFile, 'w', function(e, fd) {
            if (e) return;
            fs.write(fd, clearBody);
        });

    });

});

console.log('FINISH');



Запуск командой:
node server


Понимание алгоритма:

1. Сначала он «кравлит» все страницы
2. Создаёт файлы и называет папки согласно вашему url: product/30/index.hmtl (index.html, а можно и product/30.html кому как удобней на скорость не влияет)
3. После этого вызывает callback -> snapshotsCompleted, где производит оптимизацию каждого index.html снимка вашей страницы

Снимки вашего сайта подготовлены, осталось отдать их поисковому боту при заходе:

index.php
if (isset($_GET['_escaped_fragment_'])) {
    if ($_GET['_escaped_fragment_'] != ''){
        $val = $_GET['_escaped_fragment_'];
        include_once "snapshots" . $val . '/index.html';
    }else{
        $url = "https://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"];
        $arrUrl = parse_url($url);
        $val = $arrUrl['path'];
        include_once "snapshots" . $val . '/index.html';
    }
}else {
    include_once('pages/home.php');
}


Объяснение:

1. html5 push state
If you use html5 push state (recommended):
Just add this meta tag to the head of your pages
<meta name="fragment" content="!"> 


If your URLs look like this:
www.example.com/user/1
Then access your URLs like this:
www.example.com/user/1?_escaped_fragment_=

2. hashbang
If you use the hashbang (#!):
If your URLs look like this:
www.example.com/#!/user/1

Then access your URLs like this:
www.example.com/?_escaped_fragment_=/user/1

Дополнительно, для тех у кого есть снимки, но нет оптимизации:

var fs = require("fs");
var minify = require('html-minifier').minify;
var path = require("path");
var util = require("util");
var assert = require("assert");
var htmlSnapshots = require("html-snapshots");

//Получение списка папок
var myPath = path.join(__dirname, "./tmp/domain.ru/www/");

function getFiles (dir, files_){
    files_ = files_ || [];
    var files = fs.readdirSync(dir);
    for (var i in files){
        var name = dir + '/' + files[i];
        if (fs.statSync(name).isDirectory()){
            getFiles(name, files_);
        } else {
            files_.push(name);
        }
    }
    return files_;
}

var allFiles = getFiles(myPath);
//var allFiles = [ 'C:\\xampp\\htdocs\\nodejs\\crawler\\tmp\\domain.ru\\www\\/product/30/index.html' ];
var body;
allFiles.forEach(function(snapshotFile){

    body = fs.readFileSync(snapshotFile, { encoding: "utf8"});

    var regExp = /<style[^>]*?>.*?<\/style>/ig;
    var clearBody = body.replace(regExp, '');

    var domain = /http:\/\/localhost\/domain.ru\/www/ig;
    clearBody = clearBody.replace(domain, '//domain.ru');

    clearBody = minify(clearBody, {
        conservativeCollapse: true,
        removeComments: true,
        removeEmptyAttributes: true,
        removeEmptyElements: true,
        collapseWhitespace: true
    });

    var social = /<ul class=\"social-links\">.*?<\/ul>/ig;
    clearBody = clearBody.replace(social, '');

    fs.open(snapshotFile, 'w', function(e, fd) {
        if (e) return;
        fs.write(fd, clearBody);
    });

});

console.log('COMPLETE');


4. Почитать



stackoverflow.com/questions/2727167/getting-all-filenames-in-a-directory-with-node-js — работа с файлами в node.js
github.com/localnerve/html-snapshots — snapshots module doc
perfectionkills.com/experimenting-with-html-minifier — options snapshots module doc

yandex.ru/support/webmaster/robot-workings/ajax-indexing.xml — yandex crawler info
developers.google.com/webmasters/ajax-crawling/docs/specification — google crawler info

www.ng-newsletter.com/posts/serious-angular-seo.html — article
prerender.io/js-seo/angularjs-seo-get-your-site-indexed-and-to-the-top-of-the-search-results — article
prerender.io/documentation — article

regexr.com — regexr
stackoverflow.com/questions/15618005/jquery-regexp-selecting-and-removeclass — regexr

5. Выводы



Теперь смело можно писать любые SPA приложения не беспокоясь за их «кравлинг» поисковым ботом, также вы можете подобрать под свой набор инструментов нужную конфигурацию как для «сервера», так и для «клиента»!

Всем профессиональных успехов!
Оценить:
20.5% 1 7
5.8% 2 2
14.7% 3 5
8.8% 4 3
14.7% 5 5
0% 6 0
2.9% 7 1
2.9% 8 1
0% 9 0
29.4% 10 10
34 users voted. 51 user abstained.
+6
12.5k 69
Comments 9
Top of the day