常见错误是未正确处理被动模式、账号密码格式错误、未用二进制模式下载xml导致乱码或解析失败,以及路径错误或命令不支持等问题。

用 ftplib 下载 XML 文件时连不上 FTP 服务器
常见错误是没处理被动模式(PASV)或账号密码传错格式。很多 FTP 服务默认要求 PASV,而 ftplib.FTP 初始化后默认开启 PASV,但有些防火墙或 NAT 环境下它会失败;此时得手动关掉:ftp.set_pasv(False)。另外,login() 的参数顺序是 user 在前、passwd 在后,空密码不能传 None,得传空字符串 ''。
- 检查 FTP 地址是否带
ftp://前缀——ftplib不认这个,只接受纯主机名或 IP - 端口不填默认是 21,如果服务跑在非标端口(比如 2121),得显式传
FTP(host, user, passwd, port=2121) - 遇到
530 Login incorrect,先确认账号密码大小写、是否需要域名前缀(如domainuser)
下载的 XML 文件内容乱码或不完整
根本原因是没用二进制模式传输。XML 虽然是文本,但 FTP 协议里必须明确指定传输模式:RETR 前必须调用 ftp.cwd() 切到目录后,用 ftp.retrbinary() 而不是 ftp.retrlines()。后者会按行转换换行符,破坏 XML 声明和编码声明(比如把 <?xml version="1.0" encoding="UTF-8"?> 里的 rn 搞错,导致解析失败)。
-
retrbinary()的回调函数里直接写入文件,别用decode()——文件对象要以'wb'模式打开 - 如果 XML 本身含中文且声明为
encoding="GBK",下载完再用对应编码读取,不要在 FTP 层做解码 - 下载大文件时加个回调打点进度,避免卡住无感知:
Lambda data: fp.write(data)就够,不用额外计数
脚本跑在 linux 服务器上却提示 ftplib.error_perm: 550 Failed to change Directory
这通常不是权限问题,而是路径写错了。FTP 的根目录不等于服务器系统根目录,ftp.cwd('/') 切的是 FTP 用户的 home 目录(比如 /var/ftp/user/),不是系统 /。而且路径分隔符必须用 '/',不能用 '' 或 os.sep。
- 先用
ftp.pwd()打印当前路径,再用ftp.nlst()看有哪些文件,确认目标 XML 确实在那儿 - 路径中含空格或中文?FTP 协议对这类字符支持差,优先改名或换路径,别硬上
urllib.parse.quote() - 某些 FTP 服务(如 vsftpd)禁用了
SYST和MLSD命令,nlst()可能返回空列表,这时得靠dir()解析文本输出——但不推荐,容易因格式变化崩
想自动下载最新 XML,但 FTP 不支持通配符或时间筛选
FTP 协议本身没有“按修改时间排序”或“匹配 *.xml”的功能。你只能先 nlst() 拿全量文件名,再用 python 自己过滤和排序。注意:返回的文件名不含路径,nlst('data/') 和 nlst() 结果不同,前者只列子目录内容,后者列当前目录。
立即学习“Python免费学习笔记(深入)”;
- 用
sorted(ftp.nlst(), key=lambda f: f.lower())按字母排,适用于命名含日期(如report_20240520.xml) - 如果文件名无规律,只能靠
ftp.sendcmd('MDTM filename.xml')拿修改时间(返回形如213 20240520142312),但不是所有 FTP 服务都支持MDTM - 别在循环里反复
retrbinary()每个文件——先筛出目标文件名,再下,否则网络开销翻倍
事情说清了就结束。FTP 没有状态、不记上下文,每一步都要自己确认当前路径、文件存在性、传输模式,靠猜不如多打一行 print(ftp.pwd())。