Генерация Sitemap.XML с помощью DOMDocument

VitaliyRodnenko, 06.08.2008

Итак, как я и обещал, выкладываю статью по созданию карты сайта Sitemap.XML для поисковых систем с использованием DOM в PHP, а именно DOMDocument.

Класс DOMDocument в PHP позволяет обращаться к XML файлам и данным в XML формате, а так же создавать их. Он имеет ряд методов, которые будут нам полезны при создании Sitemap.XML.

Данный метод полностью основан на ООП, что позволяет легко улучшить или изменить для себя исходный код.

Данная статья посвящена именно генерации Sitemap.XML с помощью DOMDocument. Описание, что это такое вы можете найти на официальном сайте.

Наша группа занималась разработкой портала «VLG Navigator», представляющего собой Каталог организаций Волгограда с позиционированием на карте города.

Передо мной стояла задача — организовать возможность создавать Sitemap.XML из административного интерфейса. Все объекты в каталоге размещались администратором и, допустим, после добавления порядка 30 объектов в каталог необходимо было как можно быстрее оповестить о них поисковые системы.

В моем случае, каждый узел с URL в карте сайта был следующего типа:

<url>
  <loc>http://www.vlg-nav.ru/</loc>
  <lastmod>2008-07-24</lastmod>
  <changefreq>daily</changefreq>
  <priority>1.0</priority>
</url>

Каждый из узлов URL я решил хранить в соответствующем объекте. Для этого был написан класс SitemapUrl:

class SitemapUrl{
	
  private $loc;
  private $lastmod;
  private $changefreq;
  private $priority;

  final function __construct($loc,$lastmod,$changefreq,$priority){
	
    $this->loc = $loc;
    unset ($loc);
	
    $this->lastmod = $lastmod;	
    unset ($lastmod);
		
    $this->changefreq = $changefreq;
    unset ($changefreq);
	
    $this->priority = $priority;
    unset ($priority);
	
  }
	
  final function getLoc(){
    return $this->loc;
  }

  final function getLastmod(){
    return $this->lastmod;
  }

  final function getChangefreq(){
    return $this->changefreq;
  }

  final function getPriority(){
    return $this->priority;
  }
}

Таким образом, в переменных $this->loc, $this->lastmod, $this->changefreq, $this->priority хранятся соответствующие значения элементов конкретного узла карты сайта.

Допустим в карту сайта Sitemap.XML нам необходимо сохранить URL http://www.webpp.ru, с датой 2008-07-08, приоритетностью 1.0 и ежедневной частотой обновления. Создадим объект класса SitemapUrl:

<?php
$webpp = new SitemapUrl ('http://www.webpp.ru','2008-07-08','daily','1.0');
// Так мы создали объект $webpp класса SitemapUrl с необходимыми нам аргументами.
// В $webpp->loc хранится значения для  <loc>.
// В $webpp->lastmod хранится значения для  <lastmod>.
// В $webpp->changefreq хранится значения для  <changefreq>.
// В $webpp->priority хранится значения для  <priority>.
?>

Почемы я выбрал храненение значений в объектах, а не ,скажем, в массиве? Поясню на практике.

Допустим источником данных для построения карты сайта является некая база данных, в которой значение даты, необходимое нам в построении карты сайта хранится в собственном формате (формате базы данных). Нам нужно организовать преобразование, из формата базы данных в необходимый нам.

Хорошо, если генерация объектов происходит в одном месте, в этом случае мы можем на уровне выборки данных из БД преобразовать их в нужный нам формат каким-либо алгоритмом. А если точек генерации объектов десятки или сотни? Тогда нам нужно всех их пройти и внести в код заполнения наш алгоритм?

Поступим проще! Воспользуемся конструктором нашего класса function __construct.

Конструктор класса вызывается при объявлении объекта этого класса. В него помещают код, который нужно выполнить сразу же, как только объект был создан. В версиях PHP ниже 5й, чтобы объявить конструктор класса, функцию класса должна иметь одинаковое с классом название. В Версиях PHP 5 и выше конструктор объявляется функцией __construct(). Так же существует и деструктор класса — __destruct(), который выполняется перед уничтожением объекта класса.

