Новогодние праздники 666+666+666+6+6+6-го года в самом разгаре. За серьезные вещи совсем не хочется браться. Зато можно заняться всякими мелочами, до которых обычно не доходят руки. Такой мелочью для меня стала генерация файлов Sitemap.xml.
Sitemap.xml — это файл, содержащий в специальном формате ссылки на страницы сайта, которые должны быть проиндексированы поисковыми системами. Исчерпывающая информация о формате может быть найдена на Sitemaps.org.
Давно хотелось иметь удобный инструмент для формирования данных файлов.

Генерация Sitemap.xml

Поиски в сети дали множество «удобных online-сервисов» для ручного создания карты сайта и несколько простеньких скриптов, которые также непригодны для создания карты сайта с большим количеством ссылок.

Что хотим?..


Чтобы сгенерировать sitemap.xml для небольшого сайта не нужно много усилий. Для больших же ресурсов есть особенности.
Существуют ограничения на размер файлов sitemap.xml в 10Мб, а также ограничения на в 50000 ссылок на один файл. Автоматическая обработка данных ограничений и стала моей целью.
Таким образом, были сформированы следующие требования:
  1. Скрипт должен следить за размером получаемых файлов и количеством добавленных url. При необходимости, создавать несколько файлов в соответствии с форматом;
  2. Не хранить промежуточные данные в памяти;
  3. Создавать по необходимости сжатые версии файлов, для отдачи с помощью nginx;
  4. Автоматически выполнять простейшие проверки данных.

Сказано — сделано. Конечный вариант скриптов можно найти по ссылке в конце статьи.

Чего не делает скрипт?


Чтобы предупредить дальнейшие вопросы скажу, что скрипт не является универсальным решением, которое в один чих сгенерирует карту для произвольного сайта.
Это лишь инструмент, и список ссылок, которые будут добавляться в файл, необходимо формировать самостоятельно, возможно, в несколько заходов.
Кроме того, скрипт не исправляет и не кодирует url, переданные ему. Поэтому позаботиться о соответствии ссылок стандарту RFC-3986 для URI, стандарту RFC-3987 для IRI и XML-стандарту.

Пример


С помощью данного инструмента карту сайта можно создавать примерно так:
Пример скрипта, генерирующего карту сайта
<?php
require_once(dirname(__FILE__)."/../common.inc.php");
set_time_limit(0);
ini_set('memory_limit', '128M');

$dir = dirname(__FILE__);//document root path
$tmp_dir = dirname(__FILE__);//temp path
$base_url = 'http://mysite.ru/';//url with sitemaps (http://mysite.ru/sitemap.xml)
$gzip = true;
$config = array('path' => $dir , 'tmp_dir'=>$tmp_dir,'base_url'=>$base_url,'gzip'=>$gzip, 'gzip_level'=>9);

$builder = new SitemapBuilder($config);

$time = time();
    
$builder->start();
$builder->addUrl($base_url,$time,1.0);
$builder->addUrl($base_url."news",$time,1.0);

/*
//this is example adding url 
$documents = News::find(array('criteria'=>'is_published=1'));
foreach($documents as $document)
  $builder->addUrl($document->getUrl(),$document->getUtime(),0.8);
*/    
$builder->commit();



Ссылки


  1. Sitemaps.org
  2. Исходники скриптов генерации Sitemap.xml
  3. Репозиторий на github.com

