El libro de Django 1.0

11.6. Los sitemaps de XML

Un sitemap es un fichero XML en tu sitio web que le indica a los indexadores de los motores de búsqueda cuan frecuentemente cambian tus páginas así como la "importancia" relativa de ciertas páginas en relación con otras (siempre hablando de páginas de tu sitio). Esta información ayuda a los motores de búsqueda a indexar tu sitio.

Por ejemplo, esta es una parte del sitemap del sitio web de Django que se encuentra en http://djangoproject.com/sitemap.xml:

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>http://www.djangoproject.com/documentation/</loc>
    <changefreq>weekly</changefreq>
    <priority>0.5</priority>
  </url>
  <url>
    <loc>http://www.djangoproject.com/documentation/0_90/</loc>
    <changefreq>never</changefreq>
    <priority>0.1</priority>
  </url>
  ...
</urlset>

Para más información sobre sitemaps, vea http://www.sitemaps.org/.

El framework sitemap de Django automatiza la creación de este fichero XML si lo indicas expresamente en el código Python. Para crear un sitemap, debes simplemente escribir una clase Sitemap y hacer referencia a la misma en tu URLconf.

11.6.1. Instalación

Para instalar la aplicación sitemap, sigue los siguientes pasos:

  1. Agrega 'django.contrib.sitemaps' a tu variable de configuración INSTALLED_APPS.
  2. Asegúrate de que 'django.template.loaders.app_directories.load_template_source' está en tu variable de configuración TEMPLATE_LOADERS. Por omisión se encuentra activado, por lo que los cambios son necesarios solamente si modificaste dicha variable de configuración.
  3. Asegúrate de que tienes instalado el framework sites (ver Capítulo 14).

Nota La aplicación sitemap no instala tablas en la base de datos. La única razón de que esté en INSTALLED_APPS es que el cargador de plantillas load_template_source pueda encontrar las plantillas incluídas.

11.6.2. Inicialización

Para activar la generación del sitemap en tu sitio Django, agrega la siguiente línea a tu URLconf:

(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})

Esta línea le dice a Django que construya un sitemap cuando un cliente accede a /sitemap.xml.

El nombre del fichero sitemap no es importante, pero la ubicación sí lo es. Los motores de búsqueda solamente indexan los enlaces en tu sitemap para el nivel de URL actual y anterior. Por ejemplo, si sitemap.xml reside en tu directorio principal, el mismo puede hacer referencia a cualquier URL en tu sitio. Pero si tu sitemap reside en /content/sitemap.xml, solamente podrá hacer referencia a URLs que comiencen con /content/.

La vista sitemap toma un argumento extra: {'sitemaps': sitemaps}. sitemaps debe ser un diccionario que mapee una etiqueta corta de sección (p. ej. blog o news) a tu clase Sitemap (p.e., BlogSitemap o NewsSitemap). También mapea hacia una instancia de una clase Sitemap (p. ej. BlogSitemap(some_var)).

11.6.3. Clases Sitemap

Una clase Sitemap es simplemente una clase Python que representa una "sección" de entradas en tu sitemap. Por ejemplo, una clase Sitemap puede representar todas las entradas de tu weblog, y otra puede representar todos los eventos de tu calendario.

En el caso más simple, todas estas secciones se unen en un único sitemap.xml, pero también es posible usar el framework para generar un índice sitemap que haga a referencia ficheros sitemap individuales, uno por sección ( describiéndolo sintéticamente).

Las clases Sitemap debe ser una subclase de django.contrib.sitemaps.Sitemap. Estas pueden residir en cualquier parte del árbol de código.

Por ejemplo, asumamos que posees un sistema de blog, con un modelo Entry, y quieres que tu sitemap incluya todos los enlaces a las entradas individuales de tu Blog. Tu clase Sitemap debería verse así:

from django.contrib.sitemaps import Sitemap
from mysite.blog.models import Entry

class BlogSitemap(Sitemap):
    changefreq = "never"
    priority = 0.5

    def items(self):
        return Entry.objects.filter(is_draft=False)

    def lastmod(self, obj):
        return obj.pub_date

Declarar un Sitemap debería verse muy similar a declarar un Feed; esto es justamente un objetivo del diseño.

En manera similar a las clases Feed, los miembros de Sitemap pueden ser métodos o atributos. Ver los pasos en la sección "Un feed más complejo" para más información sobre cómo funciona esto.

Una clase Sitemap puede definir los siguientes métodos/atributos:

  • items (requerido): Provee una lista de objetos. Al framework no le importa que tipo de objeto es; todo lo que importa es que los objetos sean pasados a los métodos location(), lastmod(), changefreq(), y priority().
  • location (opcional): Provee la URL absoluta para el objeto dado. Aquí "URL absoluta" significa una URL que no incluye el protocolo o el dominio. Estos son algunos ejemplos:
    • Bien: '/foo/bar/'
    • Mal: 'example.com/foo/bar/'
    • Mal: 'http://example.com/foo/bar/' Si location no es provisto, el framework llamará al método get_absolute_url() en cada uno de los objetos retornados por items().
  • lastmod (opcional): La fecha de "última modificación" del objeto, como un objeto datetime de Python.
  • changefreq (opcional): Cuán a menudo el objeto cambia. Los valores posibles (según indican las especificaciones de Sitemaps) son:
    • 'always'
    • 'hourly'
    • 'daily'
    • 'weekly'
    • 'monthly'
    • 'yearly'
    • 'never'
  • priority (opcional): Prioridad sugerida de indexado entre 0.0 y 1.0. La prioridad por omisión de una página es 0.5; ver la documentación de http://sitemaps.org para más información de cómo funciona priority.