Поместим в конструктор нашего класса алгоритм преобразования над датой:

  final function __construct($loc,$lastmod,$changefreq,$priority){

    $this->loc = $loc;
    unset ($loc);

    // Допустим функция algoritm($lastmod) производит преобразование
    // даты из формата БД в нужный нам формат.
    $this->lastmod = algoritm($lastmod);	
    unset ($lastmod);
    // Теперь значение даты будет храниться в таком виде, в каком нам нужно.

    $this->changefreq = $changefreq;
    unset ($changefreq);

    $this->priority = $priority;
    unset ($priority);
		
  }

Добавив один раз в конструкторе класса SitemapUrl преобразование даты, мы автоматически организовали преобразование во всех точках создания объектов этого класса.

Теперь собственно нужно получить списки URL и параметры для них, и организовать для них хранение в объектах класса SitemapUrl.

Напишем еще один класс SiteMapUrlGenerator ,содержащий в себе логику, по которой будут извлекаться списки URL, и все параметры для них, и сохраняться в массив объектов класса SitemapUrl.

Если Вы захотите переписать мой скрипт под свои нужды, то редактировать Вам нужно будет только этот класс. В моем случае он содержит набор статических страниц, которые так же должны присутствовать в карте сайта, но по тем или иным причинам отсутствуют в базе данных. Т.е. если мнеизвестны страницы, которых нет в БД, но они мне также нужны, я просто укажу их руками.

Так как в нашем проекте нужно было создать карты сайта только для каталога организаций и для объявлений, то я заточил этот класс именно под эти нужды. Повторюсь, для своих проектов Вам нужно будет переписать данный класс.

class SiteMapUrlGenerator {

  //Приоритетность URL каждого объявления
  private $ad_priority = '0.8';
  
  //Периодичность посещения поисковой системой URL объявления
  private $ad_changefreq = 'weekly';

  //Приоритетность URL каждого объекта каталога
  private $cat_priority = '0.9';

  //Периодичность посещения поисковой системой URL объектов каталога
  private $cat_changefreq = 'weekly';


 /*
  Я храню эти значения в открытом виде, так сказать "по умолчанию", так как в нашем проекте не предусмотрена логика вычислений данных значений.
  В вашем случая ничего не мешает вместо статических значений подставить результат выполнения какого-либо собственного алгоритма.
*/

  var $urlList = array(); //Массив объектов, в котором будут
  //храниться все экземпляры класса SitemapURL
  
  var $db_conn; // Сюда будет создаваться объект написанного нами класса
  // для работы с БД. В вашем случае он может быть не обязательным.

  final function __construct() {

    $this->db_conn = $GLOBALS['db_conn']; //Объявляем класс для работы с БД

    // Ниже вставляем в массив $this->urlList все нужные нам на карте сайта
    // статические URL.

    $this->urlList[] = new SitemapUrl ('http://'.$_SERVER['HTTP_HOST'].'/',
                                      '2008-07-24',
                                      'daily',
                                      '1.0');

    $this->urlList[] = new SitemapUrl ('http://'.$_SERVER['HTTP_HOST'].'/ad/',
                                      '2008-07-20',
                                      'daily',
                                      '0.7');

    $this->urlList[] = new SitemapUrl ('http://'.$_SERVER['HTTP_HOST'].'/cat/',
                                      '2008-07-20',
                                      'daily',
                                      '0.7');

    $this->urlList[] = new SitemapUrl ('http://'.$_SERVER['HTTP_HOST'].
                                      '/contact/',
                                      '2008-07-20',
                                      'weekly',
                                      '0.5');

    $this->urlList[] = new SitemapUrl ('http://'.$_SERVER['HTTP_HOST'].
                                      '/advertising/',
                                      '2008-07-20',
                                      'weekly',
                                      '0.5');

    $this->urlList[] = new SitemapUrl ('http://'.$_SERVER['HTTP_HOST'].'/map/',
                                      '2008-07-20',
                                      'never',
                                      '0.6');
  }

  final function getUrls() {

    $this->getCatUrlList();
    $this->getAdUrlList();

    return $this->urlList;
          
  }

