我无法弄清楚如何使用ftplib查看FTP站点的文件内容。
我可以使用WinSCP连接到FTP站点,并查看根目录中的6个文件。
在Python 3.4中,我使用以下代码:
from ftplib import FTP_TLS
ftps = FTP_TLS(timeout=100)
ftps.connect(ipAddress, 21)
ftps.auth()
ftps.prot_p()
ftps.login('username', 'password')
产生:
Out[72]: '230 User logged in.'
我可以运行它:
ftps.pwd()
......我看到我在根目录中:
Out[73]: '/'
一切似乎都很好吃。但是,当我试图查看目录中的内容时,使用ftps.dir()或ftps.retrlines('NLST'),或者我尝试过的任何其他内容,我都会超时:
TimeoutError: [WinError 10060] A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond
我搜索过,发现有几个人说这可能是“被动与主动”连接设置问题。但是,我询问了FTP的管理员,如果它是被动的还是主动的,他表示不相信,只是说,“它不是SFTP。它是明确的FTP超过TLS !!”
我对这一切都一无所知。我只是想使用ftplib(或任何其他包!)连接到他的FTP并下载一些文件。
我错过了什么?
编辑: 我只是想通了WinSCP正在使用被动模式,所以这似乎是有效的。据我了解,默认情况下ftplib是被动的,但我继续将标志设置为true。这没什么,我仍然有同样的问题。
我还检查了我的防火墙(抓住吸管),它完全关闭了,所以那里应该没有问题。
我还尝试使用'.sendcmd()'方法发送显式命令,该方法似乎能够发送命令,但不会获取任何响应(或者它无法看到响应)。
我有什么理由可以看到我所在的目录,但没有其他任何数据?我显然已经连接并与服务器通信了,看起来这个目录可用并且对我来说是可见的,这意味着数据可以来回传递。数据是否可能以某种方式返回ftplib无法识别?有关我应该从哪里开始进行故障排除的任何想法?
最后,似乎所有内容都设置正确,因为我可以在WinSCP中看到一切正常。我应该检查哪些设置与ftplib进行比较?
编辑2:
根据要求,我设置了调试级别(感谢这个提示),下面是输出:
ftps = FTP_TLS(timeout=15)
ftps.set_debuglevel(2)
ftps.connect(ipAddress, 21)
ftps.set_pasv(True)
ftps.auth()
ftps.prot_p()
ftps.login('username', 'password')
ftps.retrlines('NLST')
*get* '220 Microsoft FTP Service\n'
*resp* '220 Microsoft FTP Service'
*cmd* 'AUTH TLS'
*put* 'AUTH TLS\r\n'
*get* '234 AUTH command ok. Expecting TLS Negotiation.\n'
*resp* '234 AUTH command ok. Expecting TLS Negotiation.'
*cmd* 'PBSZ 0'
*put* 'PBSZ 0\r\n'
*get* '200 PBSZ command successful.\n'
*resp* '200 PBSZ command successful.'
*cmd* 'PROT P'
*put* 'PROT P\r\n'
*get* '200 PROT command successful.\n'
*resp* '200 PROT command successful.'
*cmd* 'USER username'
*put* 'USER username\r\n'
*get* '331 Password required for username.\n'
*resp* '331 Password required for username.'
*cmd* 'PASS ****************'
*put* 'PASS ****************\r\n'
*get* '230 User logged in.\n'
*resp* '230 User logged in.'
*cmd* 'TYPE A'
*put* 'TYPE A\r\n'
*get* '200 Type set to A.\n'
*resp* '200 Type set to A.'
*cmd* 'PASV'
*put* 'PASV\r\n'
*get* '227 Entering Passive Mode (10,19,1,137,195,128).\n'
*resp* '227 Entering Passive Mode (10,19,1,137,195,128).'
Traceback (most recent call last):
File "<ipython-input-13-a79eb3c23dc5>", line 8, in <module>
ftps.retrlines('NLST')
File "C:\Anaconda3\lib\ftplib.py", line 467, in retrlines
with self.transfercmd(cmd) as conn,
File "C:\Anaconda3\lib\ftplib.py", line 398, in transfercmd
return self.ntransfercmd(cmd, rest)[0]
File "C:\Anaconda3\lib\ftplib.py", line 793, in ntransfercmd
conn, size = FTP.ntransfercmd(self, cmd, rest)
File "C:\Anaconda3\lib\ftplib.py", line 360, in ntransfercmd
source_address=self.source_address)
File "C:\Anaconda3\lib\socket.py", line 516, in create_connection
raise err
File "C:\Anaconda3\lib\socket.py", line 507, in create_connection
sock.connect(sa)
timeout: timed out
编辑3:
我试图将被动标志设置为false,但是当我要求文件时,我会得到以下内容:
ftps.retrlines('NLST')
*cmd* 'TYPE A'
*put* 'TYPE A\r\n'
*get* '200 Type set to A.\n'
*resp* '200 Type set to A.'
*cmd* 'PORT 10,1,10,100,223,39'
*put* 'PORT 10,1,10,100,223,39\r\n'
*get* '501 Server cannot accept argument.\n'
*resp* '501 Server cannot accept argument.'
编辑4:
我在WinSCP上启用了登录,看起来有用的Steffen Ullrich是对的!这是在WinSCP的日志中:
< 2017-05-19 08:44:27.880 227 Entering Passive Mode (10,19,1,137,195,139).
< 2017-05-19 08:44:27.880 Server sent passive reply with unroutable address 10.19.1.137, using host address instead.
那么,我如何让ftplib做同样的事情?
编辑5:
找到THIS - 这只是重写源以使其看起来像主机。其他人有更简单,更少侵入性/可能破坏性的方法吗?
作为第二种选择,因为管理员似乎不太高兴,有什么明确的东西我可以告诉他去做主机以使这项工作更好(即 - 被动返回正确的IP)?
答案 0 :(得分:9)
*get* '227 Entering Passive Mode (10,19,1,137,195,128).\n'
问题是服务器在对PASV命令的响应中返回了错误的IP地址。这对于某些防火墙后面的某些内部网络中的服在这种情况下,返回10.19.1.137,这是一个仅在本地网络中可用的IP地址。
这是FTP服务器的破坏设置。但不幸的是,这种破坏的设置很常见,因此许多客户端通过忽略响应中的给定IP地址并使用控制连接的IP地址来解决它。 ftplib不支持此类变通方法。但是人们可以修补它以提供这样的支持:
from ftplib import FTP_TLS
# replace original makepasv function with one which always returns
# the peerhost of the control connections as peerhost for the data
# connection
_old_makepasv = FTP_TLS.makepasv
def _new_makepasv(self):
host,port = _old_makepasv(self)
host = self.sock.getpeername()[0]
return host,port
FTP_TLS.makepasv = _new_makepasv
ftp = FTP_TLS(ipAddress)
ftp.login(...)
ftp.nlst()
使用Python 2.7.12和Python 3.5.2
成功测试了这一点