背景
室友让我帮他从一个国外的FTP服务器上面爬一些数据。以前只是从网页上面爬,还没有从FTP服务器爬过,然后网上大概搜了一下,写了个简单的小demo。
补充知识
- FTP(File Transfer Protocol,文件传输协议) 是TCP/IP协议组中的协议之一。FTP协议包括两个组成部分,其一为FTP服务器,其二为FTP客户端。其中FTP服务器用来存储文件,用户可以使用FTP客户端通过FTP协议访问位于FTP服务器上的资源。
- 默认情况下FTP协议使用TCP端口中的 20和21这两个端口,其中20用于传输数据,21用于传输控制信息。
ftplib模块官方文档:https://docs.python.org/3/library/ftplib.html#module-ftplibftp模块常用函数和命令
1 from ftplib import FTP # 导入ftplib模块
2 ftp=FTP() # 获取ftp变量
3 ftp.set_debuglevel(2) # 打开调试级别2,显示详细信息
4 ftp.connect("host","port") #连接的ftp sever服务器
5 ftp.login("usrname","password") # 用户登陆
6 print(ftp.getwelcome()) # 打印欢迎信息
7 ftp.cmd("xxx/xxx") # 进入远程目录
8 ftp.retrbinaly("RETR filename.txt",file_handle,bufsize) # 接收服务器上文件并写入本地文件
9 ftp.set_debuglevel(0) #关闭调试模式
10 ftp.quit() #退出ftp
11
12 ftp.cwd(ftppath) # 设置ftp当前操作的路径
13 ftp.dir() # 显示目录下所有文件信息
14 ftp.nlst() # 获取目录下的文件,返回一个list
15 ftp.mkd(pathname) # 新建远程目录
16 ftp.pwd() # 返回当前所在路径
17 ftp.rmd(dirname) # 删除远程目录
18 ftp.delete(filename) # 删除远程文件
19 ftp.rename(fromname, toname) # 将fromname修改名称为toname。
20 ftp.storbinaly("STOR filename.txt",fid,bufsize) # 上传目标文件
21 ftp.retrbinary("RETR filename.txt",fid,bufsize) # 下载FTP文件
下面是要取爬取的目标FTP服务器,左边是目录,右边是目录里面的文件。

代码如下:
由于这个服务器不需要账号和密码即可访问,所以在进行链接FTP服务器的时候没有传入账号和密码参数。其次,如果目标服务器中既有文件夹又有文件,就把函数ftpDownload中注释的部分取消注释,再稍加修改即可。
1 import os
2 from ftplib import FTP
3
4
5 # 连接ftp服务器
6 def ftpConnect(ftpserver,port):
7 ftp = FTP()
8 try:
9 ftp.connect(ftpserver, port)
10 ftp.login()
11 except:
12 raise IOError('\n FTP connection failed, please check the code!')
13 else:
14 print(ftp.getwelcome()) # 打印登陆成功后的欢迎信息
15 print('\n+------- ftp connection successful!!! --------+')
16 return ftp
17
18
19 # 下载单个文件
20 def ftpDownloadFile(ftp, ftpfile, localfile):
21 bufsize = 1024
22 path = os.path.join(localfile,ftpfile)
23 with open(path, 'wb') as fid:
24 print('正在下载:',ftpfile)
25 ftp.retrbinary('RETR {0}'.format(ftpfile), fid.write, bufsize) # 接收服务器文件并写入本地文件
26 print('下载完毕。')
27 return True
28
29
30 # 下载整个目录下的文件
31 def ftpDownload(ftp, ftpath, localpath):
32 '''
33 :param ftp: 登陆ftp返回的信息
34 :param ftpath: ftp中的目标路径
35 :param localpath: 存放下载文件的本地路径
36 :return:
37 '''
38 print('Remote Path: {0}'.format(ftpath))
39 if not os.path.exists(localpath):
40 os.makedirs(localpath)
41 ftp.cwd(ftpath)
42 print('成功进入ftp服务器:',ftpath)
43 for file in ftp.nlst():
44 print('file:',file)
45 local = os.path.join(localpath, file)
46 file_path = os.path.join(ftpath, file)
47 if not os.path.exists(local):
48 os.makedirs(local)
49 ftp.cwd(file_path)
50 print('进入子目录:--', file_path)
51 for sub_file in ftp.nlst():
52 ftpDownloadFile(ftp, sub_file, local)
53 ftp.cwd('..')
54
55 # if not os.path.exists(local):
56 # os.makedirs(local)
57 # if os.path.isdir(file): # 判断是否为子目录
58 # print('000000')
59 # if not os.path.exists(local):
60 # os.makedirs(local)
61 # print('1111111111111')
62 # ftpDownload(ftp, file, local) # 递归调用
63 # else:
64 # print('2222')
65 # ftpDownloadFile(ftp, file, local)
66 # ftp.cwd('..')
67 ftp.quit()
68 return True
69
70
71 # 退出ftp连接
72 def ftpDisConnect(ftp):
73 ftp.quit()
74
75 # 程序入口
76 if __name__ == '__main__':
77 # 输入参数
78 ftpserver = 'www.ngs.noaa.gov'
79 ftpath = '/cors/rinex/2018/001/'
80 localpath = 'F:/data/'
81
82 ftp = ftpConnect(ftpserver, 21)
83 flag = ftpDownload(ftp, ftpath, localpath)
84 print(flag)
85 ftpDisConnect(ftp)
86 print("\n+-------- OK!!! --------+\n")
最后,可能由于这个服务器是国外的原因,下载速度奇慢无比,还不如用专门的FTP下载软件去下载。