  final function getAdUrlList() { 
    // Данння функция получает из БД все ссылки объектов каталога объявлений
    // не буду вдаваться в ее описание, так как оно не имеет значения.
    // Главное получить нужные нам URL, а каким способом неважно.

    //Объявляем шаблон по которому будет строиться строиться URL объявлений.
    $shablon = 'http://'.$_SERVER['HTTP_HOST'].'/ad/';

    $ads = $this->db_conn->SelAll('ad_records','`id`,`post_date`');

    if($ads) {
      foreach($ads as $ad) {
        // Выполняем заполнение массива объектов класса SitemapUrl
        // его экземплярами, сгенерированными для каталога объявлений.

        $this->urlList[] = new SitemapUrl ($shablon.$ad['id'].'/',
        $ad['post_date'],
        $this->ad_changefreq,
        $this->ad_priority);
      }
    }
     
  }

  final function getCatUrlList() {
   // Данная функция получает из БД все ссылки объектов каталога организаций
   // не буду вдаваться в ее описание, так как оно не имеет значения.
   // Главное получить нужные нам URL, а каким способом неважно.

  //Объявляем шаблон по которому будет строиться URL каталога организаций.
    $shablon = 'http://'.$_SERVER['HTTP_HOST'].'/cat/'; 

    $query = "SELECT `icons_info`.`icon_id`,`icons_info`.`edit_date`";
    $query.= " FROM `icons_info`";

    $cats = $this->db_conn->SelQuery($query);

    if($cats) {
      foreach($cats as $record) {
        // Выполняем заполнение массива объектов класса SitemapUrl
        // его экземплярами, сгенерированными для каталога организаций.

        $this->urlList[] = new SitemapUrl ($shablon.$record['icon_id'].'/',
        $record['edit_date'],
        $this->cat_changefreq,
        $this->cat_priority);
      }
    }

  }

}

Абсолютно все URL хранятся в объектах класса SitemapUrl и хранятся в одном массиве $this->urlList .

Думаю понять как работает данный класс и написать свой подобный труда не составит. Главной генерировать нужные Вам URL и записывать их объекты класса SitemapURL. И вернуть в виде массива объектов.

Ну и осталось написать самый главный класс, который будет все это собирать в кучу и выдавать XML файл карты сайта.

class Sitemap {

  //Нужно пинговать поисковики или нет.
  var $needPing = False;

  // Флаг, существует ли файл sitemap.xml или нет
  private $XMLsitemapExist = False;

  // Относительный путь до XML файла карты сайта.
  private $XMLsitemapSaveFile = '../sitemap.xml';

  // Флаг, существует ли файл sitemap.txt или нет
  private $TXTsitemapExist = False;

  // Относительный путь до TXT файла карты сайта.
  private $TXTsitemapSaveFile = '../sitemap.txt';

  // Сюда будет происходить сохранение списка URL для
  // текстовой версии карты сайта
  private $TXTsitemapDump;

  // Сюда будем записывать массив объектов с URL для XML версии
  private $urlList;

  // Адрес, по которому пинговать Google, что карта сайта обновилась
  private $googlePingUrl;

  // Адрес, по которому пинговать Yahoo, что карта сайта обновилась
  private $yahooPingUrl;

  // Хранится полный URL до файла sitemap.xml
  private $sitemapFile;

  // Сюда будем создавать экземпляр класса DOMDocument
  private $dom;

