Cliente de FTP en Python

ftplib viene con Python y hace todo lo que hace un cliente de FTP. Antes de empezar, una advertencia que la versión anterior de esta página no hacía: el FTP normal envía la contraseña sin cifrar.

Conectarse

from ftplib import FTP

with FTP() as ftp:
    ftp.connect("ftp.ejemplo.com", 21, timeout=10)
    ftp.login("usuario", "clave")
    print(ftp.getwelcome())
    print("carpeta:", ftp.pwd())
salida220 Servidor FTP de pruebas
carpeta: /

El with cierra la conexión al salir. El timeout no es opcional en la práctica: sin él, si el servidor no responde, el programa se queda esperando indefinidamente. Los ejemplos de esta página se probaron contra un servidor FTP de pruebas montado para la ocasión, y las salidas son las suyas.

Carpetas

Para…Usa
Ver dónde estásftp.pwd()
Entrar en una carpetaftp.cwd("informes")
Subir un nivelftp.cwd("..")
Crear una carpetaftp.mkd("informes")
Borrar una carpeta vacíaftp.rmd("informes")
Listar nombresftp.nlst()

Subir y bajar

with open("subir.txt", "rb") as f:
    print(ftp.storbinary("STOR subir.txt", f))

with open("bajado.txt", "wb") as f:
    ftp.retrbinary("RETR subir.txt", f.write)
salida226 Transfer complete.

Fíjate en los modos: el archivo local se abre en binario, "rb" para subir y "wb" para bajar, y se usan storbinary y retrbinary. Existen las versiones de texto —storlines y retrlines—, pero cambian los saltos de línea por el camino y pueden estropear un archivo que no sea texto puro. Con binario el archivo llega idéntico, sea lo que sea.

Listar con detalle: mlsd

for nombre, datos in ftp.mlsd():
    if datos["type"] == "file":
        print(f"  {nombre}: {datos['size']} bytes")
salidasubir.txt: 13 bytes

mlsd devuelve cada entrada con sus datos ya separados —tipo, tamaño, fecha—, en lugar de una línea de texto con un formato que cambia de un servidor a otro. Los servidores muy antiguos no lo soportan; en ese caso queda ftp.dir(), y toca interpretar el texto.

Renombrar, borrar y errores

ftp.rename("subir.txt", "renombrado.txt")
ftp.delete("renombrado.txt")
ftp.cwd("no_existe")
salidaerror_perm: 550 No such file or directory.

Los errores del servidor llegan como ftplib.error_perm y empiezan por el código de FTP: 550 es «no existe o no tienes permiso», 530 es «usuario o contraseña incorrectos».

La parte importante: cifrar

from ftplib import FTP_TLS

with FTP_TLS("ftp.ejemplo.com", timeout=10) as ftp:
    ftp.login("usuario", "clave")
    ftp.prot_p()            # cifra también los datos, no solo la contraseña
Con FTP a secas, el usuario, la contraseña y los archivos viajan en claro, y cualquiera en el camino —una wifi pública, un equipo intermedio— puede leerlos. Si el servidor lo admite, usa FTP_TLS, que viene en la misma biblioteca, y no olvides prot_p(). Si lo que tienes es acceso por SSH, mejor todavía SFTP, que es otro protocolo distinto y se usa con el paquete paramiko.
Y no escribas la contraseña en el código, como hacían los ejemplos anteriores: léela de una variable de entorno.

Más información en inglés: FTP client in Python.

Todo el código se ejecutó con Python 3.14 en un contenedor limpio antes de publicar esta página; las salidas están copiadas de esa ejecución.