Está en la página 1de 5

DESARROLLO Python

Python y la Web

ENREDADOS
Podemos automatizar comandos y programas grficos, por qu no automatizar la interaccin con pginas web? En este artculo crearemos un pequeo script que puede ahorrarnos mucho trabajo con el ratn. POR JOSE MARA RUIZ

igamos que llegas un da por la maana a la oficina. El jefe se acerca y te pide que vuelvas a pasar, otra vez!, un montn de informacin a otra empresa a travs del peor interfaz jams diseado: una web. Carga la pgina, introduce tus datos de acceso, pincha aqu, pincha all. Cuando ests en la pgina con el formulario en cuestin debes introducir los datos y pinchar en un enlace o botn para enviarlos. Una y otra vez, una y otra vez. Quiz durante horas. Acaso no hay una mejor manera de hacer esto? Lo ideal sera poder usar tu hoja de clculo preferida, rellenar los campos en ella de forma rpida (aquellos que se repitan pueden ser copiados y pegados) y cuando estuviese lista hacer algo(magia vud?) y que se cargasen solos en la dichosa

web. Y por supuesto, sin que se enterase el jefe, as tendras ms tiempo para leer artculos como ste ;). Pues s, existe una manera de hacer exactamente lo que acabas de leer! Y vamos a explicarlo en este captulo. As podrs decirle a tu jefe que esta revista har a la empresa mucho ms productiva.

Mechanize
No es la primera vez que hago esto. Hace algunos aos tuve este mismo problema en la oficina en la que trabajaba. Haba que rellenar un formulario web para dar parte de unas mismas. Esa tarea, debido al volumen de las ventas, requera que una persona perdiese toda una maana simplemente porque a nadie en la otra empresa se le ocurri la idea de hacer el proceso ms rpido.

As que ni corto ni perezoso mi jefe cre un script en Perl que haca este trabajo a partir de un fichero de texto CSV. El problema es que lo hizo en Perl y esta seccin va sobre Python. Hemos de mudarnos todos a Perl para poder disfrutar de este tipo de ventajas? No, gracias a que John J. Lee decidi portar la librera Mechanize que Andy Lester cre en Perl (ver Recurso [1]) a Python (ver Recurso [2]). Las distribuciones de Linux suelen permitir instalarla como paquete. El problema es que la versin ms completa no est an liberada, y es recomendable emplear la que se encuentra en el servidor de Subversion de John. El proceso es simple, instalamos el cliente de Subversion que ms nos guste, aqu usar el estndar, y descargamos el cdigo fuente con:

54

Nmero 29

WWW.LINUX- MAGAZINE.ES

Python DESARROLLO

> svn co http://U codespeak.net/svn/wwwsearch/U mechanize/trunk mechanize

>>> br.set_proxies(U {http : 192.168.1.254:8000,U ftp : 192.168.1.254:8000})

muy sencilla. Digamos que queremos saber qu formularios contiene la pgina:


>>> for form in br.forms(): ... print form ... <POST http://U www.linux-magazine.es/Readers/U Newsletter/reply application/U x-www-form-urlencoded <HiddenControlU (subject=subscribe)U (readonly)> <TextControl(email=Tu email)> <SubmitControl(<None>=OK)U (readonly)>> <POST http://U www.linux-magazine.es/U search application/U x-www-form-urlencoded <TextControl(words=)>>

John emplea la librera setuptools de Python para compilar e instalar la librera, as que deberamos proceder a instalarla antes de continuar. Despus slo debemos ejecutar:
> python setup.py build > sudo python setup.py install

