Python: 从FTP服务器下载文件

90
我正在尝试下载一些公共数据文件。为了获取文件的链接,我进行了屏幕截取,所有链接看起来都像这样:
ftp://ftp.cdc.gov/pub/Health_Statistics/NCHS/nhanes/2001-2002/L28POC_B.xpt

我找不到关于Requests库网站的任何文档。

9个回答

121

requests库不支持ftp://链接。

要从FTP服务器下载文件,您可以使用urlretrieve

import urllib.request

urllib.request.urlretrieve('ftp://server/path/to/file', 'file')
# if you need to pass credentials:
#   urllib.request.urlretrieve('ftp://username:password@server/path/to/file', 'file')

或者urlopen
import shutil
import urllib.request
from contextlib import closing

with closing(urllib.request.urlopen('ftp://server/path/to/file')) as r:
    with open('file', 'wb') as f:
        shutil.copyfileobj(r, f)

Python 2:

import shutil
import urllib2
from contextlib import closing

with closing(urllib2.urlopen('ftp://server/path/to/file')) as r:
    with open('file', 'wb') as f:
        shutil.copyfileobj(r, f)

2
谢谢您的回复,但是您如何提供凭据? - SSH This
4
尝试使用'ftp://username:password@server/path/to/file',或者使用@Rakesh的回答。如果还不行,可以提问 - jfs
3
这里有一些关于urllib和requests的信息:http://www.blog.pythonlibrary.org/2012/06/07/python-101-how-to-download-a-file/ - cbare
2
@cbare:链接的意义是什么?requests是否支持ftp - jfs
1
@grisaitis 这只是一个循环:while data := r.read(blocksize): f.write(data)(从输入文件对象 r 复制数据到输出文件 f)。 - jfs
显示剩余14条评论

72

你可以尝试这个

import ftplib

path = 'pub/Health_Statistics/NCHS/nhanes/2001-2002/'
filename = 'L28POC_B.xpt'

ftp = ftplib.FTP("Server IP") 
ftp.login("UserName", "Password") 
ftp.cwd(path)
ftp.retrbinary("RETR " + filename, open(filename, 'wb').write)
ftp.quit()

如果服务器上的文件名包含一些特殊字符,例如空格、$、&等,我需要对它们进行转义吗? - Dilawar
文件名可以是任意字节序列,但有一些例外,例如b'\xff'(我不知道任何标准的方法来转义这样的名称)。这里有更多详细信息(俄语)。如果您对FTP文件名有特定问题,可以提出单独的Stack Overflow问题。 - jfs
1
将文件名从Unicode编码转换为"utf-8"对我有用。也许在不同的操作系统上会有所不同:ftp.retrbinary(u"RETR täßt.jpg".encode('utf-8'), open('local.jpg', 'wb').write) - Aidas Bendoraitis
如果返回的数据大于块大小,我认为这将继续覆盖文件并仅保留最后一个块。 - mgilbert
1
我该如何指定要将它发送到本地机器上的哪个目录? - opperman.eric

18

尝试使用Python的wget库。您可以在这里找到它的文档。

import wget
link = 'ftp://example.com/foo.txt'
wget.download(link)

4
最简单并且效果良好。您还可以使用wget.download函数的out参数设置文件名。 - wordsforthewise
1
这个方法对我有效,而其他方法会导致文件崩溃。 - Samoth
@anatoly-techtonik 我认为你是这个pypi模块的作者。你认为它是否安全可靠使用? - yzorg
1
注意:自2015年以来没有发布,pypi上的主页链接是错误的链接(指向bitbucket)。作者的其他项目已移至github,但我没有看到这个项目。https://github.com/techtonik - yzorg

7
使用 urllib2 库。更多细节请参考这个 doc.python.org 的示例
以下是教程中的代码片段,可能会有所帮助:
import urllib2

req = urllib2.Request('ftp://example.com')
response = urllib2.urlopen(req)
the_page = response.read()

7
    import os
    import ftplib
    from contextlib import closing

    with closing(ftplib.FTP()) as ftp:
        try:
            ftp.connect(host, port, 30*5) #5 mins timeout
            ftp.login(login, passwd)
            ftp.set_pasv(True)
            with open(local_filename, 'w+b') as f:
                res = ftp.retrbinary('RETR %s' % orig_filename, f.write)

                if not res.startswith('226 Transfer complete'):
                    print('Downloaded of file {0} is not compile.'.format(orig_filename))
                    os.remove(local_filename)
                    return None

            return local_filename

        except:
                print('Error during download from FTP')

我有一个与这个主题完全无关但与您在Github上上传的代码相关的问题:http://stackoverflow.com/questions/27584233/sliding-window-how-to-get-window-location-on-image - user961627

4

正如一些人所指出的那样,requests库不支持FTP协议,但Python有其他库可以实现。如果您想继续使用requests库,可以使用requests-ftp包来添加FTP功能。我已经使用过这个库,它确实可以工作。但是文档中充满了有关代码质量的警告。截至0.2.0版本,文档中说:“这个库在大约4小时的总工作时间内被牛仔式地拼凑在一起,没有测试,并且依赖于一些丑陋的技巧。”

import requests, requests_ftp
requests_ftp.monkeypatch_session()
response = requests.get('ftp://example.com/foo.txt')

这个解决方案在我的手中至少是有效的,代码如下:s = requests.Session() response = s.get(...(而不是使用requests.get)。 - Matteo Ferla

4

如果你想利用最近Python版本的异步特性,你可以使用aioftp(来自同一开发者和库家族的更流行的aiohttp库)。这里是一个代码示例,取自他们的客户端教程

client = aioftp.Client()
await client.connect("ftp.server.com")
await client.login("user", "pass")
await client.download("tmp/test.py", "foo.py", write_into=True)

2

urllib2.urlopen可以处理FTP链接。


1
对于那些刚接触Python的人:urllib已经被改名回到了原来的名称,仍然支持FTP。基本上看一下最佳答案就可以了。 - yzorg

1

urlretrieve 对我来说无法工作,官方 文档 表示它们可能在未来某个时候被弃用。

import shutil 
from urllib.request import URLopener
opener = URLopener()
url = 'ftp://ftp_domain/path/to/the/file'
store_path = 'path//to//your//local//storage'
with opener.open(url) as remote_file, open(store_path, 'wb') as local_file:
    shutil.copyfileobj(remote_file, local_file)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接