Existen dos módulos principales para leer datos de URLs en Python: urllib
y urllib2
. En esta lección aprenderemos a utilizar urllib2
ya que es mucho más completo, aunque urllib
tiene funcionalidades propias que no se pueden encontrar en urllib2
, por lo que también lo tocaremos de pasada.
urllib2
puede leer datos de una URL usando varios protocolos como HTTP, HTTPS, FTP, o Gopher.
Se utiliza una función urlopen
para crear un objeto parecido a un fichero con el que leer de la URL. Este objeto cuenta con métodos como read
, readline
, readlines
y close
, los cuales funcionan exactamente igual que en los objetos file
, aunque en realidad estamos trabajando con un wrapper que nos abstrae de un socket que se utiliza por debajo.
El método read
, como recordareis, sirve para leer el “archivo” completo o el número de bytes especificado como parámetro, readline
para leer una línea, y readlines
para leer todas las líneas y devolver una lista con ellas.
También contamos con un par de métodos geturl
, para obtener la URL de la que estamos leyendo (que puede ser útil para comprobar si ha habido una redirección) e info
que nos devuelve un objeto con las cabeceras de respuesta del servidor (a las que también se puede acceder mediante el atributo headers
).
- import urllib2
- try:
- f = urllib2.urlopen(«http://www.python.org«)
- print f.read()
- f.close()
- except HTTPError, e:
- print «Ocurrió un error»
- print e.code
- except URLError, e:
- print «Ocurrió un error»
- print e.reason
import urllib2 try: f = urllib2.urlopen("http://www.python.org") print f.read() f.close() except HTTPError, e: print "Ocurrió un error" print e.code except URLError, e: print "Ocurrió un error" print e.reason
Al trabajar con urllib2
nos podemos encontrar, como vemos, con errores de tipo URLError
. Si trabajamos con HTTP podemos encontrarnos también con errores de la subclase de URLError
HTTPError
, que se lanzan cuando el servidor devuelve un código de error HTTP, como el error 404 cuando no se encuentra el recurso. También podríamos encontrarnos con errores lanzados por la librería que urllib2
utiliza por debajo para las transferencias HTTP: httplib
; o con excepciones lanzadas por el propio módulo socket
.
La función urlopen
cuenta con un parámetro opcional data
con el que poder enviar información a direcciones HTTP (y solo HTTP) usando POST (los parámetros se envían en la propia petición), por ejemplo para responder a un formulario. Este parámetro es una cadena codificada adecuadamente, siguiendo el formato utilizado en las URLs:
‘password=contrase%A4a&usuario=manuel’
Lo más sencillo para codificar la cadena es utilizar el método urlencode
de urllib
, que acepta un diccionario o una lista de tuplas (clave, valor)
y genera la cadena codificada correspondiente:
- import urllib, urllib2
- params = urllib.urlencode({«usuario»: «manuel», «password»: «contraseña»})
- f = urllib2.urlopen(«http://ejemplo.com/login«, params)
import urllib, urllib2 params = urllib.urlencode({"usuario": "manuel", "password": "contraseña"}) f = urllib2.urlopen("http://ejemplo.com/login", params)
Si lo único que queremos hacer es descargar el contenido de una URL a un archivo local, podemos utilizar la función urlretrieve
de urllib
en lugar de leer de un objeto creado con urlopen
y escribir los datos leídos.
La función urlretrieve
toma como parámetros la URL a descargar y, opcionalmente, un parámetro filename
con la ruta local en la que guardar el archivo, un parámetro data
similar al de urlopen
y un parámetro reporthook
con una función que utilizar para informar del progreso.
A excepción de las ocasiones en las que se utiliza el parámetro data
las conexiones siempre se realizan utilizando GET (los parámetros se envían en la URL). Para enviar datos usando GET basta con concatenar la cadena resultante de urlencode
con la URL a la que nos vamos a conectar mediante el símbolo “?”.
- params = urllib.urlencode({«usuario»: «manuel», «password»: «contraseña»})
- f = urllib2.urlopen(«http://ejemplo.com/login» + «?» + params)
params = urllib.urlencode({"usuario": "manuel", "password": "contraseña"}) f = urllib2.urlopen("http://ejemplo.com/login" + "?" + params)
En urllib
también se utiliza una función urlopen
para crear nuestros pseudo-archivos, pero a diferencia de la versión de urllib
, la función urlopen
de urllib2
también puede tomar como parámetro un objeto Request
, en lugar de la URL y los datos a enviar.
La clase Request
define objetos que encapsulan toda la información relativa a una petición. A través de este objeto podemos realizar peticiones más complejas, añadiendo nuestras propias cabeceras, como el User-Agent.
El constructor más sencillo para el objeto Request
no toma más que una cadena indicando la URL a la que conectarse, por lo que utilizar este objeto como parámetro de urlopen
sería equivalente a utilizar una cadena con la URL directamente.
Sin embargo el constructor de Request
también tiene como parámetros opcionales una cadena data
para mandar datos por POST, un diccionario headers
con las cabeceras y un par de campos origin_req_host
y unverifiable
, que quedan fuera del propósito de la lección.
Veamos cómo añadir nuestras propias cabeceras utilizando como ejemplo la cabecera User-Agent. El User-Agent es una cabecera que sirve para identificar el navegador y sistema operativo que estamos utilizando para conectarnos a esa URL. Por defecto urllib2
se identifica como “Python-urllib/2.5″; si quisiéramos identificarnos como un Linux corriendo Konqueror por ejemplo, usaríamos un código similar al siguiente:
- ua = «Mozilla/5.0 (compatible; Konqueror/3.5.8; Linux)»
- h = {«User-Agent»: ua}
- r = urllib2.Request(«http://www.python.org«, headers=h)
- f = urllib2.urlopen(r)
- print f.read()
ua = "Mozilla/5.0 (compatible; Konqueror/3.5.8; Linux)" h = {"User-Agent": ua} r = urllib2.Request("http://www.python.org", headers=h) f = urllib2.urlopen(r) print f.read()
Para personalizar la forma en que trabaja urllib2
podemos instalar un grupo de manejadores (handlers) agrupados en un objeto de la clase OpenerDirector
(opener o abridor), que será el que se utilice a partir de ese momento al llamar a urlopen
.
Para construir un opener se utiliza la función build_opener
a la que se le pasa los manejadores que formarán parte del opener. El opener se encargará de encadenar la ejecución de los distintos manejadores en el orden dado. También se puede usar el constructor de OpenerDirector
, y añadir los manejadores usando su método add_handler
.
Para instalar el opener una vez creado se utiliza la función install_opener
, que toma como parámetro el opener a instalar. También se podría, si sólo queremos abrir la URL con ese opener una sola vez, utilizar el método open
del opener.
urllib2
cuenta con handlers que se encargan de manejar los esquemas disponibles (HTTP, HTTPS, FTP), manejar la autenticación, manejar las redirecciones, etc.
Para añadir autenticación tendríamos que instalar un opener que incluyera como manejador HTTPBasicAuthHandler
, ProxyBasicAuthHandler
, HTTPDigestAuthHandler
y/o ProxyDigestAuthHandler
.
Para utilizar autenticación HTTP básica, por ejemplo, usaríamos HTTPBasicAuthHandler
:
- aut_h = urllib2.HTTPBasicAuthHandler()
- aut_h.add_password(«realm», «host», «usuario», «password»)
- opener = urllib2.build_opener(aut_h)
- urllib2.install_opener(opener)
- f = urllib2.urlopen(«http://www.python.org«)
aut_h = urllib2.HTTPBasicAuthHandler() aut_h.add_password("realm", "host", "usuario", "password") opener = urllib2.build_opener(aut_h) urllib2.install_opener(opener) f = urllib2.urlopen("http://www.python.org")
Si quisiéramos especificar un proxy en el código tendríamos que utilizar un opener que contuviera el manejador ProxyHandler
. El manejador por defecto incluye una instacia de ProxyHandler
construido llamando al inicializador sin parámetros, con lo que se lee la lista de proxies a utilizar de la variable de entorno adecuada. Sin embargo también podemos construir un ProxyHandler
pasando como parámetro al inicializador un diccionario cuyas claves son los protocolos y los valores, la URL del proxy a utilizar para dicho protocolo.
- proxy_h = urllib2.ProxyHandler({«http» : «http://miproxy.net:123″})
- opener = urllib2.build_opener(proxy_h)
- urllib2.install_opener(opener)
- f = urllib2.urlopen(«http://www.python.org«)
proxy_h = urllib2.ProxyHandler({"http" : "http://miproxy.net:123"}) opener = urllib2.build_opener(proxy_h) urllib2.install_opener(opener) f = urllib2.urlopen("http://www.python.org")
Para que se guarden las cookies que manda HTTP utilizamos el manejador HTTPCookieProcessor
.
- cookie_h = urllib2.HTTPCookieProcessor()
- opener = urllib2.build_opener(cookie_h)
- urllib2.install_opener(opener)
- f = urllib2.urlopen(«http://www.python.org«)
cookie_h = urllib2.HTTPCookieProcessor() opener = urllib2.build_opener(cookie_h) urllib2.install_opener(opener) f = urllib2.urlopen("http://www.python.org")
Si queremos acceder a estas cookies o poder mandar nuestras propias cookies, podemos pasarle como parámetro al inicializador de HTTPCookieProcessor
un objeto de tipo CookieJar
del módulo cookielib
.
Para leer las cookies mandadas basta crear un objeto iterable a partir del CookieJar
(también podríamos buscar las cabeceras correspondientes, pero este sistema es más claro y sencillo):
- import urllib2, cookielib
- cookie_j = cookielib.CookieJar()
- cookie_h = urllib2.HTTPCookieProcessor(cookie_j)
- opener = urllib2.build_opener(cookie_h)
- opener.open(«http://www.python.org«)
- for num, cookie in enumerate(cookie_j):
- print num, cookie.name
- print cookie.value
import urllib2, cookielib cookie_j = cookielib.CookieJar() cookie_h = urllib2.HTTPCookieProcessor(cookie_j) opener = urllib2.build_opener(cookie_h) opener.open("http://www.python.org") for num, cookie in enumerate(cookie_j): print num, cookie.name print cookie.value print
En el improbable caso de que necesitáramos añadir una cookie antes de realizar la conexión, en lugar de conectarnos para que el sitio la mande, podríamos utilizar el método set_cookie
de CookieJar
, al que le pasamos un objeto de tipo Cookie
. El constructor de Cookie
, no obstante, es bastante complicado.