Aqu he especificado un proxy para http y otro para ftp, que suele ser lo normal. Ya tenemos nuestro navegador listo. Slo tenemos que abrir una pgina:
>>>> respuesta= br.openU (http://www.linuxmagazine.es/)

Y listo. Ya tenemos nuestra librera mechanize lista para trabajar.

Juguemos con una Web


Comencemos con algo simple. Vamos a conectar con la web de Linux Magazine y a pedirle que busque la palabra python para, a continuacin, conseguir una lista de las urls de los primeros artculos que contengan esa palabra (sern enlaces a ficheros PDF), ver Figura [1]. De esta forma veremos cmo se trabaja con mechanize, ya que tiene una forma peculiar de tratar el cdigo HTML. Para ello debemos comenzar por arrancar python e importar la librera mechanize y re (expresiones regulares):
>>> import re >>> import mechanize

Una vez abierta se nos devuelve un objeto de respuesta. Este objeto contiene todos los mtodos necesarios para poder trabajar con la informacin devuelta por el servidor web. Por ejemplo, podramos imprimir el contenido HTML de la pgina:
>>>> printU respuesta.read() <html>....

No pongo aqu la informacin devuelta porque podra ocupar una pgina completa. Adems es de poca utilidad. Lo interesante de mechanize es que genera la pgina y nos permite acceder a las partes jugosas de la misma de forma

Mechanize tiene su propio lenguaje para representar partes de la pgina y aqu podemos ver un ejemplo del mismo. Nos dice que hay dos formularios, que emplean POST como mtodo de comunicacin con la pgina. Muy bien, nosotros queremos acceder al segundo puesto que

Listado 1: Nuestra araa web


01 02 03 04 05 06 07 08 import re import mechanize br = mechanize.Browser() br.set_handle_robots(False) respuesta = br.open(http://www.linuxmagaz ine.es/) br.select_form(nr=1) br[words]=python br.submit() # Estamos en la pgina de resultados 20 21 #eliminamos duplicados 22 23 urls = dict(zip(urls,urls)).keys() 24 25 26 r = re.compile(.*/(\d+)/(.*)$) 27 28 29 for url in urls: 30 31 m= r.match(url) 32 nombre = m.group(1)+-+m.group(2) 33 print nombre 34 35 respuesta = br.open(url) 36 datos = respuesta.read() 37 38 fichero = open (nombre,w) 39 fichero.write(datos) 40 fichero.close()

No ocurre nada particularmente vistoso, a no ser que no hallamos instalado correctamente la librera. Muy bien, ahora necesitamos crear un navegador:
>>> br = mechanize.Browser()

Ahora ya lo tenemos en la variable br. No me refiero a un navegador grfico, sino a todo lo que un navegador puede hacer pero sin la parte grfica. Me explico, un navegador posee un motor que interacta con los servidores web y un interfaz grfico que interacta con el usuario. La librera mechanize nos da lo primero sin lo segundo. Nuestro interfaz de usuario sern llamadas a mtodos del objeto Browser. Un problema que nos podemos encontrar, ya desde el principio, es que nuestra red disponga de un proxy para acceder a Internet. Este caso es bastante comn, as que debemos indicrselo al objeto almacenado en br:

09 10 11 12 13 14 15 16

17 18 #primer resultado 19 urls = [url.absolute_url for url in br.links(url_regex=re.compile( rpdf$))]

WWW.LINUX- MAGAZINE.ES

Nmero 29

55

DESARROLLO Python

Figura 1: Pgina de resultados de bsqueda de Linux Magazine.

es el que emplea la url http://www.linuxmagazine.es/search. Este formulario tiene un campo de texto llamado words. Al principio cuesta un poco entender este lenguaje pero con un poco de prctica no es tan complicado. De acuerdo, tenemos que acceder al segundo formulario, as que le indicamos a br, nuestro navegador virtual, que emplee este formulario. Es posible realizar la seleccin por posicin o por nombre. El nombre vendra indicado por el parmetro HTML name, que el desarrollador de la web de Linux Magazine ha decidido ignorar, al fin y al cabo no es obligatorio. Si se diese el caso de que el formulario tuviese un nombre podramos seleccionarlo con:
>>> br.select_formU (name = miformulario)

zando en 0. Con este mtodo ya tenemos seleccionado el formulario, pero ste se compone a su vez de varios elementos incrustados. Cmo podemos seleccionarlos? Por suerte para nosotros, John, el desarrollador, ha empleado toda la potencia de Python y ha realizado un truco de magia: hacer que el objeto almacenado en br se comporte como un tipo diccionario Python. Si el elemento input donde hay que escribir las palabras a buscar se llama words, entonces todo lo que tenemos que hacer es:
>>> br[words]=python

Pero como no es este nuestro caso, lo seleccionaremos por posicin:


>>> br.select_form(nr = 1)

As de simple! Es o no maravilloso este mdulo? Esto s que es cdigo compacto en estado puro. Despus de la euforia debemos volver al asunto que nos ha trado hasta aqu: queremos los enlaces con la palabra python. Ya tenemos nuestro formulario relleno, ahora debemos pulsar el botn. Pero cmo? Pues con el mtodo submit:
>>> respuesta = br.sumbit()

Lo que acabamos de hacer es ms complejo de lo que parece, qu ha ocurrido? Al ejecutar submit hemos enviado los datos del formulario al servidor, que nos habr respondido redirigindonos a la pgina con los resultados. El contenido de esta interaccin se almacena en respuesta, que no es ni ms ni menos que otro objeto que envuelve un documento HTML. Debemos recoger todos los enlaces que nos interesan. Y aqu viene otro punto fuerte de mechanize: su integracin con las expresiones regulares. Desde luego que John no nos iba a fallar en este aspecto. Podemos elegir un enlace usando una expresin regular, de forma que no tenemos que ir buscando a tontas y a locas. Con saber ms o menos qu formato tendr el enlace que deseamos, podremos conseguir la informacin que contiene. Pero antes veamos qu deberamos hacer si no supiramos muy bien qu buscamos. Al igual que con los formularios, los enlaces se pueden recorrer como si fuesen una lista:
>>> for link in br.links(): ... print link

Empleamos el nmero 1, porque los formularios estn numerados comen-

56

Nmero 29

WWW.LINUX- MAGAZINE.ES

DESARROLLO Python

... Link(base_url=http:// U www.linux-magazine.es/,U url=/,text=logoOL.gif[IMG],U tag=a, attrs=[(href, /)]) Link(base_url=http://U www.linux-magazine.es/,U url=/,text=logoOR.gif[IMG],U tag=a, attrs=[(href, /)]) ....

Aqu slo se muestran los dos primeros. Si pruebas esto mismo en tu equipo vers que hay un nmero respetable de enlaces en esta pgina en concreto. De nuevo mechanize nos muestra lo que entiende por un enlace. Pero como nosotros sabemos lo que queremos, podemos pasar directamente a la accin con las expresiones regulares:
>>> urls =U [url.absolute_url for url in U br.links(url_regex=re.compile U (rpdf$))]

Python comprime en poco cdigo mucho trabajo, as que esta nica lnea requiere una explicacin. Comencemos por la lista de compresin. En Python es posible crear listas a partir de definiciones de lo que se supone que va en las mismas. En este caso hay que comenzar por el cdigo:
br.links(url_regex= U re.compile(rpdf$))

noce la expresin regular, lo que podramos llamar una expresin regular compilada, y lo almacenamos en el argumento con nombre url_regex. La funcin br.links() lo reconoce y sabe que debe buscar enlaces que al reconocerlos con la expresin regular devuelvan True. br.links() generar as una lista de enlaces, y aqu entra en funcin la clusula for ... in ..., que no hace otra cosa que recorrer la lista y devolver los enlaces bajo el nombre de variable url. La lista de compresin se compone de cada uno de esos enlaces, con nombre url, de lo que nos quedamos con su atributo absolute_url: su ruta completa. Y con esto acabamos. Todo se reduce a una sola lnea de Python, escribimos poco pero vale por decenas de lneas! An tenemos un problema, la web de resultado de bsqueda de Linux Magazine devuelve los resultados duplicados. Aplicando un poco de Kung Fu Python podemos deshacernos de ellos en una lnea:
>>> urls = dict( U zip(urls,urls)).keys()

extraer las llaves obtenemos la misma lista pero eliminando los duplicados. Complicado? S, como lneas anteriores, pero indudablemente til y requiere muy pocas pulsaciones del teclado. De acuerdo, ya tenemos los enlaces y qu hacemos con ellos ahora? Pues podramos descargarlos: recorriendo la lista y usando la funcin br.open() para cargarlos, y respuesta.read() para leer el contenido del fichero, guardndolo en un fichero con nombre igual a la ltima parte de la URL. Por desgracia, muchos de ellos se llaman Python.pdf, as que vamos a usar el nmero de esa revista en el nombre. Puedes ver el cdigo completo en el Listado [1]. Pero, con este cdigo slo podemos conseguir los primeros resultados no? S, para mejorarlo y que descargue todos los resultados slo tendramos que localizar el enlace a siguiente resultado y pulsar en l con el mtodo br.follow_link(). Dejo al lector que piense cmo hacerlo, dando una sola pista: emplea un bucle hasta que no encuentres links que se correspondan con la expresin regular.

Zip significa cremallera en ingls, y eso es precisamente lo que hace la funcin zip(). Cierra dos listas como si fuese una cremallera:
>>> zip([uno,dos, U tres],[1,2,3]) [(uno,1),(dos,2), U (tres,3)]

Conclusin
No es de extraar que Google emplee Python. De hecho, el propio Guido Van Rossum cre una araa web con una de las primeras implementaciones de Python ya hace algunos aitos. Hemos podido comprobar cmo podemos usar una pgina web como si estuvisemos delante de un navegador mediante la magnfica, y an en estado Beta, librera mechanize y empleando un nmero de lneas de cdigo realmente minsculo. La prxima vez que el lector se enfrente a un trabajo tedioso con una pgina web, puede que tenga un par de ideas para hacer que el ordenador trabaje por l gracias a cierta serI piente.

Este cdigo localiza todos aquellos enlaces que se puedan identificar con el argumento que pasemos al mtodo br.links(). Es posible usar un nmero, como hicimos con el formulario anteriormente, pero en lugar de eso vamos a emplear una expresin regular. As que usamos la funcin re.compile(), que no hace otra cosa que generar un objeto que contiene un reconocedor de la expresin regular que pasamos como parmetro. En nuestro caso es pdf$, (la r de delante le indica a la funcin que la cadena se corresponde con una expresin regular), que no hace otra cosa que localizar cadenas acabadas en la letras pdf, y para ello podemos el smbolo $ al final de pdf. Como decamos, re.compile() devuelve un objeto que reco-

Esto puede resultar muy conveniente, porque precisamente una lista con tuplas de 2 valores es lo que necesitamos para crear un diccionario.
>>> dict(zip([uno,dos, U tres],[1,2,3])) {dos: 2, tres: 3, U uno: 1}

Y del diccionario podemos obtener las llaves usando el mtodo keys(). Si hacemos todo esto con un lista, cerrndola con ella misma en cremallera, y teniendo en cuenta que en un diccionario no pueden existir dos llaves iguales, el resultado es que al

RECURSOS
[1] http://search.cpan.org/dist/ WWW-Mechanize/ [2] http://wwwsearch.sourceforge.net/ mechanize/

58

Nmero 29

WWW.LINUX- MAGAZINE.ES

También podría gustarte