Los creadores de sortea2 siempre hemos tenido que tratar con el mismo problema en todas las webs que hemos tenido que realizar: generar los sitemaps.
En qué consiste un sitemap
Un sitemap no es más que un fichero XML en el que ponemos todas las URLs que tiene nuestro sitio web con el fin de que Google, Bing o el robot que quiera lo lea y sea capaz de indexar todo el sitio mucho más fácilmente de lo que lo haría normalmente.
A estas URLs les podemos dar además una prioridad, que es un número decimal de entre 0 y 1 que puede servirle como dato orientativo para el motor de búsqueda. No tiene lógica ninguna darle prioridad 1 a todas las páginas con la idea de que nuestras páginas indexen mejor porque el sistema no va así. Está hecho para diferenciar importancias entre diversas secciones del sitio web y cosas así. Luego será el propio motor de búsqueda el que determine qué vale la pena más o menos.
Un sitemap en su forma más simple es algo como esto:
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.google.com/schemas/sitemap/0.84">
<url>
<loc>http://www.sortea2.com</loc>
<priority>0.6</priority>
</url>
</urlset>
Dentro de cada <url> meteremos en <loc> el link en sí y luego su prioridad. Dentro del urlset irán todas las URLs.
Un sitemap así será fácil de generar con cualquier lenguaje que queramos (PHP, python, perl, etc.), puesto que simplemente es meter en un bucle todas las URLs e ir poniéndolas en ese formato XML.
El problema viene en que existe un límite de URLs que puede tener un sitemap. El límite está en 50.000 páginas y 10MB como máximo.
En este caso habrá que dividir los sitemaps en el máximo e ir separándolos mediante Sitemap indexs. Esto no es más que otro XML con la ruta de cada sitemap al que habrá que acceder.
Un Sitemap Index tiene la siguiente estructura:
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.google.com/schemas/sitemap/0.84">
<sitemap>
<loc>http://www.sortea2.com/sitemap/sitemap1.xml</loc>
</sitemap>
<sitemap>
<loc>http://www.sortea2.com/sitemap/sitemap2.xml</loc>
</sitemap>
</sitemapindex>
Hasta aquí la breve explicación de en qué consisten los sitemaps.
Proceso habitual para generarlos
Habitualmente los generaremos mediante una tarea programada a una hora a la que veamos que nuestro tráfico baja considerablemente y ésta se encargará de escribir todo lo que sea necesario.
En PHP tenemos el problema de que por defecto los servidores suelen capar el máximo tiempo de ejecución del script a 30, 60 o 90 segundos, y si vamos a generar una cantidad grande de sitemaps implicando consultas de MySQL complicadas lo más normal es que nos quedemos cortos.
Para este problema habría que poner al principio del todo del script: set_time_limit(0); para que se le permita tardar todo lo que necesite. Aún así habrán servidores que tendrán fijado un tiempo máximo y no dejará de ninguna de las maneras cambiarlo.
Otra opción bastante viable puede ser usar Python o Perl; lenguajes mucho más veloces que PHP y que suelen venir instalados en hostings compartidos.
Primero se generarían los distintos sitemaps, cortando cada archivo cuando se llegue al límite y al final del todo se creará un índice de sitemaps conteniendo las URLs de todos los que acabamos de generar.
La manera de implementarlo ya variará dependiendo de cómo lo vayamos a hacer.
Consejos para generar miles de ellos
Si tienes que enfrentarte a la generación de una cantidad enorme de sitemaps, te convendrá tener en cuenta algunos consejos:
- Escribe de golpe en los archivos con fwrite() pero sin usar flush(). De esta manera solo utilizaremos la memoria indispensable en cada momento.
- Utiliza tablas temporales de MySQL cuando haya que hacer consultas complejas: una buena práctica que podemos necesitar es crear una tabla temporal. Si la consulta que va a generar el sitemap implica a muchas tablas, agrupaciones, eliminación de duplicados, etc. te puede convenir crear una tabla temporal que contenga simplemente los ids de los elementos a los que acceder de tal manera que luego las consultas que se hagan sean directas.
Con este sistema el mal trago solo pasará una vez al crear la tabla temporal y aún así se ejecutará mucho más rápido que la consulta en sí porque no necesita generar ningún cursor. Las consultas directas sobre esa tabla serán tan simples que serán inmediatas.
Lo óptimo es borrar la tabla temporal tan pronto como se pueda.
- Parte en trozos la consulta que vas a tener que hacer: no es recomendable coger todos los datos de golpe e introducirlos en un cursor. Si tienes un millón de registros esa operación es inviable, porque tendrá que gastar una cantidad brutal de memoria (dependiendo de los datos) que realmente no va a valer para nada, porque cada vez solo vamos a leer un dato. Además, si tenemos que realizar esta consulta inmensa junto con algún tipo de ORDER BY, DISTINCT o algo así, es muy probable que agotemos la memoria interna de MySQL y nos dé un error interno grave de Incorrect key file for table ‘/tmp/#sql_7cd7_0.MYI’; try to repair it que quiere decir que no se ha podido generar la tabla temporal necesaria.
La manera de partir en cachos el resultado de la consulta es meterla dentro de un while() que vaya cogiendo trozos del tamaño que veamos (50.000, 100.000 o lo que sea) con un LIMIT y que pare cuando ya no haya más datos.
Así se irán escribiendo las URLs usando no demasiada memoria y se leerán de inmediato gracias a la tabla temporal.
- Escribe todos los sitemaps olvidando saltos de línea y tabulaciones: Google no te va a premiar por escribir tus sitemaps completamente bien estructurados y legibles. Escríbelo todo en una sola línea continua con el menor número de espacios posible y así ahorrarás espacio en disco.
Estamos hablando de miles de sitemaps, por lo que la diferencia si puede ser notable. En pocos sitemaps esto sería absurdo e innecesario.
- Se pueden comprimir usando GZIP: si lo consideras oportuno puedes comprimir tus sitemaps usando GZIP, de esta manera ocuparán mucho menos y ahorrarás ancho de banda.
Es de libre elección hacerlo o no. Por un lado tiene la ventaja de que ahorra disco duro y ancho de banda pero por otro tiene la desventaja de que te costará muchísimo más el generarlos porque la compresión tardará también lo suyo.
Se pueden generar todos normalmente y luego lanzar un proceso que vaya comprimiendo uno a uno. No habría problema ninguno y serían perfectamente válidos.