  final function generate() {
    /*
    Главный метод класса Sitemap. Собвственно он и
    выполняет генерацию карты сайта с помощью DOMDocument.
    */

    // Так как объект класса DOMDocument у нас уже объявлен, а с ним и корневой
    // элемент <xml>, то создаем дочерний элемент <urlset>
    $root = $this->dom->appendChild($this->dom->createElement('urlset'));
          
    // Добавляем необходимые атрибуты.
    $root->setAttribute('xmlns','http://www.sitemaps.org/schemas/sitemap/0.9');
    $root->setAttribute('xmlns:xsi','http://www.w3.org/2001/XMLSchema-instance');
    $root->setAttribute('xsi:schemaLocation',
              'http://www.sitemaps.org/schemas/sitemap/0.9'.' '.
              'http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd');

    // Запускаем генерацию массива объектов типа SitemapUrl
    $SiteMapUrlGenerator = new SiteMapUrlGenerator;
    $this->urlList = $SiteMapUrlGenerator->getUrls();

    // Очищаем память, так как массив был успешно создан
    // и более объект не требуется.
    unset($SiteMapUrlGenerator);

    // Перебираем значения полученного массива и формируем XML
    for($i=0;$i<sizeof($this->urlList);$i++) {

     // Создаем узел <url>
      $url = $root->appendChild($this->dom->createElement('url'));

      // Создаем дочерний узел <loc> и заполняем его информацией.
      $url->appendChild($this->dom->createElement('loc',
                                    $this->urlList[$i]->getLoc()));

      // Создаем дочерний узел <lastmod> и заполняем его информацией.
      $url->appendChild($this->dom->createElement('lastmod',
                                    $this->urlList[$i]->getLastmod()));

      // Создаем дочерний узел <changefreq> и заполняем его информацией.
      $url->appendChild($this->dom->createElement('changefreq',
                                    $this->urlList[$i]->getChangefreq()));

      // Создаем дочерний узел <priority> и заполняем его информацией.
      $url->appendChild($this->dom->createElement('priority',
                                    $this->urlList[$i]->getPriority()));

      // Так же создаем список URL для текстовой версии карты сайта.
      $this->TXTsitemapDump .= $this->urlList[$i]->getLoc()."\n";
 
    }

    // Если файл sitemap.xml существует, то удаляем его.
    if($this->XMLsitemapExist) {
      unlink($this->XMLsitemapSaveFile);
    }

    // Сохраняем сгенерированные XML данные в файл sitemap.xml
    $this->dom->save($this->XMLsitemapSaveFile);

    // Сохраняем сгенерированные TXT данные в файл sitemap.txt
    // Если файл sitemap.txt существует, он будет удален и создан новый.
    fwrite(fopen($this->TXTsitemapSaveFile,"w-", $this->TXTsitemapDump);

    // Если требуется пинговка поисковиков, то выполняем.
    if($this->needPing) {
      $this->pingGoogle();
      $this->pingYahoo();
    }
  }

  final function pingGoogle() {

    //Метод, выполняющий пинг Google, о том, что карта сайта обновилась
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $this->googlePingUrl);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $result = curl_exec($ch);
  }

  final function pingYahoo() {

    //Метод, выполняющий пинг Yahoo, о том, что карта сайта обновилась
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $this->yahooPingUrl);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $result = curl_exec($ch);
 }

  final function __construct() {
    // Производим инициализацию объектов. Объявляем переменные окружения класса.

    // Объявляем URL до файла sitemap.xml
    $this->XMLsitemapFile = 'http://'.$_SERVER['HTTP_HOST'].'/sitemap.xml';

    // Объявляем URL по которому пинговать Google
    $this->googlePingUrl = 'http://www.google.com/webmasters/sitemaps/'.
                           'ping?sitemap=';
    $this->googlePingUrl .= urlencode($this->XMLsitemapFile);

    // Объявляем URL по которому пинговать Yahoo
    $this->yahooPingUrl = 'http://search.yahooapis.com/SiteExplorerService/V1/'.
                          'ping?sitemap=';
    $this->yahooPingUrl .= .urlencode($this->XMLsitemapFile);

    // Создаем объект класса DOMDocument
    $this->dom = new DOMDocument('1.0','UTF-8');
    $this->dom->formatOutput = true;

    // Так, на всякий случай
    $this->TXTsitemapDump = '';

    // Проверяем, существует ли уже файл sitemap.xml
    $this->XMLsitemapExist = file_exists($this->XMLsitemapFile);

    // Проверяем, существует ли уже файл sitemap.txt
    $this->TXTsitemapExist = file_exists($this->TXTsitemapSaveFile);
  }

}

Вот собственно и все, старался ясно описывать все действия в комментариях, но повторюсь еще раз.

