Estuve el domingo unas cuantas horas buscando por internet como hacer esto y me estaba frustrando. Mi nuevo mini-proyecto secreto requiere monitorizar unas imágenes regularmente para ir actualizándolas a medida que van cambiando mediante un cron script que se ejecuta cada 15 minutos. Hay dos opciones:

  1. Bajarnos las imágenes todas las veces y comparar el md5sum para ver si han cambiado
  2. o hacer un pedido HEAD en vez de GET y hacer uso de las cabeceras Last-Modified y ETag.

¿Que cuales son las ventajas del punto dos? Lo más importante es que nos ahorramos ancho de banda nosotros y a los que le estemos bajando las imágenes, tampoco es cuestión de abusar del servidor de otra persona. Ojo que esto no aplica sólo a las imágenes, cualquier servidor moderno de hoy en día tiene soporte para estas cabeceras y las devolverá con cualquier tipo de archivo, incluso los que estén generados dinámicamente. Nos ahorramos ancho de banda porque sólo pedimos la cabecera del archivo que exceptuando casos extremos será mucho más pequeño que el resto del documento. Esto es especialmente útil para el contenido estático (js, imagenes, css, etc) y contenido que se baja regularmente como feeds RSS. Además de ahorrar ancho de banda también hacemos que el script vaya más rápido ya que no tiene que procesar todo el documento.

La idea es que hacemos un pedido HEAD inicial y guardamos el Last-Modified y ETag, luego en los subsiguientes pedidos enviamos pedidos GET con las cabeceras If-Modified-Since y If-None-Match. If-Modified-Since se usa con la fecha que guardamos anteriormente y If-None-Match con el ETag. Si ambos campos son los mismos en el archivo actual del lado del servidor entonces este debe responder con un ‘304 Not Modified’

import StringIO
import pycurl

c = pycurl.Curl()
s = StringIO.StringIO()

c.setopt(pycurl.URL, 'http://theragingche.com/images/hand_made_cms.gif')
c.setopt(pycurl.HEADER, True) # estas dos lineas son las que importan
c.setopt(pycurl.NOBODY, True) # para hacer un pedido HEAD
c.setopt(pycurl.WRITEFUNCTION, s.write)

c.perform()
print s.getvalue()

Nos devuelve solo la cabecera:

$ python head.py
HTTP/1.1 200 OK
Date: Mon, 26 Nov 2007 12:29:50 GMT
Server: Apache/2.0.54 (Fedora)
Last-Modified: Thu, 16 Mar 2006 19:02:45 GMT
ETag: "303247c3-105-40f21566a7f40"
Accept-Ranges: bytes
Content-Length: 261
Connection: close
Content-Type: image/gif
X-Pad: avoid browser bug

Si luego hacemos:

import StringIO
import pycurl 

c = pycurl.Curl()
s = StringIO.StringIO()

c.setopt(pycurl.URL, 'http://theragingche.com/images/hand_made_cms.gif')

# especificamos las cabeceras
c.setopt(pycurl.HTTPHEADER,
    ['If-Modified-Since: Thu, 16 Mar 2006 19:02:45 GMT',
    'If-None-Match: "303247c3-105-40f21566a7f40"'])

c.setopt(pycurl.WRITEFUNCTION, s.write)
c.perform()

print c.getinfo(pycurl.RESPONSE_CODE)

Nos da:

$ python head.py
304
 

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Set your Twitter account name in your settings to use the TwitterBar Section.