使用Python的FTP库获取文件
这是我在这里的第一篇帖子,我很高兴能成为这个社区的一员。我有一个比较普通的问题想问,但这个问题让我很烦恼,所以希望能找到答案。
我正在尝试使用Python的FTPLIB模块来获取一个二进制文件。
我直接在解释器中输入的代码是这样的:
>>> from ftplib import FTP
>>> ftp = FTP('xxx.xx.xx.x') # IP of target device
>>> ftp.login()
>>> file = "foobar.xyz" # target file
>>> ftp.retrbinary("RETR " + file, open('filez.txt', 'wb').write)
虽然某些功能可以正常工作(我可以查看设备上的所有文件,获取FTP服务器应用的欢迎信息,甚至可以重命名文件),但当我尝试执行上面的最后一个命令时,我得到了
error_perm Traceback (most recent call last)
/Users/user_name/<ipython console> in <module>()
/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/ftplib.pyc in retrlines(self, cmd, callback)
419 if callback is None: callback = print_line
420 resp = self.sendcmd('TYPE A')
--> 421 conn = self.transfercmd(cmd)
422 fp = conn.makefile('rb')
423 while 1:
/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/ftplib.pyc in transfercmd(self, cmd, rest)
358 def transfercmd(self, cmd, rest=None):
359 """Like ntransfercmd() but returns only the socket."""
--> 360 return self.ntransfercmd(cmd, rest)[0]
361
362 def login(self, user = '', passwd = '', acct = ''):
/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/ftplib.pyc in ntransfercmd(self, cmd, rest)
327 if rest is not None:
328 self.sendcmd("REST %s" % rest)
--> 329 resp = self.sendcmd(cmd)
330 # Some servers apparently send a 200 reply to
331 # a LIST or STOR command, before the 150 reply
/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/ftplib.pyc in sendcmd(self, cmd)
241 '''Send a command and return the response.'''
242 self.putcmd(cmd)
--> 243 return self.getresp()
244
245 def voidcmd(self, cmd):
/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/ftplib.pyc in getresp(self)
216 raise error_temp, resp
217 if c == '5':
--> 218 raise error_perm, resp
219 raise error_proto, resp
220
error_perm: 502 Command not implemented.
我查看了ftplib的源代码,但我在这方面的编程经验比较有限,因为我通常用Python做数学计算,从来没有接触过FTP。
所以,如果有人能给我一些解决方案的想法,那将是一个很大的帮助。或者,如果你能建议用其他语言的解决方案路径,那也会很有帮助。
3 个回答
对我来说一切正常,试试其他服务器吧(我用的是一个Debian的FTP服务器进行测试):
ftp = FTP('xx.xx.xx.xx')
ftp.login()
'230 登录成功.'
file="robots.txt"
ftp.retrlines("RETR "+file, open('filez.txt', 'wb').write)
'226 文件发送成功.'
如果你的情况真的这么简单,那就没必要使用ftplib这个模块了。urllib.urlretrieve
就能满足你的需求。例如:
import urllib
urllib.urlretrieve('ftp://xxx.xx.xx.x/foobar.xyz', filename='filez.txt')
注意:在Python 3.x中,这个功能是urllib.request.urlretrieve
。
如果FTP服务器不是匿名的,你只需要在网址字符串中指定用户名和密码,就像平常那样(例如:ftp://user:password@server/path/to/file
)。当然,FTP协议会以明文形式发送密码,而你又把用户名和密码写在了脚本里,所以这样做在安全上有很多问题,但无论你用哪个库,这个问题都是存在的。
看起来你是在匿名登录,也就是在ftp.login()
里没有输入用户名和密码,所以你遇到了权限错误。试试用下面的方式登录:
ftp.login(user='foo', passwd='bar')
来试试。
补充:这里有一个简单的ftplib使用示例(很简单,没有错误处理):
#!/usr/bin/env python
from ftplib import FTP
HOST = "localhost"
UNAME = "foo"
PASSWD = "bar"
DIR = "pub"
FILE = "test.test"
def download_cb(block):
file.write(block)
ftp = FTP(HOST)
ftp.login(user=UNAME, passwd=PASSWD)
ftp.cwd(DIR)
file = open(FILE, "wb")
ftp.retrbinary("RETR " + FILE, download_cb)
file.close()
ftp.close()
注意:如果retrbinary
不工作,你也可以在调用它之前,先用ftp.sendcmd("TYPE I")
来明确设置为二进制模式。这种情况不常见,但可能对某些特殊的ftp服务器有帮助。