{"id":691,"date":"2011-02-27T14:22:04","date_gmt":"2011-02-27T13:22:04","guid":{"rendered":"http:\/\/www.sortea2.com\/blog\/2011\/02\/generar-sitemaps-de-millones-de-pginas\/"},"modified":"2011-02-27T14:24:06","modified_gmt":"2011-02-27T13:24:06","slug":"generar-sitemaps-de-millones-de-pginas","status":"publish","type":"post","link":"https:\/\/www.sortea2.com\/blog\/2011\/02\/generar-sitemaps-de-millones-de-pginas\/","title":{"rendered":"Generar sitemaps de millones de p&aacute;ginas"},"content":{"rendered":"<p>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.<\/p>\n<h2>En qu\u00e9 consiste un sitemap<\/h2>\n<p>Un sitemap no es m\u00e1s 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\u00e1s f\u00e1cilmente de lo que lo har\u00eda normalmente.<\/p>\n<p>A estas URLs les podemos dar adem\u00e1s una prioridad, que es un n\u00famero decimal de entre 0 y 1 que puede servirle como dato orientativo para el motor de b\u00fasqueda. No tiene l\u00f3gica ninguna darle prioridad 1 a todas las p\u00e1ginas con la idea de que nuestras p\u00e1ginas indexen mejor porque el sistema no va as\u00ed. Est\u00e1 hecho para diferenciar importancias entre diversas secciones del sitio web y cosas as\u00ed. Luego ser\u00e1 el propio motor de b\u00fasqueda el que determine qu\u00e9 vale la pena m\u00e1s o menos.<\/p>\n<p>Un sitemap en su forma m\u00e1s simple es algo como esto:<\/p>\n<p> <code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;    <br \/>&lt;urlset xmlns=&quot;<a href=\"http:\/\/www.google.com\/schemas\/sitemap\/0.84&quot;\">http:\/\/www.google.com\/schemas\/sitemap\/0.84&quot;<\/a>&gt;     <br \/>&lt;url&gt;     <br \/>&#160; &lt;loc&gt;<a href=\"http:\/\/www.sortea2.com\">http:\/\/www.sortea2.com<\/a>&lt;\/loc&gt;     <br \/>&#160; &lt;priority&gt;0.6&lt;\/priority&gt;     <br \/>&lt;\/url&gt;     <br \/>&lt;\/urlset&gt; <\/code>  <\/p>\n<p>&#160;<\/p>\n<p>Dentro de cada &lt;url&gt; meteremos en &lt;loc&gt; el link en s\u00ed y luego su prioridad. Dentro del urlset ir\u00e1n todas las URLs.<\/p>\n<p>Un sitemap as\u00ed ser\u00e1 f\u00e1cil 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\u00e9ndolas en ese formato XML.<\/p>\n<p>El problema viene en que <strong>existe un l\u00edmite de URLs<\/strong> que puede tener un sitemap. El l\u00edmite est\u00e1 en 50.000 p\u00e1ginas y 10MB como m\u00e1ximo.<\/p>\n<p>En este caso habr\u00e1 que dividir los sitemaps en el m\u00e1ximo e ir separ\u00e1ndolos mediante <strong>Sitemap indexs<\/strong>. Esto no es m\u00e1s que otro XML con la ruta de cada sitemap al que habr\u00e1 que acceder.<\/p>\n<p>Un Sitemap Index tiene la siguiente estructura:<\/p>\n<p> <code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;    <br \/>&lt;sitemapindex xmlns=&quot;http:\/\/www.google.com\/schemas\/sitemap\/0.84&quot;&gt;     <br \/>&lt;sitemap&gt;     <br \/>&lt;loc&gt;http:\/\/www.sortea2.com\/sitemap\/sitemap1.xml&lt;\/loc&gt;     <br \/>&lt;\/sitemap&gt;     <br \/>&lt;sitemap&gt;     <br \/>&lt;loc&gt;http:\/\/www.sortea2.com\/sitemap\/sitemap2.xml&lt;\/loc&gt;     <br \/>&lt;\/sitemap&gt;     <br \/>&lt;\/sitemapindex&gt; <\/code>  <\/p>\n<p>&#160;<\/p>\n<p>Hasta aqu\u00ed la breve explicaci\u00f3n de en qu\u00e9 consisten los sitemaps.<\/p>\n<h2>Proceso habitual para generarlos<\/h2>\n<p>Habitualmente los generaremos mediante una tarea programada a una hora a la que veamos que nuestro tr\u00e1fico baja considerablemente y \u00e9sta se encargar\u00e1 de escribir todo lo que sea necesario.<\/p>\n<p>En PHP tenemos el problema de que por defecto los servidores suelen capar el m\u00e1ximo tiempo de ejecuci\u00f3n 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\u00e1s normal es que nos quedemos cortos.<\/p>\n<p>Para este problema habr\u00eda que poner al principio del todo del script: <a href=\"http:\/\/es.php.net\/manual\/en\/function.set-time-limit.php\">set_time_limit<\/a>(0); para que se le permita tardar todo lo que necesite. A\u00fan as\u00ed habr\u00e1n servidores que tendr\u00e1n fijado un tiempo m\u00e1ximo y no dejar\u00e1 de ninguna de las maneras cambiarlo.<\/p>\n<p>Otra opci\u00f3n bastante viable puede ser usar Python o Perl; lenguajes mucho m\u00e1s veloces que PHP y que suelen venir instalados en hostings compartidos.<\/p>\n<p>Primero se generar\u00edan los distintos sitemaps, cortando cada archivo cuando se llegue al l\u00edmite y al final del todo se crear\u00e1 un \u00edndice de sitemaps conteniendo las URLs de todos los que acabamos de generar.<\/p>\n<p>La manera de implementarlo ya variar\u00e1 dependiendo de c\u00f3mo lo vayamos a hacer.<\/p>\n<h2>Consejos para generar miles de ellos<\/h2>\n<p>Si tienes que enfrentarte a la generaci\u00f3n de una cantidad enorme de sitemaps, te convendr\u00e1 tener en cuenta algunos consejos:<\/p>\n<ul>\n<li>Escribe de golpe en los archivos con fwrite() pero sin usar flush(). De esta manera solo utilizaremos la memoria indispensable en cada momento. <\/li>\n<li>Utiliza <strong>tablas temporales de MySQL<\/strong> cuando haya que hacer consultas complejas: una buena pr\u00e1ctica que podemos necesitar es crear una tabla temporal. Si la consulta que va a generar el sitemap implica a muchas tablas, agrupaciones, eliminaci\u00f3n 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.\n<p>Con este sistema el mal trago solo pasar\u00e1 una vez al crear la tabla temporal y a\u00fan as\u00ed se ejecutar\u00e1 mucho m\u00e1s r\u00e1pido que la consulta en s\u00ed porque no necesita generar ning\u00fan cursor. Las consultas directas sobre esa tabla ser\u00e1n tan simples que ser\u00e1n inmediatas.       <\/p>\n<p>Lo \u00f3ptimo es borrar la tabla temporal tan pronto como se pueda. <\/li>\n<li>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\u00f3n de registros esa operaci\u00f3n es inviable, porque tendr\u00e1 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\u00e1s, si tenemos que realizar esta consulta inmensa junto con alg\u00fan tipo de ORDER BY, DISTINCT o algo as\u00ed, es muy probable que agotemos <strong>la memoria interna de MySQL<\/strong> y nos d\u00e9 un error interno grave de <strong><em>Incorrect key file for table &#8216;\/tmp\/#sql_7cd7_0.MYI&#8217;; try to repair it <\/em><\/strong>que quiere decir que no se ha podido generar la tabla temporal necesaria.\n<p>La manera de partir en cachos el resultado de la consulta es meterla dentro de un while() que vaya cogiendo trozos del tama\u00f1o que veamos (50.000, 100.000 o lo que sea) con un LIMIT y que pare cuando ya no haya m\u00e1s datos.       <\/p>\n<p>As\u00ed se ir\u00e1n escribiendo las URLs usando no demasiada memoria y se leer\u00e1n de inmediato gracias a la tabla temporal. <\/li>\n<li>Escribe todos los sitemaps olvidando saltos de l\u00ednea y tabulaciones: Google no te va a premiar por escribir tus sitemaps completamente bien estructurados y legibles. Escr\u00edbelo todo en una sola l\u00ednea continua con el menor n\u00famero de espacios posible y as\u00ed ahorrar\u00e1s espacio en disco.\n<p>Estamos hablando de miles de sitemaps, por lo que la diferencia si puede ser notable. En pocos sitemaps esto ser\u00eda absurdo e innecesario. <\/li>\n<li>Se pueden comprimir usando GZIP: si lo consideras oportuno puedes comprimir tus sitemaps usando GZIP, de esta manera ocupar\u00e1n mucho menos y ahorrar\u00e1s ancho de banda.\n<p>Es de libre elecci\u00f3n 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\u00e1 much\u00edsimo m\u00e1s el generarlos porque la compresi\u00f3n tardar\u00e1 tambi\u00e9n lo suyo.       <\/p>\n<p>Se pueden generar todos normalmente y luego lanzar un proceso que vaya comprimiendo uno a uno. No habr\u00eda problema ninguno y ser\u00edan perfectamente v\u00e1lidos. <\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>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\u00e9 consiste un sitemap Un sitemap no es m\u00e1s que un fichero XML en el que ponemos todas las URLs que tiene nuestro sitio web con el fin de [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[44,57,56],"class_list":["post-691","post","type-post","status-publish","format-standard","hentry","category-tutoriales","tag-mysql","tag-python","tag-sitemap"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.sortea2.com\/blog\/wp-json\/wp\/v2\/posts\/691","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sortea2.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sortea2.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sortea2.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sortea2.com\/blog\/wp-json\/wp\/v2\/comments?post=691"}],"version-history":[{"count":2,"href":"https:\/\/www.sortea2.com\/blog\/wp-json\/wp\/v2\/posts\/691\/revisions"}],"predecessor-version":[{"id":693,"href":"https:\/\/www.sortea2.com\/blog\/wp-json\/wp\/v2\/posts\/691\/revisions\/693"}],"wp:attachment":[{"href":"https:\/\/www.sortea2.com\/blog\/wp-json\/wp\/v2\/media?parent=691"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sortea2.com\/blog\/wp-json\/wp\/v2\/categories?post=691"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sortea2.com\/blog\/wp-json\/wp\/v2\/tags?post=691"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}