Итак, мы объявили наш класс Sitemap. Из чего он состоит? Первоначально мы создаем внутренние переменные класса, которые будут использоваться в нем, такие как флаги, существуют ли уже файлы карты сайта или нет. Адреса пинга поисковиков Google и Yahoo. Полный путь до файлов карты сайта. URL адрес файла sitemap.xml. И прочие необходимые нам переменные.

Далее они инициализируются в конструкторе класса. Так же в конструкторе класса происходит создание объекта класса DOMDocument, через который мы будем производить все манипуляции с XML данными.

Методы pingGoogle() и pingYahoo() поизводят пинг поисковых систем, о том, что карта сайта обновилась. Эти методы работают через библиотеку curl которая должна быть подключена в PHP.

Ну и основной метод generate(). Он и управляет самой генерацией карты сайта Sitemap.XML и TXT версии. Порядок, в котором работает данный метод описан в комментариях к коду.

Теперь осталось только создать объект класса Sitemap и вызвать генерацию XML карты сайта. Вместе с ней создастся и TXT версия.

$sitemap = new Sitemap;
$sitemap->generate();

Все, готово! Таким образом мы получили нужную нам карту сайта со всеми выбранными URL.

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

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

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

Подписаться на обновления блога

Вам понравился наш блог, хотите следить за обновлениями? Подпишитесь на RSS рассылку или рассылку по электронной почте. Так же вы можете следить за нами в Twitter.

