17.6、开发FTP文件传输程序
在计算机网络领域中,远程文件传输又是一个重要的分支。在计算机的7层协议当中,TCP、FTP、Telnet、UDP可以实现远程文件处理。作为一门功能强大的开发语言,Python可以实现对远程文件的处理。本章将详细讲解使用Python语言开发远程文件传输系统的过程。
17.6.1、Python和FTP
当使用Python语言编写FTP客户端程序时,需要将相应的Python 模块ftplib导入项目程序中。具体开发流程如下所示。
(1)连接到服务器。
(2)登录。
(3)发出服务请求(希望能得到响应)。
(4)退出。
在使用Python语言开发FTP程序时,首先需要导入,然后实例化一个ftplib.FTP类对象,所有的FTP 操作(如登录、传输文件和注销等)都要使用这个对象完成。使用类FTP可以创建一个FTP连接对象。具体语法格式如下所示。
FTP(host, user, passwd, acct) |
"host:要连接的FTP服务器,可选参数。
"user:登录FTP服务器所使用的用户名,可选参数。
"passwd:登录FTP服务器所使用的密码,可选参数。
"acct:可选参数,默认为空。
在内置模块ftplib的FTP类中,主要包含如下所示常用的方法。
(1)方法set_debuglevel()。
当创建一个FTP连接对象以后,可以使用方法set_debuglevel()设置调试级别。其语法格式如下。
set_debuglevel (level) |
参数level是指调试级别,默认的调试级别为0。
(2)方法connect()。
如果在创建FTP连接对象时没有使用参数host,则可以使用FTP对象中的方法connect(),其语法格式如下。
connect(host,port,timeout,source-address) |
"host:要连接的FTP服务器。
"port:FTP服务器的端口,可选参数。
(3)方法login()。
如果在创建FTP对象时没有使用用户名和密码,则可以通过FTP对象中的方法login()使用用户名和密码登录FTP服务器。其语法格式如下。
login (user, passwd, acct) |
"user:登录FTP服务器所使用的用户名。
"passwd:登录FTP服务器所使用的密码。
"acct:可选参数,默认为空。
(4)方法getwelcome()、sendcmd()和voidcmd()。
使用FTP对象中的方法getwelcome()可以获得FTP服务器的欢迎信息。使用FTP对象中的方法abort()可以中断文件传输。使用FTP对象中的方法sendcmd()和方法voidcmd()可以向FTP服务器发送命令,这两个方法的不同之处在于voidcmd()方法没有返回值。这两个方法的语法格式如下。
sendcmd( command) voidcmd( command) |
参数command是指向服务器发送的命令字符串。
(5)方法retrbinary()和方法retrlines()。
使用FTP对象中的方法retrbinary()和方法retrlines()可以从FTP服务器下载文件。不同的是,retrbinary()方法使用二进制形式传输文件,而retrlines()方法使用ASCII形式传输文件。其语法格式如下。
retrbinary(command, callback, maxblocksize, rest) retrlines (command, callback) |
对于retrbinary()方法,各个参数的具体含义如下所示。
"command:传输命令,由"RETR+文件名"组成(之间有空格)。
"callback:传输回调函数。
"maxblocksize:设置每次传输的最大字节数,可选参数。
"rest:设置文件续传位置,可选参数。
对于retrlines()方法,各个参数的具体含义如下所示。
"command:传输命令。
"callback:传输回调函数。
(6)方法storbinary()和方法storlines()。
使用FTP对象中的方法storbinary()和方法storlines()可以向FTP服务器上传文件。这两个方法的不同之处是storbinary()方法使用二进制形式传输文件,而storlines()方法使用ASCII码形式传输文件。这两个方法的语法格式如下。
storbinary(command, file, blocksize) storlines (command, file) |
对于storbinary()方法,各个参数的具体含义如下所示。
"command:传输命令,由"STOR+文件名"组成(之间有空格)。
"file:本地文件句柄。
"blocksize:设置每次读取文件的最大字节数,可选参数。
对于storlines()方法,各个参数的含义如下。
"command:传输命令。
"file:本地文件句柄。
(7)方法set_pasv()。
使用FTP对象中的方法set_pasv()可以设置传输模式。具体语法格式如下所示。
set_pasv (boolean) |
如果参数boolean为True,则为被动模式;如果为False,则为主动模式。
(8)方法dir()和方法rename()。
使用FTP对象中的方法dir()可以获取当前目录中内容列表。使用FTP对象中的方法rename()可以修改FTP服务器中的文件名。具体语法格式如下所示。
rename (fromname, toname) |
"fromname:原来文件名。
"toname:重命名后的文件名。
(9)方法delete()。
使用FTP对象中的方法delete()可以从FTP服务器上删除文件。具体语法格式如下所示。
delete( filename) |
参数filename是要删除的文件名。
(10)方法cwd()。
使用FTP对象中的方法cwd()可以改变当前目录。具体语法格式如下所示。
cwd (pathname) |
参数pathname是要进入目录的路径。
(11)方法mkd()。
使用FTP对象中的方法mkd()可以在FTP服务器上创建目录。具体语法格式如下所示。
mkd (pathname) |
参数pathname表示要创建目录的路径。
(12)方法pwd()。
使用FTP对象中的方法pwd()可以获得当前目录。
(13)方法rmd()。
使用FTP对象中的方法rmd()可以删除FTP服务器上的目录。具体语法格式如下所示。
rmd (dirname) |
参数dirname表示要删除的目录。
(14)方法size()。
使用FTP对象中的方法size()可以获得文件的大小。具体语法格式如下所示。
size(filename) |
参数filename表示要获取文件大小的文件名。
(15)方法quit()和方法close()。
使用FTP对象中的方法quit()和方法close()可以关闭与FTP服务器的连接。
17.6.2、创建一个FTP文件传输客户端
下面的实例代码演示了使用ftplib库创建一个简单的FTP文件传输客户端的过程。
实例17-8 创建一个简单的FTP文件传输客户端
源码路径 daima\17\17-8
实例文件ftp.py的具体实现代码如下所示。
from ftplib import FTP #导入FTP bufsize = 1024 #设置缓冲区的大小 def Get(filename): #定义函数Get()下载文件 command = 'RETR ' + filename #变量command初始化 #下载FTP文件 ftp.retrbinary(command, open(filename, 'wb').write, bufsize) print('下载成功') #下载成功提示 def Put(filename): #定义函数Put()上传文件 command = 'STOR ' + filename #变量command初始化 filehandler = open(filename,'rb')#打开指定文件 ftp.storbinary(command,filehandler,bufsize) #实现文件上传操作 filehandler.close() #关闭连接 print('上传成功') #显示提示 def PWD(): #定义获取当前目录的函数PWD() print(ftp.pwd()) #返回当前所在位置 def Size(filename): #定义获取文件大小的函数Size() print(ftp.size(filename))#显示文件大小 def Help(): #定义系统帮助函数Help() print(''' #开始显示帮助提示 ================================== Simple Python FTP ================================== cd 进入文件夹 delete 删除文件 dir 获取当前文件列表 get 下载文件 help 帮助 mkdir 创建文件夹 put 上传文件 pwd 获取当前目录 rename 重命名文件 rmdir 删除文件夹 size 获取文件大小 ''') server = input('请输入FTP服务器地址:') #信息输入 ftp = FTP(server) #获取服务器地址 username = input('请输入用户名:') #输入用户名 password = input('请输入密码:') #输入密码 ftp.login(username,password) #使用用户名和密码登录FTP服务器 print(ftp.getwelcome()) #显示欢迎信息 #定义一个字典actions,在里面保存操作命令 actions = {'dir':ftp.dir, 'pwd': PWD, 'cd':ftp.cwd, 'get':Get, 'put':Put, 'help':Help, 'rmdir': ftp.rmd, 'mkdir': ftp.mkd, 'delete':ftp.delete, 'size':Size, 'rename':ftp.rename} while True: #执行循环操作 print('pyftp>') #显示提示符 cmds = input() #获取用户的输入 cmd = str.split(cmds) #使用空格分隔用户输入的内容 try: #异常处理 if len(cmd) == 1: #验证输入命令中是否有参数 if str.lower(cmd[0]) == 'quit': #如果输入的命令是quit,则退出循环 break else: actions[str.lower(cmd[0])]() #调用与输入的命令对应的操作函数 elif len(cmd) == 2: #处理只有一个参数的命令 actions[str.lower(cmd[0])](cmd[1]) #调用与输入的命令对应的操作函数 elif len(cmd) == 3: #处理有两个参数的命令 actions[str.lower(cmd[0])](cmd[1],cmd[2]) #调用与输入的命令对应的操作函数 else: #如果是其他情况 print('输入错误') #显示错误提示 except: print('命令出错') ftp.quit() #退出系统 |
运行上述实例代码后,会要求输入FTP服务器的地址、用户名和密码。如果正确输入上述信息则完成FTP服务器登录,并显示一个"pyftp>"提示符,等待用户输入命令。如果输入"dir"和"pwd"这两个命令,就会调用和执行对应的命令操作函数。在测试和运行本实例代码时,需要有一个FTP服务器及登录该服务器的用户名和密码。如果读者没有互联网中的FTP服务器,可以尝试在本地计算机中通过IIS配置一个FTP服务器,然后进行测试。执行后的效果如图17-10所示。
17.7、解析XML
XML是指可扩展置标语言(eXtensible Markup Language),标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。在现实应用中,常见的XML编程接口有两种,分别是SAX和DOM。所以与之对应的是,Python语言有两种解析XML文件的方法,分别是SAX和DOM方法。本节将详细讲解使用Python语言解析XML文件的知识。
17.7.1、SAX解析方法
在Python语言的标准库中包含了SAX解析器,SAX通过使用事件驱动模型,在解析XML的过程中触发一个个的事件并调用用户定义的回调函数来处理XML文件。
SAX是一种基于事件驱动的API,当利用SAX解析XML文档时会涉及如下两部分。
"解析器:负责读取XML文档,并向事件处理程序发送事件,例如元素开始与元素结束事件。
"事件处理器:负责对事件做出响应,对传递的XML数据进行处理。
当在Python程序中使用SAX方式处理XML文件时,需要先引入文件xml.sax中的parse()函数,还有xml.sax.handler中的类ContentHandler。其中类ContentHandler中的常用函数如下所示。
(1)characters(content):调用时机如下所示。
"从行开始,在遇到标签之前,存在字符,content的值为这些字符串。
"从一个标签开始,在遇到下一个标签之前,存在字符,content的值为这些字符串。
"从一个标签开始,在遇到行结束符之前,存在字符,content的值为这些字符串。
这里标签可以是开始标签,也可以是结束标签。
(2)startDocument():当启动文档时调用。
(3)endDocument():当解析器到达文档结尾时调用。
(4)startElement(name, attrs):当遇到XML开始标签时调用,name是标签的名字,attrs是标签的属性值字典。
(5)endElement(name):当遇到XML结束标签时调用。
在xml.sax中的常用内置函数如下所示。
(1)parse ():通过如下语法格式可以创建一个SAX解析器,并解析XML文档。
xml.sax.parse( xmlfile, handler[, errorhandler]) |
各个参数的具体说明如下所示。
"xmlfile:XML文件名。
"handler:必须是一个ContentHandler的对象。
"errorhandler:如果指定该参数,errorhandler必须是一个SAX ErrorHandler对象。
(2)parseString():功能是创建一个XML解析器并解析XML字符串。具体语法格式如下所示。
xml.sax.parseString(xmlstring, contenthandler[, errorhandler]) |
各个参数的具体说明如下所示。
"xmlstring:XML字符串。
"contenthandler:必须是一个ContentHandler的对象。
"errorhandler:如果指定该参数,errorhandler必须是一个SAX ErrorHandler对象。
下面的实例代码演示了使用SAX方法解析XML文件的过程。
实例17-9 使用SAX方法解析XML文件
源码路径 daima\17\17-9
文件movies.xml是一个基本的XML文件,在里面保存一些和电影有关的资料信息。文件movies.xml的具体实现代码如下所示。
<collection shelf="New Arrivals"> <movie title="Enemy Behind"> <type>War, Thriller</type> <format>DVD</format> <year>2003</year> <rating>PG</rating> <stars>10</stars> <description>Talk about a US-Japan </description> </movie> <movie title="Transformers"> <type>Anime, Science Fiction</type> <format>DVD</format> <year>1989</year> <rating>R</rating> <stars>8</stars> <description>A scientific fiction</description> </movie> <movie title="Trigun"> <type>Anime, Action</type> <format>DVD</format> <episodes>4</episodes> <rating>PG</rating> <stars>10</stars> <description>Vash the Stampede!</description> </movie> <movie title="Ishtar"> <type>Comedy</type> <format>VHS</format> <rating>PG</rating> <stars>2</stars> <description>Viewable boredom</description> </movie> </collection> |
实例文件sax.py的功能是解析文件movies.xml的内容。具体实现代码如下所示。
import xml.sax class MovieHandler( xml.sax.ContentHandler ): def __init__ (self): self.CurrentData = "" self.type = "" self.format = "" self.year = "" self.rating = "" self.stars = "" self.description = "" #元素开始调用 def startElement(self, tag, attributes): self.CurrentData = tag if tag == "movie": print ("*****Movie*****") title = attributes["title"] print ("Title:", title) #元素结束调用 def endElement(self, tag): if self.CurrentData == "type": #处理XML中的type元素 print ("Type:", self.type) elif self.CurrentData == "format": #处理XML中的format元素 print ("Format:", self.format) elif self.CurrentData == "year": #处理XML中的year元素 print ("Year:", self.year) elif self.CurrentData == "rating": #处理XML中的rating元素 print ("Rating:", self.rating) elif self.CurrentData == "stars": #处理XML中的stars元素 print ("Stars:", self.stars) elif self.CurrentData == "description": #处理XML中的description元素 print ("Description:", self.description) self.CurrentData = "" # 读取字符时调用 def characters(self, content): if self.CurrentData == "type": self.type = content elif self.CurrentData == "format": self.format = content elif self.CurrentData == "year": self.year = content elif self.CurrentData == "rating": self.rating = content elif self.CurrentData == "stars": self.stars = content elif self.CurrentData == "description": self.description = content if (__name__ == "__main__"): # 创建一个XMLReader parser = xml.sax.make_parser() # turn off namespaces parser.setFeature(xml.sax.handler.feature_namespaces, 0) # 重写ContentHandler Handler = MovieHandler() parser.setContentHandler( Handler ) parser.parse("movies.xml") |
执行后的效果如图17-11所示。
17.7.2、DOM解析方法
DOM是文件对象模型(Document Object Model)的简称,是W3C组织推荐的处理可扩展置标语言的标准编程接口。当一个DOM解析器在解析一个XML文档时,可以一次性读取整个文档。将文档中的所有元素保存在内存中的一个树结构中后,可以利用DOM 提供的不同的函数来读取或修改文档的内容和结构,也可以把修改过的内容写入到XML文件中。
下面的实例代码演示了使用DOM方法解析XML文件的过程。
实例17-10 使用DOM方法解析XML文件
源码路径 daima\17\17-10
实例文件dom.py的功能是解析文件movies.xml的内容。具体实现代码如下所示。
from xml.dom.minidom import parse import xml.dom.minidom #使用minidom解析器打开 XML 文档 DOMTree = xml.dom.minidom.parse("movies.xml") collection = DOMTree.documentElement if collection.hasAttribute("shelf"): print ("Root element : %s" % collection.getAttribute("shelf")) #在集合中获取所有电影 movies = collection.getElementsByTagName("movie") #输出每部电影的详细信息 for movie in movies: print ("*****Movie*****") if movie.hasAttribute("title"): print ("Title: %s" % movie.getAttribute("title")) type = movie.getElementsByTagName('type')[0] print ("Type: %s" % type.childNodes[0].data) format = movie.getElementsByTagName('format')[0] print ("Format: %s" % format.childNodes[0].data) rating = movie.getElementsByTagName('rating')[0] print ("Rating: %s" % rating.childNodes[0].data) description = movie.getElementsByTagName('description')[0] print ("Description: %s" % description.childNodes[0].data) |
执行后的效果和实例17-9的一样(见图17-11)。
相关阅读:
版权声明:51Testing软件测试网获人民邮电出版社和作者授权连载本书部分章节。
任何个人或单位未获得明确的书面许可,不得对本文内容复制、转载或进行镜像,否则将追究法律责任。