Помогаем ботам найти все страница сайта
Проблема: есть достаточно большой сайт, у которого есть карта в формате XML. Есть бот, который этот сайт индексирует. Но бот не может найти страницы, имеющие уровень вложенности больше трёх. Нужно помочь боту проиндексировать весь сайт.
Самый простой способ — создание страницы, в которой будут перечислены все страницы сайта и размещение на неё ссылки из подвала сайта. Идеальный кандидат на такую страницу — карта сайта в формате XML. Проблема в том, что не все боты утруждают себя разбором XML-карт. Для таких ботов карту нужно преобразовывать в формат HTML.
Как и в другом похожем случае, на помощь приходит преобразование XSL.
Пример шаблона стилей XML:
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:x="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns="http://www.w3.org/1999/xhtml" exclude-result-prefixes="x"> <xsl:output method="xml" indent="yes" encoding="UTF-8" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/> <xsl:strip-space elements="*"/> <xsl:template match="/x:urlset"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>Site Map</title> </head> <body> <h1><a href="http://blog.sjinks.pro/">Карта сайта</a></h1> <ul> <xsl:apply-templates select="x:url"> <xsl:sort select="lastmod" order="descending"/> </xsl:apply-templates> </ul> </body> </html> </xsl:template> <xsl:template match="/x:urlset/x:url"> <li><strong><a href="{x:loc}"><xsl:value-of select="x:loc" disable-output-escaping="yes"/></a></strong><xsl:if test="x:lastmod"> (<xsl:value-of select="x:lastmod" disable-output-escaping="yes"/>)</xsl:if></li> </xsl:template> </xsl:stylesheet>
Преобразование выполняется так:
xsltproc -o /path/to/sitemap.html /path/to/sitemap2html.xml /path/to/sitemap.xml
Можно выполнение xsltproc
повесить на крон и наслаждаться результатом.
Как сделать не более N ссылок на файл
Продолжение статьи «Генерация карты сайта в HTML из XML».
В этой части мы рассмотрим, как сделать так, чтобы на одной генерируемой странице располагалось не более определённого количества ссылок.
Нам понадобятся дополнительно:
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:x="http://www.sitemaps.org/schemas/sitemap/0.9"> <xsl:output method="text" media-type="text-plain" indent="no"/> <xsl:strip-space elements="*"/> <xsl:template match="/x:urlset/x:url"> <xsl:value-of select="x:loc" disable-output-escaping="yes"/> <xsl:text>
</xsl:text> </xsl:template> </xsl:stylesheet>
и немного фантазии
Алгоритм работы простой:
- Преобразовываем исходную карту в текстовый список адресов (один URL на строку)
- Разбиваем файл на несколько частей с заданным количеством строк
- Преобразовываем каждый из полученных файлов в XML
- Преобразовываем полученные XML-файлы в HTML
Пункты 3 и 4 можно совместить: вместо генерации XML-файла можно сразу генерировать HTML. Но на всякий случай будем генерировать и XML, и HTML.
Получим такой скрипт:
#! /bin/sh xsltproc -o urls.txt sitemap2txt.xsl sitemap.xml split -d -l 100 urls.txt map for i in map*; do awk ' BEGIN { print "<?xml version=\"1.0\"?>\ <urlset\ xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\ xsi:schemaLocation=\"http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd\"\ xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">" } { print "<url><loc>"$1"</loc></url>" } END { print "</urlset>" } ' $i > site$i.xml xsltproc -o site$i.html sitemap2html.xsl site$i.xml done
Всё просто!™
Автоматизация: перекладываем работу на Google XML Sitemaps
В данной статье будет рассмотрен вопрос модификации плагина Google XML Sitemaps с целью добавления поддержки автоматической генерации HTML-версии карты сайта; как и в предыдущих статьях, преобразование будет осуществляться средствами XSL.
В первой части статьи мы рассматривали вариант, основанный на использовании утилиты xsltproc
. Предполагалось, что генерация HTML-версии карты сайта осуществляется с использованием cron
. Создание карты по расписанию означает, что построение HTML-карты будет запаздывать на некоторое время; а если не сравнивать время модификации XML- и HTML-версий карт, получим бесполезные вызовы xsltproc
.
Те, кто используют WordPress, очень часто для автоматического построения карты сайта используют плагин Google XML Sitemaps; для них предлагается вариант с модификацией кода плагина для автоматической генерации HTML-карты.
Плагин позволяет задавать свою таблицу стилей XSLT:
Мы изменим плагин таким образом, чтобы при заданной таблице стилей XSL плагин создавал HTML-файл с результатами преобразований XSL.
В файле sitemap-core.php
ищем такой кусок кода:
if($this->GetOption("b_xml")) { if($this->_fileHandle && fclose($this->_fileHandle)) { $this->_fileHandle = null; $status->EndXml(true); $pingUrl=$this->GetXmlUrl(); } else $status->EndXml(false,"Could not close the sitemap file.");
После него добавляем такой:
if (class_exists('XSLTProcessor', true)) { $file = $this->GetXmlPath(); $styleSheet = ($this->GetDefaultStyle() && $this->GetOption('b_style_default') === true ? $this->GetDefaultStyle() : $this->GetOption('b_style')); if (!empty($styleSheet)) { $xsl = new DOMDocument(); $xsl->load($styleSheet); $xml = new DOMDocument(); $xml->load($file); $proc = new XSLTProcessor(); $proc->importStyleSheet($xsl); $proc->transformToURI($xml, 'file://' . str_replace('.xml', '.html', $file)); } }
Данный код требует PHP 5 и расширения XSLT (в Debian/Ubuntu оно предоставляется пакетом php5-xsl
). Код получает имя файла с картой сайта и адрес таблицы стилей (этот адрес должен быть задан абсолютным URL); XML и XSL скармливаются XSLT Processor’у, результат записывается в файл с тем же именем, что и карта сайта XML, но с расширением .html
.
Теперь при каждом построении карты сайта будет создаваться и HTML-версия.
Тем, кто любит патчи (патч кумулятивный, исправляет и некоторые другие недостатки плагина):
--- sitemap-core.php.orig 2010-08-16 10:49:16.000000000 +0300 +++ sitemap-core.php 2010-11-28 02:10:08.000000000 +0200 @@ -1654,9 +1654,9 @@ $this->AddElement(new GoogleSitemapGeneratorXmlEntry('<' . '?xml-stylesheet type="text/xsl" href="' . $styleSheet . '"?' . '>')); } - $this->AddElement(new GoogleSitemapGeneratorDebugEntry("generator=\"wordpress/" . get_bloginfo('version') . "\"")); - $this->AddElement(new GoogleSitemapGeneratorDebugEntry("sitemap-generator-url=\"http://www.arnebrachhold.de\" sitemap-generator-version=\"" . $this->GetVersion() . "\"")); - $this->AddElement(new GoogleSitemapGeneratorDebugEntry("generated-on=\"" . date(get_option("date_format") . " " . get_option("time_format")) . "\"")); +// $this->AddElement(new GoogleSitemapGeneratorDebugEntry("generator=\"wordpress/" . get_bloginfo('version') . "\"")); +// $this->AddElement(new GoogleSitemapGeneratorDebugEntry("sitemap-generator-url=\"http://www.arnebrachhold.de\" sitemap-generator-version=\"" . $this->GetVersion() . "\"")); +// $this->AddElement(new GoogleSitemapGeneratorDebugEntry("generated-on=\"" . date(get_option("date_format") . " " . get_option("time_format")) . "\"")); //All comments as an asso. Array (postID=>commentCount) $comments=($this->GetOption("b_prio_provider")!=""?$this->GetComments():array()); @@ -2172,6 +2172,22 @@ $status->EndXml(true); $pingUrl=$this->GetXmlUrl(); } else $status->EndXml(false,"Could not close the sitemap file."); + + if (class_exists('XSLTProcessor', true)) { + $file = $this->GetXmlPath(); + $styleSheet = ($this->GetDefaultStyle() && $this->GetOption('b_style_default') === true ? $this->GetDefaultStyle() : $this->GetOption('b_style')); + if (!empty($styleSheet)) { + $xsl = new DOMDocument(); + $xsl->load($styleSheet); + + $xml = new DOMDocument(); + $xml->load($file); + + $proc = new XSLTProcessor(); + $proc->importStyleSheet($xsl); + $proc->transformToURI($xml, 'file://' . str_replace('.xml', '.html', $file)); + } + } } if($this->IsGzipEnabled()) { @@ -2514,9 +2530,9 @@ * @return int The time in seconds */ function GetTimestampFromMySql($mysqlDateTime) { - list($date, $hours) = split(' ', $mysqlDateTime); - list($year,$month,$day) = split('-',$date); - list($hour,$min,$sec) = split(':',$hours); + list($date, $hours) = explode(' ', $mysqlDateTime); + list($year,$month,$day) = explode('-',$date); + list($hour,$min,$sec) = explode(':',$hours); return mktime(intval($hour), intval($min), intval($sec), intval($month), intval($day), intval($year)); }