Категории: PHP | Комментировать

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

  1. сео блоггер / 06.08.2008 в 12:14

    А готового скрипта пока нет?

  2. Skaizer / 06.08.2008 в 14:16

    сео блоггер, есть, можете скачать его тут.
    В нем требуется переписать только получение данных для генератора, а именно класс «SiteMapUrlGenerator».

  3. John / 11.09.2008 в 09:38

    Что то не работает после запуска ни чего не происходит, хотя это снизу добавил:

    $sitemap = new Sitemap;
    $sitemap->generate();
  4. Skaizer / 11.09.2008 в 09:47

    John, а Вы переписали класс SiteMapUrlGenerator ?
    В вашем случае он будет другой, не такой как в моем примере.

  5. John / 11.09.2008 в 10:52

    Разобрался 8-) не увидел просто что он за каталог их выкинул … вот так переделал:

    $url = $root->appendChild($this->dom->createElement('url'));
    $url->appendChild($this->dom->createElement('loc',$this->urlList[$i]->getLoc()));
    if($this->urlList[$i]->getLastmod())$url->appendChild($this->dom->createElement('lastmod',$this->urlList[$i]->getLastmod()));
    if($this->urlList[$i]->getChangefreq())$url->appendChild($this->dom->createElement('changefreq',$this->urlList[$i]->getChangefreq()));
    if($this->urlList[$i]->getPriority())$url->appendChild($this->dom->createElement('priority',$this->urlList[$i]->getPriority()));
    $this->TXTsitemapDump .= $this->urlList[$i]->getLoc()."\n";
  6. John / 11.09.2008 в 11:05

    Мдя удалить наверно лучше мой комент а то все разъехолось 8-)

  7. Skaizer / 11.09.2008 в 12:31

    Да не )) подправим, полезные комменты никогда лишними не бывают :)
    Хм, а для чего Вы добавили проверки:

    if($this->urlList[$i]->getLastmod())
    if($this->urlList[$i]->getChangefreq())
    if($this->urlList[$i]->getPriority())

    Насколько мне известно данные узлы в Sitemap.XML являются обязательными, их отсутствие может вызвать ошибку у поисковика (предупреждение в разделе веб-мастеров).

  8. John / 11.09.2008 в 12:43

    http://www.sitemaps.org/ru/protocol.php Да нет тут вроде написано что не обязательны они ;-)

  9. Skaizer / 11.09.2008 в 12:49

    ^______^
    точно )) проглядел наверное :-)

  10. Юрий / 18.04.2009 в 12:22

    Память лишняя занимается скриптом, постоянные переменные лучше добаить перед самой генерацией xml (переменные $_SERVER). Если сайт из 1000 страниц, экономия существенная.

  11. Skaizer / 18.04.2009 в 23:14

    Ну я не отрицаю, что скрипт возможно прожорлив. Думаю каждый для своих целей сможет его оптимизировать, как надо :) Данная реализация без проблем справляется с генерацией карты из 2000 страниц, думаю с большим объемом тоже легко будет работать.

  12. Роман / 04.06.2009 в 15:30

    Скажите, должен ли создавать скрипт хотябы файлы, если его положить так как он есть без изменения ?

  13. Skaizer / 06.06.2009 в 20:59

    Скрипт создает два файла: sitemap.xml и sitemap.txt. Путь их размещения определен в классе Sitemap:

      // Относительный путь до XML файла карты сайта.
      private $XMLsitemapSaveFile = '../sitemap.xml';
    
      // Флаг, существует ли файл sitemap.txt или нет
      private $TXTsitemapExist = False;
    
      // Относительный путь до TXT файла карты сайта.
      private $TXTsitemapSaveFile = '../sitemap.txt';

    Так же вам необходимо определить логику выборки URL для вашего проекта, делается это в классе SiteMapUrlGenerator.

    Остальное можно ничего не менять.

  14. privateperson / 05.12.2009 в 16:29

    А к сайту на DLE, возможно ли прикрутить этот скрипт?

  15. Виталий / 22.10.2010 в 13:43

    Можно привязать абсолютно к чему угодно. Главной определить правила выборки в SiteMapUrlGenerator и все, вставляйте в любой проект.

  16. Вадим / 08.04.2011 в 12:52

    Отличный скрипт, спасибо!
    Не придется писать или разбираться самому, все расписано супер!
    p.s. что самое главное грамотно составлен.

  17. Владимир / 11.04.2011 в 12:46

    Просто супер,выше всех похвал)))))програмировать на php, mysql только начинаю! я разобрался в этом скрипте,всё получилось, хочу написать подобное!!!неподскажеш можно ли сделать так что бы при обращении на sitemap.php выводилось все то что написано в sitemap.xml. а то когда обращаешся к php-белый экран!

  18. Владимир / 11.04.2011 в 12:53

    и еще сикундачку!неподскажеш как сделать скрытую переадресацию с sitemap.xml на sitemap.php

  19. Владимир / 14.04.2011 в 22:15

    не подскажеш можно ли переписать этот класс class SiteMapUrlGenerator { так чт бы он брал данные из БД таблице Sitemap в которой находяться все пути, приоритеты, дата и т.п.

  20. Виталий Маташ / 16.04.2011 в 15:33

    немножко разширил количество поисковиков

    
    final function generate(){
    		.....
    		if($this->needPing){
    			$this->ping($this->yandexPingUrl);
    			$this->ping($this->googlePingUrl);
    			$this->ping($this->yahooPingUrl);
    			$this->ping($this->askPingUrl);
    			$this->ping($this->bingPingUrl);
    			
    		}
    
    	}
    	
    	
    	final function ping($url){
    		$ch = curl_init();
    		curl_setopt($ch, CURLOPT_URL, $url);
    		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    		$result = curl_exec($ch);
    	}
    	
    	
    	final function __construct(){
    		$this->XMLsitemapFile = 'http://'.$_SERVER['HTTP_HOST'].'/sitemap.xml';
    		$this->googlePingUrl = 'http://www.google.com/webmasters/sitemaps/ping?sitemap='.urlencode($this->XMLsitemapFile);
    		$this->yahooPingUrl = 'http://search.yahooapis.com/SiteExplorerService/V1/ping?sitemap='.urlencode($this->XMLsitemapFile);
    		$this->yandexPingUrl = 'http://webmaster.yandex.ru/wmconsole/sitemap_list.xml?host='.urlencode($this->XMLsitemapFile);
    		$this->askPingUrl = 'http://submissions.ask.com/ping?sitemap='.urlencode($this->XMLsitemapFile);	
    		$this->bingPingUrl = 'http://www.bing.com/webmaster/ping.aspx?siteMap='.urlencode($this->XMLsitemapFile);		
    		
    .....
    	}
    }
    
    

Оставить комментарий

480×60
480×60