Para hacer una araña que explore un servidor FTP se puede aprovechar el ftplib de python... solo tiene un impedimento, que cuando se pide la lista de archivos en un directorio se devuelve por stdout, lo que es bastante molesto para algo como esto.
La solución más sencilla es substituir la salida estándar ( sys.stdout ) por un objeto que almacene los datos, el único método que hace falta en el objeto es objeto.write(string) , el que se usa para mostrar strings por la pantalla, esto puede servir:
class catcher():
def clean(self):
self.trap = ""
def readlines(self):
return self.trap.split("\n")
def readline(self):
l = self.readlines()
res = l.pop(0)
self.trap = "\n".join(l).strip()
return res
def __init__(self):
self.clean()
def write(self, s):
self.trap += s
Después de dar el cambiazo por stdout y de que recoja los string, este queda en catcher.trap, además se pueden utilizar los siguientes métodos:
catcher.readlines()
Devuelve lo recojido como un array de strings (una por línea).
catcher.readline()
Devuelve una línea de lo recojido y la elimina.
catcher.clean()
Elimina lo recojido
Después hace falta convertir las líneas en algo útil, esto se puede hacer con:
import sys, re
# Elimina los dobles espacios
def smash(s):
b = ""
while b != s:
b = s
s = s.replace(" ", " ")
return s
# Convierte las líneas de `ls` en algo útil
def parseLs(sa, cdir = None):
d = []
cd = (cdir != None)
for s in sa: # Por cada línea
s = smash(s.strip()) # Se elimina lo que sobra
if (len(s) < 1): # Si aún queda algo
continue
o = {} # Aquí se guardarán los datos
sld = s.split(" ") # Se separan por los espacios
# "Si para solucionar un problema se te ocurre
# 'esto lo puedo hacer con expresiones regulares!'
# ...ya tienes dos problemas" -- No me acuerdo xD
if (re.match(".{10}\ [0-9]{1,9}\ [a-z,A-Z,0-9]{1,50}\ " +
"[a-z,A-Z,0-9]{1,50}\ [0-9]{1,1000}\ [a-z,A-Z,0-9]{3}\ " +
"[0-9]{2}\ [0-9]{2}\:[0-9]{2}\ .{1,255}", s) != None):
# Lee la información
if (len(sld) < 5) or not(":" in s):
print >>sys.stderr, "-|>", s
continue
o['perm' ] = sld[0]
o['num' ] = sld[1]
o['user' ] = sld[2]
o['group'] = sld[3]
o['size' ] = sld[4]
o['mon' ] = sld[5]
o['day' ] = sld[6]
o['hour' ] = sld[7]
if (cd):
o['pwd'] = cdir
n = s.index(":")
# Obtiene el resto y se guarda como el nombre
o['name' ] = s[ (s[n : ].index(" ") + n + 1) : ].strip()
d.append(o) # Se añade a la lista
# Otra opción de formateado
elif(re.match(".{10}\ [0-9]{1,9}\ [a-z,A-Z,0-9]{1,50}\ " +
"[a-z,A-Z,0-9]{1,50}\ [0-9]{1,1000}\ [a-z,A-Z,0-9]{3}\ " +
"[0-9]{2}\ [0-9]{4}\ .{1,255}",s) != None):
# Lo mismo
o['perm' ] = sld[0]
l = len(sld[0])
o['num' ] = sld[1]
l += len(sld[1])
o['user' ] = sld[2]
l += len(sld[2])
o['group'] = sld[3]
l += len(sld[3])
o['size' ] = sld[4]
l += len(sld[4])
o['mon' ] = sld[5]
l += len(sld[5])
o['year' ] = sld[6]
l += len(sld[6])
o['name' ] = s[ s[ 7 + l : ].index(" ") + 8 + l : ]
if (cd):
o['pwd'] = cdir
d.append(o) # Se añade a la lista
# Si falla
else:
print >>sys.stderr, "--->", s, "<---"
raw_input("")
return d
Solo hay que alimentar la función parseLs con el array de líneas (desde catcher.readlines() por ejemplo) y opcionalmente con el directorio actual (solo se añade como parámetro) y devuelve un array de diccionarios con al menos las entradas 'perm' (permisos), 'num', 'user', 'group', 'size' y 'mon' (mes).
Nota: parseLs() funciona con los formateados que me he encontrado, que seguramente no sean todos.
A partir de ahí el resto es parecido a una araña normal, por ejemplo [ ftp_crawler.py ], si quieres guardar los datos en un PostgreSQL, hay que descomentar las líneas 8, 41 (aquí esta la query, el nombre de la tabla es nu, cambiala por la que quieras) a 45 y 124 (en esta se define el nombre de la base de datos).
La tabla usa estas columnas: 'permisos', 'numero', 'usuario', 'grupo', 'tamanho', 'path' (varchar y pista)
Nos vemos
[Referencia]
http://docs.python.org/library/ftplib.html
No hay comentarios:
Publicar un comentario