Комментарии (29)


  1. Borro
    06.01.2016 13:53
    +3

    Я просто оставлю это здесь:


    1. vasiatka
      06.01.2016 15:39
      -4

      Ни чего не имею против всякого рода новомодных приблуд… Но это не значит, что все должны ими пользоваться.
      Особенно не люблю, когда мне пытаются навязывать, что и куда положить, как программировать и т.п. Предпочитаю знать, что включаю в проект, и как оно рабатает.


      1. NorthDakota
        06.01.2016 15:55

        Ни чего не имею против всякого рода новомодных приблуд

        Это не новомодные приблуды, а вещи которые должен хотя бы знать уважающий себя php-программист


        1. vasiatka
          06.01.2016 21:26
          -1

          Вы считаете, что компосер должны использовать все? Это именно та вещь, которую просто необходимо использовать каждому php-программисту?


          1. SamDark
            06.01.2016 21:56
            -1

            Всё идёт к тому…


          1. Borro
            06.01.2016 22:54
            -1

            Я считаю, что нужно использовать в каждом php-проекте, где нужна хотя бы одна зависимость или это библиотека. Он решает проблему версионирования, подключения классов и даже поиска нужного пакета


      1. Borro
        06.01.2016 16:17
        +4

        Я тоже предпочитаю знать, что я включаю в проект и как оно работает. Поэтому я буду подключать всё через composer, и пользоваться общепринятыми стандартами или библиотеками, которые этот стандарт поддерживают.

        • PSR-2 помогает сделать код более читабельным, а так же другим людям позволяет сразу же понят, как правильно писать (табы, пробелы, фигурные скобки, прочее)
        • PSR-4 помогает сделать структуру, в которой не нужно использовать в каждом файле include.
        • Composer помогает подключать библиотеки стандартно с помощью одной команды, и больше не думать как подключать каждую из библиотек
        • PHPUnit — признанный инструмент для написания unit-тестов. Я видел, что у вас тоже есть тесты, но они не стандартизированы, а значит ни одна система сборки их не сможет прогнать без доработки напильником.

        Единственное от чего можно отказаться это от GrumPHP, но он мне очень нравится, т.к. проверяет код перед комитом (на стандарты, запускает тесты и прочее)


        1. SamDark
          06.01.2016 16:25
          +1

          Да ладно, travis вполне сможет прогнать эти тесты.


          1. Borro
            06.01.2016 17:33

            Да, с тестами действительно неправ, т.к. это на самопис, а github.com/limb-php-framework/limb/tree/master/tests_runner


            1. SamDark
              06.01.2016 17:57

              Да всё равно, самопис или нет. Ни один CI не завязан ни на одну конкретную систему тестирования.


              1. Borro
                06.01.2016 20:36

                Конечно. Но для многих есть плагины под популярные системы. А если это самопис — то придется самому писать и плагин для CI


              1. vasiatka
                06.01.2016 20:51
                -2

                да вы правы Limb. Скажу больше test_runner это обвертка для PHPUnit.


  1. SamDark
    06.01.2016 16:24
    +2

    Я тоже писал библиотеку для генерации SiteMap: github.com/samdark/sitemap. Её отличительная особенность в том, что она не кушает память и отлично подходит для создания карт с сотнями тысяч URL. У вас же библиотека для этого не годится. Прожорливая.


    1. vasiatka
      06.01.2016 20:20
      -1

      Вы видимо мой код посмотрели… Подскажите, где кушает память. Вы то вот написали код, который сгенерирует файлы sitemap.xml, которые не будут приняты поисковыми системами (часто сайтмапы не добираются до ограничения в 50000 ссылок, 10Мб достигается раньше).


      1. ustasby
        06.01.2016 21:23

        Я точно посмотрел, скажу прямо, это полный отстой.


        1. vasiatka
          06.01.2016 21:43
          -1

          Если я говорю, что у вас ошибка, то указываю в чем. А так и я могу вашу реализацию какашкой обозвать.


          1. ustasby
            07.01.2016 00:15
            -1

            У меня тоже хватает отстойного кода, но он мой, для меня. А указания есть в 1 посте, просто там не написано говнокод.


      1. SamDark
        06.01.2016 22:11

        Больше всего при сжатии. Ну и немного на буферизацию строки, которую будем писать.

        Проверку лимита в 10 мегабайт добавлю. Упустил это дело.


  1. nazarpc
    06.01.2016 16:55
    -2

    Вот у меня вопрос. А что такого вы можете своим скриптом сгенерировать чего поисковик (который ещё и JS исполняет) не найдет сам?
    Если ничего — то какой в этом вообще смысл?


    1. evilbot
      06.01.2016 16:58

      Рассказать поисковику о всех страницах сайта, а не только о тех, которые он знает.


      1. nazarpc
        06.01.2016 17:14
        -1

        О каких страницах он может не знать? У него 200% более продвинутый парсер, который (как я уже говорил) поддерживает даже исполнение JS и находит ссылки в динамическом содержимом.



        1. aktuba
          06.01.2016 20:11

          А автоматически определить, когда еще заходить на конкретные страницы сайта поисковик сможет? А без дополнительной нагрузки на сам сайт?


          1. nazarpc
            06.01.2016 20:15

            Вы, наверное, не поверите — но да. Он определяет частоту изменения содержимого страницы и подстраивает обход сайта. Именно поэтому изменение на малопосещаемом сайте может быть незамеченным несколько дней, а статья из популярного сайта новостей появляется в выдаче мгновенно.


            1. aktuba
              06.01.2016 21:15

              Ага… Вот только чтобы подобрать «частоту изменения», ему приходится заходить на страницу довольно часто в начале… Именно про это я говорил, когда упомянул «дополнительную нагрузку на сайт». А если потом контент на странице начнет меняться часто — робот это либо будет игнорировать (говорю по опыту), либо долго подстраиваться (в теории), что снова создаст дополнительную нагрузку.

              Далее: может ли робот проиндексировать страницу, на которую нет ни одной ссылки. Ни статической, ни динамической. Поисковики, конечно, умные, но не настолько…

              Представьте, что у вас часть сайта на отдельном поддомене. Для поискового робота — это совсем отдельный проект. Указав ему страницы через sitemap.xml — вы указываете связь между основным доменом и поддоменом. Таких сценариев достаточно много, на самом деле. Вот только вам они не интересны и не нужны, иначе вы бы про них знали).

              Ну и напоследок: если сами поисковики просят создавать sitemap — считаете, они глупые?


              1. nazarpc
                06.01.2016 21:35

                Вот только чтобы подобрать «частоту изменения»

                Ну погодите, разговор начался из статьи, вы видите в ней поддержку данной функциональности? Я же не говорю что у вас может быть сайт новостей, где вы на основе данных из БД о создании и изменении записей генерируете sitemap.xml. Речь о генераторе описанном в статье, который прикидается очень простым поисковиком.
                Далее: может ли робот проиндексировать страницу, на которую нет ни одной ссылки. Ни статической, ни динамической. Поисковики, конечно, умные, но не настолько…

                Зачем поисковику (пользователю поисковика) ссылка на страницу, на которую пользователь сам никак попасть не может?
                Представьте, что у вас часть сайта на отдельном поддомене.

                Это дико странный сайт и сомневаюсь что sitemap.xml вам с этим поможет. Зеркала указываются через robots.txt, а вот часть сайта на отдельном домене — это другой сайт, всё верно. И поисковики не рекомендуют, если мне не изменяет память, к примеру, языковые версии, разносить по разным доменам по той же причине.
                Ну и напоследок: если сами поисковики просят создавать sitemap — считаете, они глупые?

                Нет, это было вполне резонно когда поисковики были маленькие и им было сложно переваривать большие сайты, тогда зная свой сайт вы могли (в теории) пропарсить его более качественно и сгенерировать sitemap.xml. Сейчас такой проблемы нет, а вы очень маловероятно напишете лучший парсер.

                Хорошо структурированные данные, как то новости, вполне имеет смысл засовывать в sitemap.xml ввиду простоты и качества (точное время создания/изменения) реализации. Вручную проходить по сайту самописным парсером и засовывать это в sitemap.xml смысла не вижу.


                1. aktuba
                  06.01.2016 22:29

                  >Ну погодите, разговор начался из статьи, вы видите в ней поддержку данной функциональности?

                  Вообще — да: https://github.com/vasiatka/sitemap/blob/master/sitemap/src/SitemapBuilder.class.php#L56. Это стандарт.

                  >Зачем поисковику (пользователю поисковика) ссылка на страницу, на которую пользователь сам никак попасть не может?

                  С чего вы решили, что на страницу нельзя попасть?))) Я говорил только про отсутствие ссылок.

                  >Это дико странный сайт и сомневаюсь что sitemap.xml вам с этим поможет.

                  Ой-ли… С чего вдруг он стал странным? С того, что тематическую часть вынесли в отдельный поддомен? И да, sitemap с этим поможет.

                  >Зеркала указываются через robots.txt, а вот часть сайта на отдельном домене — это другой сайт, всё верно.

                  Т.е., m.habrahabr.ru — это отдельный сайт? Ну а news.yandex.ru конечно-же не имеет никакого отношения к yandex.ru). Более простой пример — news.nnm.me — на поддоменах отдельные разделы, но никак не отдельные сайты.

                  >И поисковики не рекомендуют, если мне не изменяет память, к примеру, языковые версии, разносить по разным доменам по той же причине.

                  Не помню таких рекомендаций, если честно, но верю на слово. Тут другие примеры — тематические разделы в отдельных поддоменах.

                  >Сейчас такой проблемы нет, а вы очень маловероятно напишете лучший парсер.

                  И сейчас такие проблемы есть — я их написал, но вы упорно пытаетесь их игнорировать). Второй момент — автору сайта не надо писать парсер, он может очень точно указать ссылки на страницы для индексации. При чем тут вообще парсер?)))

                  >Вручную проходить по сайту самописным парсером и засовывать это в sitemap.xml смысла не вижу.

                  Вы уверены, что комментируете тот пост, который хотите? Как-бы сам пост, пример в посте и ссылка на гитхаб говорят только о генерации sitemap для своего сайта, ни о каком ручном проходе стороннего сайта речи нет)


                  1. nazarpc
                    06.01.2016 23:11

                    Вообще — да: github.com/vasiatka/sitemap/blob/master/sitemap/src/SitemapBuilder.class.php#L56. Это стандарт.

                    Это я видел, и это полумера. Такую цифру спокойно выведет поисковик, а фиксированную статью повторно индексировать смысла нет, то есть параметр очень неточный. Да, на это у поисковика уйдет время на вывод параметра, но тем не менее. Дату модификации скрипт не может проставить, поскольку у него нет нужной информации. То есть как по мне именно практического смысла здесь большого нет. Вы пытаетесь сделать то, что поисковик уже успешно делает.
                    С чего вы решили, что на страницу нельзя попасть?))) Я говорил только про отсутствие ссылок.

                    С того что ссылка — это основной способ перехода между страницами сайта. Если это не ссылка — весьма небольшая вероятность что туда нужно соваться поисковику. Я понимаю, что можно сгенерировать ссылку с помощью JS и бросить туда пользователя, но это в подавляющем большинстве не то, куда поисковик должен заглядывать.
                    Ой-ли… С чего вдруг он стал странным? С того, что тематическую часть вынесли в отдельный поддомен? И да, sitemap с этим поможет.

                    С того что вы почему-то вынесли его не на domain.tld/news, а на news.domain.tld. Я понимаю, что можно сначала создать себе проблему, а потом пытаться её решить, но это не слишком продуктивное времяпровождение. Вы поищите что люди пишут по поводу поддоменов — поддомен это отдельный сайт для отдельного региона, отдельного продукта компании и так далее.
                    Т.е., m.habrahabr.ru — это отдельный сайт?

                    Загляните на habrahabr.ru/sitemap.xml и потом на m.habrahabr.ru/sitemap.xml
                    Ну а news.yandex.ru конечно-же не имеет никакого отношения к yandex.ru).

                    Кроме того что владелец сайта одна компания — никакого. На одном поиск, а на другом новости. Их обслуживают разные сервера, над ними работают разные команды и даже дизайн отличается, хоть и в стиле компании. То есть сама компания — почти единственное что объединяет сайты, потому и поддомен.
                    Более простой пример — news.nnm.me — на поддоменах отдельные разделы, но никак не отдельные сайты.

                    Это как раз более сложный вариант, тот, который я называл выше странным. Это по сути отображение новостей из определённого раздела, это и правда часть основного сайта, и если бы разработчики вынесли его в nnm.me/news — то подняли бы основной сайт в выдаче.
                    Расширение для поддержки поддоменов чисто гугловская штука судя по тому что пишут люди: https://productforums.google.com/forum/#!topic/webmasters/3cB5qpGv-zk
                    Цитата из спецификации:
                    Note: A Sitemap index file can only specify Sitemaps that are found on the same site as the Sitemap index file. For example, www.yoursite.com/sitemap_index.xml can include Sitemaps on www.yoursite.com but not on www.example.com or yourhost.yoursite.com. As with Sitemaps, your Sitemap index file must be UTF-8 encoded.

                    И сейчас такие проблемы есть — я их написал, но вы упорно пытаетесь их игнорировать). Второй момент — автору сайта не надо писать парсер, он может очень точно указать ссылки на страницы для индексации. При чем тут вообще парсер?)))

                    Парсер при том что статья о парсере. Если у вас sitemap.xml генерируется на основании точных данных из БД — вопросов нет.


                    1. aktuba
                      07.01.2016 00:10

                      >Это я видел, и это полумера. Такую цифру спокойно выведет поисковик, а фиксированную статью повторно индексировать смысла нет, то есть параметр очень неточный.

                      Да куда уж точнее-то? Мы же сами указываем, раз в какой период заходить на страницу…

                      >Дату модификации скрипт не может проставить, поскольку у него нет нужной информации.

                      Ах, вот оно что… Есть. Подробнее — в самом конце моего комментария.

                      >С того что вы почему-то вынесли его не на domain.tld/news, а на news.domain.tld.

                      А почему я должен делать тематику частью урла, а не домена? По мне, news.domain.tld/moscow куда приятнее и понятнее, чем domait.tld/news/moscow.

                      >Вы поищите что люди пишут по поводу поддоменов — поддомен это отдельный сайт для отдельного региона, отдельного продукта компании и так далее.

                      Люди? Мне проще прочесть спецификацию и использовать ее. Но, пусть будет по вашему. Все-равно не вижу причин не использовать поддомены в функционале сайта.

                      >и если бы разработчики вынесли его в nnm.me/news — то подняли бы основной сайт в выдаче.

                      Откуда такая информация? Мне правда интересно. Тем более, что сами посты не находятся в поддоменах, в них только списки постов.

                      >Цитата из спецификации

                      говорит об индексном файле, т.е. ссылки на части sitemap должны располагаться в пределах одного домена. В спецификации нет ни слова о том, что страницы должны располагаться в пределах домена.

                      >Парсер при том что статья о парсере.

                      Я уж было усомнился в своей читательской способности, но нет… Перечитал пост, поискал через Cmd+F — нет в посте ни слова про парсер. Откуда вы это взяли?

                      Я понял, что мы говорим абсолютно о разных скриптах. Тот, что в посте, генерирует на основе данных, переданных ему. В нем нет паука, нет парсера и пр. (сужу по примеру, а не коду). И пример говорит мне о том, что sitemap генерируется по данным на основе БД:

                      $documents = News::find(array('criteria'=>'is_published=1'));
                      foreach($documents as $document)
                      $builder->addUrl($document->getUrl(),$document->getUtime(),0.8);