11.6.4. Accesos directos

El framework sitemap provee un conjunto de clases para los casos más comunes. Describiremos estos casos en las secciones a continuación.

11.6.4.1. FlatPageSitemap

La clase django.contrib.sitemaps.FlatPageSitemap apunta a todas las páginas planas definidas para el sitio actual y crea una entrada en el sitemap. Estas entradas incluyen solamente el atributo location — no lastmod, changefreq, o priority.

Para más información sobre Páginas Planas ver el Capítulo 14.

11.6.4.2. Sitemap Genérico

La clase GenericSitemap trabaja con cualquier vista genérica (ver Capítulo 9) que hayas definido con anterioridad.

Para usarla, crea una instancia, pasándola en el mismo info_dict que se pasa a la vista genérica. El único requerimiento es que el diccionario tenga una entrada queryset. También debe poseer una entrada date_field que especifica un campo fecha para los objetos obtenidos del queryset. Esto será usado por el atributo lastmod en el sitemap generado. También puedes pasar los argumentos palabra clave (keyword) priority y changefreq al constructor GenericSitemap para especificar dichos atributos para todas las URLs.

Este es un ejemplo de URLconf usando tanto, FlatPageSitemap como GenericSiteMap (con el anterior objeto hipotético Entry):

from django.conf.urls.defaults import *
from django.contrib.sitemaps import FlatPageSitemap, GenericSitemap
from mysite.blog.models import Entry

info_dict = {
    'queryset': Entry.objects.all(),
    'date_field': 'pub_date',
}

sitemaps = {
    'flatpages': FlatPageSitemap,
    'blog': GenericSitemap(info_dict, priority=0.6),
}

urlpatterns = patterns('',
    # some generic view using info_dict
    # ...

    # the sitemap
    (r'^sitemap.xml$',
     'django.contrib.sitemaps.views.sitemap',
     {'sitemaps': sitemaps})
)

11.6.5.  Crear un índice Sitemap

El framework sitemap también tiene la habilidad de crear índices sitemap que hagan referencia a ficheros sitemap individuales, uno por cada sección definida en tu diccionario sitemaps. Las única diferencias de uso son:

  • Usas dos vistas en tu URLconf: django.contrib.sitemaps.views.index y django.contrib.sitemaps.views.sitemap.
  • La vista django.contrib.sitemaps.views.sitemap debe tomar un parámetro que corresponde a una palabra clave, llamado section.

Así deberían verse las líneas relevantes en tu URLconf para el ejemplo anterior:

(r'^sitemap.xml$',
 'django.contrib.sitemaps.views.index',
 {'sitemaps': sitemaps}),

(r'^sitemap-(?P<section>.+).xml$',
 'django.contrib.sitemaps.views.sitemap',
 {'sitemaps': sitemaps})

Esto genera automáticamente un fichero sitemap.xml que hace referencia a ambos ficheros sitemap-flatpages.xml y sitemap-blog.xml. La clase Sitemap y el diccionario sitemaps no cambian en absoluto.

11.6.6. Hacer ping a Google

Puedes desear hacer un "ping" a Google cuando tu sitemap cambia, para hacerle saber que debe reindexar tu sitio. El framework provee una función para hacer justamente eso: django.contrib.sitemaps.ping_google().

Nota Hasta el momento en que este libro se escribió, únicamente Google responde a los pings de sitemap. Pero es muy probable que pronto Yahoo y/o MSN también admitan estos pings.

Cuando eso suceda, cambiaremos el nombre de ping_google() a algo como ping_search_engines(), así que asegúrate de verificar la ultima documentación sobre sitemap.

ping_google() toma un argumento opcional, sitemap_url, que debe ser la URL absoluta de tu sitemap (por ej., '/sitemap.xml'). Si este argumento no es provisto, ping_google() tratará de generar un sitemap realizando una búsqueda reversa en tu URLconf.

ping_google() lanza la excepción django.contrib.sitemaps.SitemapNotFound si no puede determinar la URL de tu sitemap.

Una forma útil de llamar a ping_google() es desde el método save():

from django.contrib.sitemaps import ping_google

class Entry(models.Model):
    # ...
    def save(self):
        super(Entry, self).save()
        try:
            ping_google()
        except Exception:
            # Bare 'except' because we could get a variety
            # of HTTP-related exceptions.
            pass

Una solución más eficiente, sin embargo, sería llamar a ping_google() desde un script cron o un manejador de tareas. La función hace un pedido HTTP a los servidores de Google, por lo que no querrás introducir esa demora asociada a la actividad de red cada vez que se llame al método save().