Está en la página 1de 5

Python y la Web

ENREDADOS
D
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 for-
mulario 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 estu-
viese lista hacer algo(magia vud?) y
que se cargasen solos en la dichosa
DESARROLLO Python
54
Nmero 29
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
web. Y por supuesto, sin que se ente-
rase 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 traba-
jaba. Haba que rellenar un formulario
web para dar parte de unas mismas.
Esa tarea, debido al volumen de las
ventas, requera que una persona per-
diese toda una maana simplemente
porque a nadie en la otra empresa se
le ocurri la idea de hacer el proceso
ms rpido.
WWW. L I NUX- MAGAZI NE. ES
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 Mecha-
nize 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 com-
pleta no est an liberada, y es reco-
mendable 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 des-
cargamos el cdigo fuente con:
054-058_PythonLM29 12/6/07 6:02 pm Pgina 54
> svn co http://U
codespeak.net/svn/wwwsearch/U
mechanize/trunk mechanize
John emplea la librera setuptools de
Python para compilar e instalar la libre-
ra, as que deberamos proceder a insta-
larla antes de continuar. Despus slo
debemos ejecutar:
> python setup.py build
> sudo python setup.py install
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, conse-
guir 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
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 inter-
faz de usuario sern llamadas a mto-
dos del objeto Browser.
Un problema que nos podemos encon-
trar, 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:
>>> br.set_proxies(U
{http : 192.168.1.254:8000,U
ftp : 192.168.1.254:8000})
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/)
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 com-
pleta. 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
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=)>>
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 comuni-
cacin con la pgina. Muy bien, nosotros
queremos acceder al segundo puesto que
Python DESARROLLO
55
Nmero 29 WWW. L I NUX- MAGAZI NE. ES
01 import re
02 import mechanize
03
04 br = mechanize.Browser()
05
06 br.set_handle_robots(False)
07
08 respuesta =
br.open(http://www.linuxmagaz
ine.es/)
09
10 br.select_form(nr=1)
11
12 br[words]=python
13
14 br.submit()
15
16 # Estamos en la pgina de
resultados
17
18 #primer resultado
19 urls = [url.absolute_url for
url in
br.links(url_regex=re.compile(
rpdf$))]
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()
Listado 1: Nuestra araa web
054-058_PythonLM29 12/6/07 6:03 pm Pgina 55
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 almace-
nado 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
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 formu-
lario relleno, ahora debemos pulsar
el botn. Pero cmo? Pues con el
mtodo submit:
>>> respuesta = br.sumbit()
Lo que acabamos de hacer es ms com-
plejo de lo que parece, qu ha ocu-
rrido? Al ejecutar submit hemos
enviado los datos del formulario al ser-
vidor, que nos habr respondido rediri-
gindonos a la pgina con los resulta-
dos. El contenido de esta interaccin se
almacena en respuesta, que no es ni
ms ni menos que otro objeto que
envuelve un documento HTML. Debe-
mos 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 for-
mato tendr el enlace que deseamos,
podremos conseguir la informacin que
contiene. Pero antes veamos qu debe-
ramos 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
es el que emplea la url http://www.linux-
magazine.es/search. Este formulario
tiene un campo de texto llamado words.
Al principio cuesta un poco entender
este lenguaje pero con un poco de prc-
tica no es tan complicado.
De acuerdo, tenemos que acceder al
segundo formulario, as que le indica-
mos 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 Maga-
zine ha decidido ignorar, al fin y al
cabo no es obligatorio.
Si se diese el caso de que el formula-
rio tuviese un nombre podramos selec-
cionarlo con:
>>> br.select_formU
(name = miformulario)
Pero como no es este nuestro caso, lo
seleccionaremos por posicin:
>>> br.select_form(nr = 1)
Empleamos el nmero 1, porque los
formularios estn numerados comen-
DESARROLLO Python
56
Nmero 29 WWW. L I NUX- MAGAZI NE. ES
Figura 1: Pgina de resultados de bsqueda de Linux Magazine.
054-058_PythonLM29 12/6/07 6:03 pm Pgina 56
054-058_PythonLM29 12/6/07 6:03 pm Pgina 57
DESARROLLO Python
58
Nmero 29 WWW. L I NUX- MAGAZI NE. ES
extraer las llaves obtenemos la misma
lista pero eliminando los duplicados.
Complicado? S, como lneas ante-
riores, pero indudablemente til y
requiere muy pocas pulsaciones del
teclado.
De acuerdo, ya tenemos los enla-
ces y qu hacemos con ellos
ahora? Pues podramos descargarlos:
recorriendo la lista y usando la fun-
cin br.open() para cargarlos, y res-
puesta.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 nom-
bre. Puedes ver el cdigo completo
en el Listado [1].
Pero, con este cdigo slo pode-
mos conseguir los primeros resulta-
dos 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 expre-
sin regular.
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 implementacio-
nes de Python ya hace algunos ai-
tos. Hemos podido comprobar cmo
podemos usar una pgina web como
si estuvisemos delante de un nave-
gador mediante la magnfica, y an
en estado Beta, librera mechanize y
empleando un nmero de lneas de
cdigo realmente minsculo. La pr-
xima 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 ser-
piente. I
...
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 prime-
ros. Si pruebas esto mismo en tu
equipo vers que hay un nmero res-
petable 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 inU
br.links(url_regex=re.compileU
(rpdf$))]
Python comprime en poco cdigo
mucho trabajo, as que esta nica
lnea requiere una explicacin.
Comencemos por la lista de compre-
sin. 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$))
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 ante-
riormente, 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.com-
pile() devuelve un objeto que reco-
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 compre-
sin se compone de cada uno de esos
enlaces, con nombre url, de lo que
nos quedamos con su atributo abso-
lute_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 deshacer-
nos de ellos en una lnea:
>>> urls = dict(U
zip(urls,urls)).keys()
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)]
Esto puede resultar muy conveniente,
porque precisamente una lista con
tuplas de 2 valores es lo que necesita-
mos 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 crema-
llera, y teniendo en cuenta que en un
diccionario no pueden existir dos lla-
ves iguales, el resultado es que al
[1] http://search.cpan.org/dist/
WWW-Mechanize/
[2] http://wwwsearch.sourceforge.net/
mechanize/
RECURSOS
054-058_PythonLM29 12/6/07 6:03 pm Pgina 58

También podría gustarte