11.3. Características de HTTP

Hay cinco características importantes de HTTP que debería tener en cuenta.

11.3.1. User-Agent

El User-Agent es simplemente una manera para que el cliente indique al servidor quién es cuando solicita una página web, un feed sindicado o cualquier tipo de servicio web sobre HTTP. Cuando el cliente solicite un recurso, siempre debería anunciar quién es, lo más específicamente que sea posible. Esto permite al administrador del servidor estar en contacto con el desarrollador del lado del usuario en caso de que algo vaya espectacularmente mal.

Python envía un User-Agent genérico por omisión: Python-urllib/1.15. En la siguiente sección veremos cómo cambiar esto a algo más específico.

11.3.2. Redirecciones

A veces se cambia de sitio un recurso. Los sitios web se reorganizan, las páginas pasan a tener direcciones nuevas. Incluso se puede reorganizar un servicio web. Una sindicación en http://example.com/index.xml puede convertirse en http://example.com/xml/atom.xml. O puede que cambie un dominio completo, al expandirse y reorganizarse una organización; por ejemplo, http://www.example.com/index.xml podría redirigirse a http://server-farm-1.example.com/index.xml.

Cada vez que solicita cualquier tipo de recurso a un servidor HTTP, el servidor incluye un código de estado en su respuesta. El código 200 significa “todo normal, aquí está la página que solicitó”. El código 404 significa “no encontré la página” (probablemente haya visto errores 404 al navegar por la web).

HTTP tiene dos maneras diferentes de indicar que se ha cambiado de sitio un recurso. El código de estado 302 es una redirección temporal; significa “ups, lo hemos movido temporalmente aquí” (y entonces da la dirección temporal en una cabecera Location:). El código de estado 301 es una redirección permanente; significa “ups, lo movimos permanentemente” (y entonces da la dirección temporal en una cabecera Location:). Si recibe un código de estado 302 y una dirección nueva, la especificación de HTTP dice que debería usar la nueva dirección que le indicaron, pero la siguiente vez que acceda al mismo recurso, puede intentar de nuevo con la dirección antigua. Pero si obtiene un código 301 y una nueva dirección, se espera que la utilice siempre de ahí en adelante.

urllib.urlopenseguirá” automáticamente las redirecciones cuando reciba del servidor HTTP el código apropiado, pero desafortunadamente no nos lo indica cuando lo hace. Obtendremos los datos que solicitamos, pero nunca sabremos que la biblioteca tuvo la “atención” de seguir la redirección por nosotros. Así que seguiremos usando la dirección antigua y nos redirigirán cada vez a la nueva. Así damos dos rodeos en lugar de uno: ¡no es muy eficiente! Más adelante en este capítulo verá cómo evitar esto para poder tratar adecuada y eficientemente las redirecciones permanentes.

11.3.3. Last-Modified/If-Modified-Since

Algunos datos cambian con el tiempo. La página principal de CNN.com cambia constantemente cada pocos minutos. Por otro lado, la página principal de Google.com sólo cambia una vez cada pocas semanas (cuando ponen un logotipo especial de fiesta, o anuncian un nuevo servicio). Los servicios web no son diferentes; normalmente el servidor sabe cuándo fue la última vez que cambiaron los datos solicitados, y HTTP proporciona una manera para que el servidor incluya esta fecha de última modificación junto con los datos pedidos.

Si pedimos los mismos datos una segunda vez (o tercera, o cuarta), podemos decirle al servidor la fecha de última modificación que obtuvimos la vez anterior: enviamos junto a la petición la cabecera If-Modified-Since, con la fecha que nos dieron la última vez. Si los datos no han cambiado desde entonces, el servidor envía un código de estado HTTP especial, 304, que significa “estos datos no han cambiado desde la última vez que los pediste”. ¿Por qué supone esto una mejora? Porque cuando el servidor envía un 304, no envía los datos. Todo lo que obtenemos es el código de estado. Así que no hace falta descargar los mismos datos una y otra vez si no han cambiado; el servidor asume que los tenemos en caché local.

Todos los navegadores web modernos admiten la comprobación de fecha de última modificación. Si entró en una página, la visitó de nuevo un día después encontrando que no había cambiado, y se preguntó por qué había cargado tan rápidamente la segunda vez, ésta podría ser la respuesta. El navegador dejó el contenido de la página en una caché local la primera vez, y cuando la visitamos de nuevo el servidor envió automáticamente la última fecha de modificación que obtuvo del servidor. El servidor se limita a decir 304: Not Modified, así que el navegador sabe que debe cargar la página de la cache. Los servicios web también pueden ser así de inteligentes.

La biblioteca de URL de Python no incorpora la verificación de última modificación pero, dado que podemos incluir cabeceras arbitrarias a cada consulta y leer las cabeceras de cada respuesta, podemos añadir esta funcionalidad nosotros mismos.

11.3.4. ETag/If-None-Match

Las ETags son una manera alternativa de conseguir lo mismo que la comprobación de última modificación: no descargar de nuevo datos que no han variado. Funciona así, el servidor envía algún tipo de suma de comprobación de los datos (en una cabecera ETag) junto con los datos solicitados. La manera en que se determina esta suma es asunto del servidor. La segunda vez que pedimos esos mismos datos, incluimos la suma ETag en una cabecera If-None-Match: y, si los datos no han cambiado, el servidor nos devolverá un código 304. Igual que con la comprobación de última modificación el servidor simplemente envía el 304; no envía los mismos datos una segunda vez. Al incluir la suma de comprobación ETag en la segunda consulta, le estamos diciendo al servidor que no necesita enviarlos los datos si aún coinciden con esta suma, ya que aún conservamos los de la última consulta.

La biblioteca de URL de Python no incorpora la funcionalidad de las ETag, pero veremos cómo añadirla más adelante en este capítulo.

11.3.5. Compresión

La última característica importante de HTTP es la compresión gzip. Cuando hablamos de servicios web HTTP, casi siempre hablamos de mover datos XML por los cables. XML es texto, la verdad es que bastante prolijo, y el texto se comprime bien por lo general. Cuando solicitamos un recurso mediante HTTP podemos pedirle al servidor que, si tiene datos nuevos que enviarnos, haga el favor de enviarlos en un formato comprimido. Incluimos la cabecera Accept-encoding: gzip en la consulta y, si el servidor admite compresión, enviará los datos comprimidos con gzip y lo indicará con una cabecera Content-encoding: gzip.

La biblioteca de URL de Python no admite per se la compresión gzip, pero podemos añadir cabeceras arbitrarias a la consulta. Y Python incluye un módulo gzip con funciones que podemos usar para descomprimir los datos nosotros mismos.

Advierta que nuestro pequeño script de una línea para descargar feeds sindicados no admite ninguna de estas características de HTTP. Veamos cómo mejorarlo.