zoukankan      html  css  js  c++  java
  • 【python3的进阶之路二】因特网客户端编程

    一、文件传输

    1.1 文件传输因特网协议

          最流行的协议包括文件传输协议(FTP)、UNIX到UNIX复制协议(UUCP)、用于Web的超文本传输协议(HTTP)。另外,还有(UNIX下的)远程文件复制命令rcp(以及更安全、灵活的scp和rsync)。
          HTTP主要用于基于Web的文件下载以及访问Web服务,一般客户端无须登录就可以访问服务器上的文件和服务。大部分HTTP文件传输请求都用于获取网页(即将网页文件下载到本地)。
          而scp和rsync需要用户登录到服务器主机。在传输文件之前必须验证客户端的身份,否则不能上传或下载文件。FTP与scp/rsync相同,它也可以上传或下载文件,并采用了UNIX的多用户概念,用户需要输入有效的用户名和密码。但FTP也允许匿名登录。

    1.2 文件传输协议

         文件传输协议(File Transfer Protocol,FTP)主要用于匿名下载公共文件,也可以用于在两台计算机之间传输文件,特别实在使用Windows进行工作,而文件存储系统使用UNIX的情况下。
         FTP要求输入用户名和密码才能访问远程FTP服务器,但也允许没有账号的用户匿名登录。不过管理员要先设置FTP服务器以允许匿名用户登录。这时,匿名用户的用户名是“anonymous”,密码一般是用户的电子邮件地址。与向特定的登录用户传输文件不通过,这相当于公开某些目录让大家访问。但与登录用户相比,匿名用户只能使用有限的几个FTP明令。

    图片.png

    由上图展示的这个协议,其工作流程如下:
    1、客户端连接远程主机上的FTP服务器
    2、客户端输入用户名和密码(或“anonymous”和电子邮件地址)
    3、客户端进行各种文件传输和信息查询操作
    4、客户端从远程FTP服务器退出,结束传输
    有时,由于网络两边计算机的崩溃或网络的问题,会导致整个传输在完成之前中断。如果客户端超过15分钟(900秒)还没有响应,FTP连接就会超时并中断。
         在底层,FTP只使用TCP,而不是UDP。另外,可以将FTP看作客户端/服务器编程中的特殊情况。因为这里的客户端和服务器都使用俩个套接字来通信:一个是控制和命令端口(21号端口),另一个是数据端口(有时是20号端口)。
         这里说的“有时”是因为FTP由俩种模式:主动和被动。只有主动模式下服务器才使用数据端口。在服务器把20号端口设置为数据端口后,它“主动”连接客户端的数据端口。而在被动模式下,服务器只是告诉客户端随机的数据端口号,客户端必须主动建立数据连接。在这种模式下,FTP服务器在建立数据连接时是“被动”的。最后,现在已经有了一种扩展的被动模式来支持第6版本的因特网协议(IPv6)地址——详见RFC 2428

    1.3 Python和FTP

    回顾流程:
    1、连接到服务器
    2、登录
    3、发出服务请求(希望得到响应)
    4、退出
         在使用Python的FTP支持时,所需要做的只是导入ftplib模块,并实例化一个ftplib.FTP类对象。所有的FTP操作(如登录、传输文件和注销等)都要使用这个对象完成。

    from ftplib import FTP
    
    f = FTP('some.ftp.server')
    f.login('anonymous', 'your@email.address')
    .
    .
    .
    f.quit()

    1.4 ftplib.FTP类的方法

    方法 描述
    login(self, user, password, acct) 登录到FTP服务器,所有参数都是可选的
    pwd(self) 获取当前工作目录
    cwd(self, dirname) 在服务器上设置当前目录
    dir(self, args) 生成LIST命令返回的目录列表,将其打印到标准输出。可选参数是要列出的目录(默认为当前服务器目录)。可以使用多个参数将非标准选项传递给LIST命令。如果最后一个参数是一个函数,它将被用作回调函数retrlines(); 默认打印到 sys.stdout。此方法返回None。
    nlst(self, args) 与dir()类似,但返回一个文件名列表,而不是显示这些文件名
    retrlines(self, cmd, callback) 给定FTP命令(如“RETR filename”),用于下载文件。可选的回调函数cb用于处理文件的每一行
    retribinary(self, cmd, fp, blocksize, callback, rest) 与retrlines类似,只是这个指令处理二进制文件。回调函数用于处理每一块(块大小默认8KB)
    storlines(self, cmd, fp, callback) 给定FTP命令(如“STOR filename”),用来上传文本文件。要给定一个文件对象f
    storbinary(self, cmd, fp, blocksize, callback, rest) 与storlines()类似,只是这个指令处理二进制文件。要给定一个文件对象f,上传块大小bs默认为8KB
    rename(self, fromname, toname) 文件重命名
    delete(self, filename) 删除名为dirname的远程文件。如果成功,则返回响应的文本,否则会引发error_perm权限错误或 error_reply其他错误。
    mkd(self, dirname) 在服务器上创建一个新目录
    rmd(self, dirname) 删除服务器上名为dirname的目录
    quit(self) 关闭连接并退出
    close(self) 单方面关闭连接

    FTP对象更多信息:https://docs.python.org/3/library/ftplib.html

    1.5 客户端FTP程序示例

    import ftplib
    import os
    import socket
    
    HOST = 'ftp.sjtu.edu.cn'   # 不可用ftp://ftp.sjtu.edu.cn/
    DIRN = 'logs/rsync'
    FILE = 'fedora-epel.trace'
    
    def main():
        try:
            f = ftplib.FTP(HOST)
        except (socket.error, socket.gaierror) as e:
            print('ERROR:cannot reach %s' % HOST)
            return
        print('*** Connected to host %s' % HOST)
    
        try:
            f.login()
        except ftplib.error_perm:
            print('ERROR: cannot login anoymously')
            f.quit()
            return
        print("*** Logged in as 'anonymous'")
    
        try:
            f.cwd(DIRN)
        except ftplib.error_perm:
            print('ERROR: cannot CD to %s' % DIRN)
            f.quit()
            return
        print('*** Changed to %s folder' % f.pwd())
    
        try:
            f.retrbinary('RETR %s' % FILE, open(FILE, 'r'))
        except ftplib.error_perm:
            print('ERROR: cannot read file %s' % FILE)
            os.unlink(FILE)
        else:
            print('*** Downloaded %s to CWD' % FILE)
        f.quit()
    
    if __name__ == '__main__':
        main()

    说明:
    第一到七行

    代码前几行导入要用的模块(主要用于抓取异常对象),并设置一些常量

    第九行到四十一行

    创建一个FTP对象,尝试连接到FTP服务器(第十到十五行),然后返回。如果发生任何错误九退出。接着尝试用“anonymous”登录,如果不行九结束(第十七到二十三行)。下一步就是转到发布目录(第二十五到三十一行),最后下载文件(第三十三扫四十一行)。
    在第三十四行,向retrbinary()传递一个回调函数,没接收到一块二进制数据的时候都会调用这个回调函数。这个函数就是船舰文件的本地版本时需要用到的文件对象的write()方法。传输结束时,python解释器会自动关闭这个文件对象,因此不会丢失数据。虽然方便,但要尽量做到在资源不再被使用的时候九立即释放,而不是依赖其他代码来完成释放操作。这里应该把开放的文件对象保存到一个变量(如变量loc),然后把loc.write传给ftp.retrbinary()。
    完成传输后,调用loc.close()。如果由于某些原因无法保存文件,则移除空的文件夹来避免弄乱文件系统(第三十七行)。在os.unlink(FILE)俩侧添加一些错误检查代码,以应对文件不存在的情况。

    第四十二到四十三行

    运行独立脚本的惯用方法

    1.6 FTP的其他内容

    Python同时支持主动和被动模式。注意,在Python2.0及以前版本中,被动模式默认时关闭的;在Python2.1及以后版本中,默认时打开的。
    以下是一些典型的FTP客户端类型:

    • 命令行客户端程序: 使用一些FTP客户端程序(如/bin/ftp或NcFTP)进行FTP传输,用户可以在命令中交互式执行FTP传输
    • GUI客户端程序: 与命令客户端程序相似,但它是一个GUI程序,如WS_FTP、Filezilla、CuteFTP、Fetch、SmartFTP
    • Web浏览器: 除了使用HTTP之外,大多数Web浏览器(也称为客户端)可以进行FTP传输。URL/URI的第一部分就用来表示所使用的协议,如“http://blahblah”。这就告诉浏览器要使用HTTP作为指定网站传输数据的协议。通过修改协议部分,就可以发送使用FTP的请求,如“ftp://blahblah”,这与使用HTTP的网页URL很像(“blahblah”可以展开为“host/path?attributes”)。如果要登录,用户可以把登录信息(以明文方式)放在URL里,如“ftp://user:password@host/path?attr1=vall&attr2=val2…”
    • 自定义应用程序: 自己编写的用于FTP文件传输的程序。这些式用于特殊目的的应用程序,一般这种程序不允许用户与服务器交互

    二、网络新闻

    2.1 Usenet与新闻组

          Usenet新闻系统式一个全球存档的“电子公告板”。各个主题的新闻组一应俱全,新闻组可以面向全球,也可以只面向某个特定区域。老的Usenet使用UUCP作为其网络传输机制,在20世纪80年代中期出现了另一个网络协议TCP/IP,之后大部分网络流量转向使用TCP/IP。

    2.2 网络新闻传输协议

          作为客户端/服务器架构的另一个例子,NNTP与FTP的操作方式相似,但更简单。FTP中,登录、传输数据和控制需要使用不同的端口,而NNTP只使用一个标准端口119来通信。用户向服务器发送一个请求,服务器就做出相应的响应。

    图片.png

    2.3 Python和NNTP

    NNTP协议流程:
    1、连接到服务器
    2、登录(根据需要)
    3、发出服务请求
    4、退出

    from nntplib import NNTP
    
    n = NNTP('your.nntp.server')
    r,c,f,l,g = n.group('comp.lang.python')
    .
    n.quit()

    登录后需要调用group()方法来选择一个感兴趣的新闻组。该方法返回服务器的回复、文章的数量、第一篇和最后一篇文章的ID、新闻组的名称。

    2.4 nntplib.NNTP类方法

    方法 描述
    group(self, name) 选择一个组的名字,返回一个元组(rsp,ct,fst,lst,group),分别表示服务器响应信息、文章数量、第一个和最后一个文章的编号、组名,所有数据都是字符串。(返回的group与传进去的name应该式相同的)
    xhdr(self, hdr, str, file) 发送XHDR命令。该命令没有在RFC中定义,但是是一个常见的扩展。的报头参数是一个报头的关键字,例如'subject'。该字符串参数应具有的形式'first-last',其中第一和最后一个是第一个和最后的文章编号,以搜索。返回一对(response, list),其中list是对的列表(id, text),其中id是商品编号(作为字符串),text是该文章请求的标题的文本。如果提供了文件参数,则XHDR命令的输出将存储在文件中。如果文件是一个字符串,然后该方法将打开一个名称为文件对象,写入并关闭它。如果file是一个文件对象,那么它将开始调用write()它来存储命令输出的行。如果提供了文件,则返回的列表是一个空列表。
    body(self,message_spec, file) 发送一个BODY命令,其中id的含义与以前相同stat()。如果提供了文件参数,则主体存储在文件中。如果file是一个字符串,那么该方法将打开一个具有该名称的文件对象,写入并关闭它。如果文件是一个文件对象,那么它将开始调用write()它来存储正文的行。返回head()。如果提供了文件,则返回的列表是一个空列表。
    head(self,message_spec, file) 发送一个HEAD命令,其中id的含义与以前相同stat()。返回一个元组(response, number, id, list),其中前三个元素与for相同stat(),list是文章标题列表(一个没有解释的行列表,没有尾随换行符)。
    article(self,message_spec, file) 发送一个ARTICLE命令,其中id的含义与以前相同stat()。返回head()。
    stat(self,message_spec) 发送一个STAT命令,其中id是消息ID(用'<'and 括起来'>')或商品编号(作为字符串)。返回一个triple (response, number, id),其中number是商品编号(作为字符串),id是消息ID(用'<'和括起来'>')。
    next(self) 把文章指针移到下一篇文章,返回与stat()相似的元组
    last(self) 把文章指针移到最后一篇文章,返回与stat()相似的元组
    post(self,data) 使用该POST命令发布文章。的文件参数是其使用其读直至EOF一个打开的文件对象readline()的方法。它应该是一篇格式良好的新闻文章,包括必需的标题。该post()方法自动转义以.。开头的行。
    quit(self) 关闭连接并退出

    2.5 客户端程序NNTP示例

    import nntplib
    import socket
    
    HOST = 'your.nntp.server'
    GRNM = 'comp.lang.python'
    USER = 'wesley'
    PASS = 'youllNeverGuess'
    
    def main():
        try:
            n = nntplib.NNTP(HOST)
            #, user = USER, password = PASS)
        except socket.gaierror as e:
            print('ERROR: cannot reach host %s' % HOST)
            print('%s' % eval(str(e))[1])
            return
        except nntplib.NNTPermanentError as e:
            print('ERROR:access denied on %s' % HOST)
            print('%s' % str(e))
            return
        print('*** Connected to host %s' % HOST)
    
        try:
            rsp, ct, fst, lst, grp = n.group(GRNM)
        except nntplib.NNTPTemporaryError as ee:
            print('ERROR: cannot load group %s' % GRNM)
            print('%s' % str(e))
            print('Server may require authentication')
            print('Uncomment/edit login line above')
            n.quit()
            return
        except nntplib.NNTPTemporaryError as ee:
            print('ERROR: group %s unavailable' % GRNM)
            print('%s' % str(e))
            n.quit()
            return
        print('*** Found newsgroup %s' % GRNM)
    
        rng = '%s-%s' % (lst, lst)
        rsp, frm = n.xhdr('from', rng)
        rsp, sub = n.xhdr('subject',rng)
        rsp, dat = n.xhdr('date', rng)
        print('''*** Found last article (#%s)
        From:%s
        Subject:%s
        Date:%s
        ''' % (lst, frm[0][1], sub[0][1], dat[0][1]))
    
        rsp, anum, mid, data = n.body(lst)
        displayFirst20(data)
        n.quit()
    
    def displayFirst20(data):
        print('*** First (<=20) meaningful lines:
    ')
        count = 0
        lines = (line.rstrip() for line in data)
        lastBlank = True
        for line in lines:
            if line:
                lower = line.lower()
                if(lower.startswith('>') and not 
                lower.startswith('>>>')) or 
                lower.startswith('|') or 
                lower.startswith('in article') or 
                lower.endswith('writes:') or 
                lower.endswith('wrote:'):
                    continue
            if not lastBlank or (lastBlank and line):
                print('%s' % line)
                if line:
                    count += 1
                    lastBlank = False
                else:
                    lastBlank = True
            if count == 20:
                break
    
    if __name__ == '__main__':
        main()

    说明:
    第九行到三十七行:
          尝试连接NNTP主机服务器,如果失败就退出。接着尝试读取指定的新闻组。同样,如果新闻组不存在,或服务器没有保存这个新闻组,或需要身份验证,就退出
    第三十九到五十一行:
          这一部分读取并显示一些头消息(第三十九到第四十七行)。程序会读取作者、主题、日期这些数据并显示给用户。每次调用xhdr()方法时,都要给定想要提取消息头的文章的范围。因为这里只想获取一条消息,所以范围就是“X-X”,其中X是最新一条消息的号码。xhdr()方法返回一个长度为2的元组,其中包含了服务器的响应(rsp)和指定范围的消息头的列表。
          最后一部分是下载文章的内容(第四十九到五十一行)。先调用body()方法,然后至多显示前20个有意义的行,最后从服务器注销,完成处理。
    第五十三到七十六行:
          该函数接受文章的一些内容,并做一些预处理,如把计数器清0,创建一个生成器表达式对文章内容的所有行做一些处理,然后“假装”刚碰到并显示了一行空行(第五十五到五十七行)。
          由于不想显示引用的文本和引用文本指示行,而在第六十到六十七使用了一个大if语句。只有在当前行不是空行时,才做这个检查。检查的时候,会把字符串转成小写,这样就能做到比较的时候不区分大小写。
          第六十八行的if语句表示只有在上一行不为空,或者上一行为空但当前不为空的时候才显示。

    三、电子邮件

    3.1 电子邮件电子组件和协议

          电子邮件系统的重要组件是消息传输代理(MTA).这是在邮件交换主机上运行的服务进程,它负责邮件的路由、队列处理和发送工作。MTA就是邮件从发送主机到接收主机所要经过的主机和“跳板”,也称为“消息传输”的“代理”。MTA要知道的俩件事:
    1、如何找到消息应该到达的下一个MTA
    2、如何与另一台MTA通信

    3.2 Python和SMTP

    需要一个smtplib模块和一个需要实例化的smtplib.SMTP类。流程:
    1、连接到服务器
    2、登录(根据需要)
    3、发出服务请求
    4、退出

    from smtplib import SMTP
    
    n = SMTP('smtp.yourdomain.com')
    .
    .
    .
    n.quit()

    3.4 smtplib.SMTP类方法

    方法 描述
    sendmail(self, from_addr, to_addr, msg, mail_options, rcpt_options) 将msg从from_addr发送至to_addr,还可以选择性地设置ESMTP邮件(mail_options)和收件人(rcpt_options)选项
    ehlo(self, name)或helo(self, name) 使用ehlo指令像ESMTP(SMTP扩展)确认你的身份/使用helo指令向SMTP服务器确认你的身份
    starttls(self, keyfile, certfile, context) 让服务器启用TLS模式。如果给定了keyfile或certfile,则它们用来创建安全套接字
    set_debuglevel(self,debuglevel) 为服务器通信设置调试级别
    quit(self) 关闭连接并退出
    login(self, user, password, initial_response_ok) 使用用户名和密码登录SMTP服务器

    3.5 Python和POP3

    需要一个poplib模块和一个需要实例化的poplib.POP3类。流程:
    1、连接到服务器
    2、登录(根据需要)
    3、发出服务请求
    4、退出

    图片.png
    from poplib import POP3
    
    p = POP3('pop.python.is.cool')
    p.user(...)
    p.pass_(...)
    ...
    p.quit()

    3.6 poplib.POP3类方法

    方法 描述
    user(self, user) 向服务器发送登录名,并显示服务器的响应,表示服务器正在等待输入该用户的密码
    pass_(self, pswd) 在用户使用user()登录后,发送password。如果登录失败,则抛出异常
    stat(self) 返回邮件的状态,即一个长度为2的元组,分别表示消息的数量和消息的总大小
    list(self, which) stat()的扩展,从服务器返回以三元组表示的整个消息列表,分别表示为服务器的响应、消息列表、返回消息的大小。
    retr(self, which) 从服务器中得到消息的邮件,并设置其“已读”标志。返回一个长度为3的元组,分别为服务器的响应、消息的邮件的所有行、消息的字节数
    dele(self, which) 将标记的消息删除,大多数服务器在调用quit()后执行删除操作
    quit(self) 注销、提交修改(如处理“已读”和“删除”标记等)、解锁邮箱、终止连接,然后退出

    3.7 客户端程序SMTP和POP3示例

    from smtplib import SMTP
    from poplib import POP3
    from time import sleep
    from email.parser import Parser
    from email.mime.text import MIMEText
    
    
    SMTPSVR = 'smtp.exmail.qq.com'   # SMTP服务器
    POPSVR = 'pop.exmail.qq.com'     # POP服务器
    mail_user = "username"  # 用户名
    mail_pass = "password"  # 密码
    
    sender = 'sender mail'       # 发件人邮箱
    receivers = ['receiver mail']    # 接收人邮箱
    
    content = 'Hello World!'
    title = 'test msg'  # 邮件主题
    message = MIMEText(content, 'plain', 'utf-8')  # 内容, 格式, 编码
    message['From'] = "{}".format(sender)
    message['To'] = ",".join(receivers)
    message['Subject'] = title
    
    sendSvr = SMTP(SMTPSVR)
    sendSvr.login(mail_user, mail_pass)
    errs = sendSvr.sendmail(sender, receivers, message.as_string()) # 发送邮件,sendmail()的第三个参数是电子邮件消息本身
    sendSvr.quit()
    assert len(errs) == 0, errs
    sleep(10)
    
    recvSvr = POP3(POPSVR)
    recvSvr.user(mail_user)
    recvSvr.pass_(mail_pass)
    rsp, msg, siz = recvSvr.retr(recvSvr.stat()[0])  # stat()方法得到可用消息列表,通过[0]符号选中第一条消息;retr()下载这条消息
    msg_content = b'
    '.join(msg).decode('utf-8')
    recvBody = Parser().parsestr(msg_content)

    3.8 Python和IMAP4

    from imaplib import IMAP4
    
    s = IMAP4('imap.python.is.cool')
    s.login(...)
    ...
    s.close()
    s.logout()

    3.9 imaplib.IMAP4类的常用方法

    方法 描述
    close(self) 关闭当前邮箱。如果访问权限不是只读,则本地删除大的邮件在服务器端也会被丢弃
    login(self, user, password) 使用指定的用户名和密码登录
    logout(self) 从服务器注销
    fetch(self, message_set, message_parts) 获取之前由message_set设置的电子邮件消息状态(或使用message_parts获取部分状态信息)
    noop(self) ping服务器,但不产生任行为
    search(self, charset, *criteria) 查询邮箱中至少匹配一块criteria的消息。如果charset为False,则默认使用US-ASCII
    select(self, mailbox, readonly) 选择一个文件夹(默认是INBOX),如果是只读,则不允许用户修改其中的内容
  • 相关阅读:
    UnixTime的时间戳的转换
    dotnet cors 跨域问题
    sqlServer备份和还原语句
    mvc的生命周期
    Java序列化
    js 分页
    jquery js 分页
    Myeclipse 6.0代码
    前序遍历_中序遍历_后序遍历
    数组去重的一些方法以及数组排序
  • 原文地址:https://www.cnblogs.com/CSgarcia/p/9883011.html
Copyright © 2011-2022 走看看