Pregunta newbie sobre sockets y urllib2

Gabriel Genellina gagsl-py2 en yahoo.com.ar
Jue Dic 13 19:01:05 CET 2007


En Thu, 13 Dec 2007 12:17:31 -0300, Gustavo Ces <g.ces en pettra.es> escribi�:

> Saludos,
>
>     Gracias por la paciencia. Y tomo nota de tu recomendación. La verdad  
> es
> que he estado leyendo algo ( no lo suficiente, como puedes comprobar ),  
> pero
> sólo quería saber cúal sería la forma más correcta. Pero, aún pudiendo
> equivocarme por ser un poco novato y quedarme en deuda la lectura de  
> toda la
> información disponible, no había encontrado en la referencia de python  
> sobre
> Try si el hecho de probar y que no salte una excepción es true y la
> excepción es false, con lo quedaría claro que tu código sería la opción  
> más
> sencilla y evidente. En la Guia de Aprendizaje hay un pequeño ejemplo en  
> el
> que lo usa, en el apartado de Gestion de Excepciones, pero no está muy  
> claro
> si lo que es True es el hecho de asignar un valor a una variable ( y por  
> lo
> tanto, ser verdadera) o al hecho de haber salido exitoso el código  
> dentro de
> try ( si el ejemplo tuviese varias sentencias en el try quedaría mucho  
> más
> claro).

En el tutorial, el manejo de excepciones está explicado en la seccion 8.2
No tiene nada que ver con verdadero/falso. Una excepción se genera cuando,  
durante la ejecución del programa, se da una situación que impide que se  
siga ejecutando. Si nunca es atrapada, se terminará mostrando por consola:

py> x = 1
py> y = 0
py> z = x/y
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

Para atrapar una excepción, usamos try/except:

py> try:
...   z = x/y
...   print "cociente=",z
... except ZeroDivisionError:
...   print "operación imposible"
...
operación imposible

Como la división no se pudo realizar, el flujo normal del programa se  
interrumpe. La sentencia print siguiente no se ejecuta, y el control pasa  
al primer bloque except que tiene una declaración compatible con la  
excepción que fue generada. En este caso, es el único bloque que hay, y se  
imprime "operación imposible".

Volviendo a tu problema: queremos repetir los intentos de conexion a un  
sitio, una y otra vez, hasta que logre conectarse, ok? Qué significa "que  
logre conectarse" en términos pythonescos? Que NO ocurra una excepción del  
tipo IOError. Si ocurre la excepción, hay que seguir intentando. Si no  
ocurre, ya está, nos logramos conectar. Está claro que todo esto hay que  
hacerlo dentro de un bucle, no? Entonces sería algo así:

while True:
   try:
     arch = urllib2.urlopen(direccion)
     break
   except IOError:
     pass

while True: es la forma de decir "repetir una y otra vez hasta que se  
acabe el mundo". Dentro del bucle, intentamos conectarnos usando urlopen.  
Si todo anduvo bien, se va a ejecutar la linea siguiente, break. break  
indica "salir del bucle", y es justamente la forma de romper ese bucle  
infinito. En cambio, si hubo un IOError, el break no se va a ejecutar,  
sino que el control va a saltar a la linea pass, que no hace nada. Pero  
seguimos dentro del bucle, asi que vuelta a empezar con el urlopen.
Aca va una version mejorada:

def urlopen_retry(direccion, reintentos=10, espera=30):
   i = 0
   while True:
     try:
       arch = urllib2.urlopen(direccion)
       break
     except IOError:
       i += 1
       if i>reintentos: raise
       time.sleep(espera)
   return arch


Ahora tiene un cierto numero maximo de reintentos, y un tiempo de espera  
entre cada reintento.
Lo unico que cambio es la clausula except: si hubo un error, incrementamos  
el numero de reintentos. Si fueron demasiados ya, re-lanzamos la misma  
excepcion (el raise vacío). Es decir, si despues de todos los reintentos  
aún sigue fallando, la excepción le va a llegar a quien haya llamado la  
función. Si simplemente devolviéramos None o algo asi, estaríamos  
descartando mucha información contenida en la excepción que puede ser  
importante para el llamador.

Y ahora solo nos queda el bucle externo, iterando sobre todas las  
direcciones:

for direccion in lista:
   try:
     arch = urlopen_retry(direccion)
   except:
     print "%s: %s al procesar %s" % (sys.exc_info()[0],
           sys.exc_info()[1], direccion)
     continue
   try:
     pagina = arch.read()
   finally:
     arch.close()
   html=BeautifulSoup(pagina)

Espero que ahora te quede un poco mas claro.

-- 
Gabriel Genellina

------------ próxima parte ------------
_______________________________________________
Lista de correo Python-es 
http://listas.aditel.org/listinfo/python-es
FAQ: http://listas.aditel.org/faqpyes


Más información sobre la lista de distribución Python-es