Python核心编程——第3章 因特网客户端编程 笔记

  • 第3章 因特网客户端编程
    • 3.1 文件传输
      • 3.1.1 文件传输协议
      • 3.1.2 Python 和 FTP
      • 3.1.3 ftplib.FTP 类的方法
      • 3.1.4 交互式 FTP 示例
      • 3.1.5 客户端 FTP 程序示例
    • 3.2 网络新闻
      • 3.2.1 Usenet 与新闻组
      • 3.2.2 网络新闻传输协议(NNTP)
    • 3.3 电子邮件
      • 3.3.1 发送电子邮件
      • 3.3.2 Python 和 SMTP
      • 3.3.3 smtplib.SMTP 类方法
      • 3.3.4 交互式 SMTP 示例
      • 3.3.5 POP 和 IMAP
      • 3.3.6 Python 和 POP3
      • 3.3.7 交互式 POP3 示例
      • 3.3.8 poplib.POP3 类方法
      • 3.3.9 客户端程序 SMTP 和 POP3 示例
      • 3.3.10 Python 和 IMAP4
      • 3.3.11 交互式 IMAP4 示例
      • 3.3.12 imaplib.IMAP4 类中的常用方法
    • 3.4 实战
      • 3.4.1 生成电子邮件
      • 3.4.2 解析电子邮件
      • 3.4.3 Yahoo! Mail
      • 3.4.4 Gmail
    • 3.5 相关模块
      • 3.5.1 电子邮件
      • 3.5.2 其他因特网客户端协议

第3章 因特网客户端编程

3.1 文件传输

3.1.1 文件传输协议

文件传输协议(File Transfer Protocol, FTP)
Python核心编程——第3章 因特网客户端编程 笔记_第1张图片
工作流程如下。
1.客户端连接远程主机上的 FTP 服务器。
2.客户端输入用户名和密码(或“anonymous”和电子邮件地址 )。
3.客户端进行各种文件传输和信息查询操作。
4.客户端从远程 FTP 服务器退出,结束传输。

当然,这只是一般情况下的流程。有时,由于网络两边计算机的崩溃或网络的问题,会导致整个传输在完成之前就中断。 如果客户端超过 15 分钟(900 秒) 还没有响应, FTP 连接就会超时并中断。

在底层, FTP 只使用 TCP(见第 2 章),而不使用 UDP。 另外, 可以将 FTP 看作客户端/服务器编程中的特殊情况。 因为这里的客户端和服务器都使用两个套接字来通信:一个是控制和命令端口(21 号端口),另一个是数据端口(有时是 20 号端口)

说“有时”是因为 FTP 有两种模式:主动和被动。只有在主动模式下服务器才使用数据端口。在服务器把 20 号端口设置为数据端口后,它“主动”连接客户端的数据端口。而在被动模式下,服务器只是告诉客户端随机的数据端口号,客户端必须主动建立数据连接。在这种模式下, FTP 服务器在建立数据连接时是“被动”的。最后,现在已经有了一种扩展的被动模式来支持第 6 版本的因特网协议(IPv6)地址

3.1.2 Python 和 FTP

流程:
1.连接到服务器。
2.登录。
3.发出服务请求(希望能得到响应)。
4.退出。
Pytho 伪代码:

from ftplib import FTP
f = FTP('some.ftp.server')
f.login('anonymous', '[email protected]')
:
f.quit()

3.1.3 ftplib.FTP 类的方法

FTP 对象的方法

方 法 描 述
login(user=’anonymous’,passwd=”, acct=”) 登录 FTP 服务器,所有参数都是可选的
pwd() 获得当前工作目录
cwd(path) 把当前工作目录设置为 path 所示的路径
dir ([path[,…[,cb]]) 显示 path 目录里的内容,可选的参数 cb 是一个回调函数,会传递给 retrlines()方法
nlst ([path[,…]) 与 dir()类似, 但返回一个文件名列表,而不是显示这些文件名
retrlines(cmd [, cb]) 给定 FTP命令(如“RETR filename”),用于下载文本文件。可选的回调函数 cb 用于处理文件的每一行
retrbinary(cmd,cb[,bs=8192[, ra]]) 与 retrlines()类似,只是这个指令处理二进制文件。回调函数 cb 用于处理每一块(块大小默认为 8KB)下载的数据
storlines(cmd, f) 给定 FTP 命令(如“STOR filename”),用来上传文本文件。要给定一个文件对象 f
storbinary(cmd, f[,bs=8192]) 与 storlines()类似,只是这个指令处理二进制文件。要给定一个文件对象 f,上传块大小 bs 默认为 8KB
rename(old, new) 把远程文件 old 重命名为 new
delete(path) 删除位于 path 的远程文件
mkd(directory) 创建远程目录
rmd(directory) 删除远程目录
quit() 关闭连接并退出

关于 FTP 对象的更多信息,请参阅
https://docs.python.org/3/library/ftplib.html

3.1.4 交互式 FTP 示例

>>> from ftplib import FTP
>>> f = FTP('ftp.python.org')
>>> f.login('anonymous', '[email protected]')
'230 Guest login ok, access restrictions apply.'
>>> f.dir()
total 38
drwxrwxr-x 10 1075 4127 512 May 17 2000 .
drwxrwxr-x 10 1075 4127 512 May 17 2000 ..
drwxr-xr-x 3 root wheel 512 May 19 1998 bin
drwxr-sr-x 3 root 1400 512 Jun 9 1997 dev
drwxr-xr-x 3 root wheel 512 May 19 1998 etc
lrwxrwxrwx 1 root bin 7 Jun 29 1999 lib -> usr/lib
-r--r--r-- 1 guido 4127 52 Mar 24 2000 motd
drwxrwsr-x 8 1122 4127 512 May 17 2000 pub
drwxr-xr-x 5 root wheel 512 May 19 1998 usr
>>> f.retrlines('RETR motd')
Sun Microsystems Inc. SunOS 5.6 Generic August 1997
'226 Transfer complete.
>>> f.quit()
'221 Goodbye.'

3.1.5 客户端 FTP 程序示例

import ftplib
import os
import socket

# 链接国内好像访问不了
HOST = 'ftp.mozilla.org'
DIRN = 'pub/mozilla.org/webtools'
FILE = 'bugzilla-LATEST.tar.gz'


def main():
    try:
        # 创建一个 FTP 对象,尝试连接到 FTP 服务器
        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:
        # 用“anonymous”登录
        f.login()
    except ftplib.error_perm:
        print('ERROR: cannot login anonymously')
        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' % DIRN)

    try:
        # 下载文件
        f.retrbinary('RETR %s' % FILE, open(FILE, 'wb').write)
    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()

3.2 网络新闻

3.2.1 Usenet 与新闻组

Usenet 新闻系统是一个全球存档的“电子公告板”。各种主题的新闻组一应俱全,从诗歌到政治,从自然语言学到计算机语言,从软件到硬件,从种植到烹饪、招聘/应聘、 音乐、魔术、 相亲等。新闻组可以面向全球,也可以只面向某个特定区域。

整个系统是一个由大量计算机组成的庞大的全球网络,计算机之间共享 Usenet 上的帖子。如果某个用户发了一个帖子到本地的 Usenet 计算机上, 这个帖子会被传播到其他相连的计算机上,再由这些计算机传到与它们相连的计算机上,直到这个帖子传播到了全世界,
每个人都收到这个帖子为止。 帖子在 Usenet 上的存活时间是有限的,这个时间可以由 Usenet系统管理员来指定,也可以为帖子指定一个过期的日期/时间。

每个系统都有一个已“订阅”的新闻组列表, 系统只接收感兴趣的新闻组里的帖子,而不是接收服务器上所有新闻组的帖子。 Usenet 新闻组的内容由提供者安排,很多服务都是公开的。但也有一些服务只允许特定用户使用,例如付费用户、 特定大学的学生等。 Usenet 系统管理员可能会进行一些设置来要求用户输入用户名和密码,管理员也可以设置是否只能上传或只能下载。

Usenet 正在逐渐退出人们的视线,主要被在线论坛替代。

3.2.2 网络新闻传输协议(NNTP)

Python核心编程——第3章 因特网客户端编程 笔记_第2张图片
NNTP 与 FTP 的操作方式相似,但更简单。在FTP 中,登录、传输数据和控制需要使用不同的端口,而 NNTP 只使用一个标准端口 119 来通信。用户向服务器发送一个请求,服务器就做出相应的响应。

关于 NNTP 的更多信息,请参阅
https://docs.python.org/3/library/nntplib.html

3.3 电子邮件

先看看电子邮件系统的各个组件。最重要的组件是消息传输代理(MTA)。这是在邮件交换主机上运行的服务器进程,它负责邮件的路由、队列处理和发送工作。 MTA 就是邮件从发送主机到接收主机所要经过的主机和“跳板”,所以也称为“消息传输”的“代理”。
要让所有这些工作起来, MTA 要知道两件事情:
1)如何找到消息应该到达的下一台MTA;
2)如何与另一台 MTA 通信。
第一件事由域名服务(DNS)来查找目的域名的 MX(Mail Exchange,邮件交换)来完成。查找到的可能不是最终收件人,而可能只是下一个能最终把消息送到目的地的主机。对于第二件事, MTA 怎么把消息转给其他的 MTA 呢?

3.3.1 发送电子邮件

为了发送电子邮件,邮件客户端必须要连接到一个 MTA, MTA 靠某种协议进行通信。MTA之间通过消息传输系统(MTS)互相通信。只有两个 MTA 都使用这个协议时,才能进行通信。在本节开始时就说过,由于以前存在很多不同的计算机系统,每个系统都使用不同的网络软件,因此这种通信很危险,具有不可预知性。更复杂的是,有的计算机使用互连的网络,而有的计算机使用调制解调器拨号,消息的发送时间也是不可预知的。这种复杂性导致了现代电子邮件的基础之一 ——简单邮件传输协议(Simple Mail Transfer Protocol, SMTP)的诞生。

1. SMTP、 ESMTP、 LMTP

SMTP 原先由已故的 Jonathan Postel(加州大学信息学院)创建,记录在 RFC 821 中,于1982 年 8 月公布,其后有一些小修改。在 1995 年 11 月,通过 RFC 1869, SMTP 增加了一些扩展服务(即 EXMTP),现在 STMP 和 ESMTP 都合并到当前的 RFC 5321 中,于 2008 年 10月公布。这里使用 STMP 同时表示 SMTP 和 ESMTP。对于一般的应用,只要能登录服务器、发送邮件、退出即可。这些都很基础。

还有其他的协议,如 LMTP(Local Mail Transfer Protocol,本地邮件传输协议),其基于SMTP 和 ESMTP,作为 RFC 2033 于 1996 年 10 月定义。 SMTP 需要有一个邮件队列,但这需要额外的存储和管理工作。而 LMTP 提供了更轻量级的系统,移除了对邮件队列的需求。但邮件需要立即发送(即不会入队)。 LMTP 服务器不暴露到外面直接与连接到因特网的邮件网关工作,以表示接收还是拒绝一条消息。而网关作为 LMTP 的队列。

2. MTA

一些实现 SMTP 的著名 MTA 包括以下几个。
开源 MTA
• Sendmail
• Postfix
• Exim
• qmail
商业 MTA
• Microsoft Exchange
• Lotus Notes Domino Mail Server

注意,虽然这些都实现了最小的 SMTP 协议需求,但其中大多数,尤其是一些商业 MTA,都在服务器中加入了协议定义之外的特有功能。

SMTP 是在因特网上的 MTA 之间消息交换的最常用 MTSMTA。用 SMTP 把电子邮件从一台(MTA)主机传送到另一台(MTA)主机。 发电子邮件时,必须要连接到一个外部 SMTP服务器,此时邮件程序是一个 SMTP 客户端。而 SMTP 服务器也因此成为消息的第一站。

3.3.2 Python 和 SMTP

过程:
1.连接到服务器。
2.登录(根据需要)。
3.发出服务请求。
4.退出。

Python 伪代码

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

3.3.3 smtplib.SMTP 类方法

SMTP 对象常见的方法

方 法 描 述
sendmail(from, to, msg[, mopts, ropts]) 将 msg 从 from 发送至 to(以列表或元组表示),还可以选择性地设置 ESMTP 邮件(mopts)和收件人(ropts)选项
ehlo()或 helo() 使用 EHLO 或 HELO 初始化 SMTP 或 ESMTP 服务器的会话。这是可选的,因为sendmail()会在需要时自动调用相关内容
starttls(keyfile=None, certfile=None) 让服务器启用 TLS 模式。如果给定了 keyfile 或 certfile,则它们用来创建安全套接字
set_debuglevel(level) 为服务器通信设置调试级别
quit() 关闭连接并退出
login(user, passwd) 使用用户名和密码登录 SMTP 服务器

Python 对 SMTP 的更多支持, 可以阅读 https://docs.python.org/3/library/smtplib.html

3.3.4 交互式 SMTP 示例

>>> from smtplib import SMTP as smtp
>>> s = smtp('smtp.python.is.cool')
>>> s.set_debuglevel(1)
>>> s.sendmail('[email protected]', ('[email protected]',
'[email protected]'), ''' From: [email protected]\r\nTo:
[email protected], [email protected]\r\nSubject: test
msg\r\n\r\nxxx\r\n.''')
send: 'ehlo myMac.local\r\n'
reply: '250-python.is.cool\r\n'
reply: '250-7BIT\r\n'
reply: '250-8BITMIME\r\n'
reply: '250-AUTH CRAM-MD5 LOGIN PLAIN\r\n'
reply: '250-DSN\r\n'
reply: '250-EXPN\r\n'
reply: '250-HELP\r\n'
reply: '250-NOOP\r\n'
reply: '250-PIPELINING\r\n'
reply: '250-SIZE 15728640\r\n'
reply: '250-STARTTLS\r\n'
reply: '250-VERS V05.00c++\r\n'
reply: '250 XMVP 2\r\n'
reply: retcode (250); Msg: python.is.cool
7BIT
8BITMIME
AUTH CRAM-MD5 LOGIN PLAIN
DSN
EXPN
HELP
NOOP
PIPELINING
SIZE 15728640
STARTTLS
VERS V05.00c++
XMVP 2
send: 'mail FROM: size=108\r\n'
reply: '250 ok\r\n'
reply: retcode (250); Msg: ok
send: 'rcpt TO:\r\n'
reply: '250 ok\r\n'
reply: retcode (250); Msg: ok
send: 'data\r\n'
reply: '354 ok\r\n'
reply: retcode (354); Msg: ok
data: (354, 'ok')
send: 'From: [email protected]\r\nTo:
[email protected]\r\nSubject: test msg\r\n\r\nxxx\r\n..\r\n.\r\n'
reply: '250 ok ; id=2005122623583701300or7hhe\r\n'
reply: retcode (250); Msg: ok ; id=2005122623583701300or7hhe
data: (250, 'ok ; id=2005122623583701300or7hhe')
{}
>>> s.quit()
send: 'quit\r\n'
reply: '221 python.is.cool\r\n'
reply: retcode (221); Msg: python.is.cool

3.3.5 POP 和 IMAP

第一个用于下载邮件的协议称为邮局协议(Post Office Protocal, POP),“邮局协议(POP)的目的是让用户的工作站可以访问邮箱服务器里的邮件,并在工作站中。通过简单邮件传输协议(SMTP)将邮件发送到邮件服务器”。 POP协议的最新版本是第 3 版,也称为 POP3。在 POP 出现几年之后有了一个与之竞争的协议,即因特网消息访问协议(Internet
Message Access Protocol, IMAP)。 IMAP 还有其他名称,如“因特网邮件访问协议”、“交互式邮件访问协议”、“临时邮件访问协议”。
IMAP 旨在提供比 POP 更完整的解决方案,但它也因此比 POP 更复杂。例如, IMAP 非常适合今天的需要,因为用户需要通过不同的设备使用电子邮件,如台式机、笔记本电脑、平板电脑、手机、视频游戏系统等。 POP 无法很好地应对多邮件客户端,尽管 POP 应用依然广泛,但大部分情况下已经被废弃了。

当前广泛使用的版本是 IMAP4rev1。本书中的 IMAP4 同时表示 IMAP4 和 IMAP4rev1 协议。

3.3.6 Python 和 POP3

流程:
1.连接到服务器。
2.登录。
3.发出服务请求。
4.退出。

Python核心编程——第3章 因特网客户端编程 笔记_第3张图片

Python 伪代码如下

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

3.3.7 交互式 POP3 示例

>>> from poplib import POP3
>>> p = POP3('pop.python.is.cool')
>>> p.user('wesley')
'+OK'
>>> p.pass_("you'llNeverGuess")
Traceback (most recent call last):
File "", line 1, in ?
File "/usr/local/lib/python2.4/poplib.py", line 202,
in pass_
return self._shortcmd('PASS %s' % pswd)
File "/usr/local/lib/python2.4/poplib.py", line 165,
in _shortcmd
return self._getresp()
File "/usr/local/lib/python2.4/poplib.py", line 141,
in _getresp
raise error_proto(resp)
poplib.error_proto: -ERR directory status: BAD PASSWORD
>>> p.user('wesley')
'+OK'
>>> p.pass_('youllNeverGuess')
'+OK ready'
>>> p.stat()
(102, 2023455)
>>> rsp, msg, siz = p.retr(102)
>>> rsp, siz
('+OK', 480)
>>> for eachLine in msg:
... print eachLine
...
Date: Mon, 26 Dec 2005 23:58:38 +0000 (GMT)
Received: from c-42-32-25-43.smtp.python.is.cool
by python.is.cool (scmrch31) with ESMTP
id <2005122623583701300or7hhe>; Mon, 26 Dec 2005 23:58:37
+0000
From: [email protected]
To: [email protected]
Subject: test msg
xxx
.
>>> p.quit()
'+OK python.is.cool'

3.3.8 poplib.POP3 类方法

在登录时, user()方法不仅向服务器发送用户名,还会等待并显示服务器的响应,表示服务器正在等待输入该用户的密码。 如果 pass_()方法验证失败,会引发一个 poplib.error_proto异常。如果成功,会得到一个以“+”号开头的应答消息,如“+OK ready”,然后锁定服务器上的这个邮箱,直到调用 quit()方法为止。

POP3 对象的常用方法

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

3.3.9 客户端程序 SMTP 和 POP3 示例

from smtplib import SMTP
from poplib import POP3
from time import sleep

SMTPSVR = 'smtp.python.is.cool'
POP3SVR = 'pop.python.is.cool'

who = '[email protected]'
body = '''\
From: %(who)s
To: %(who)s
Subject: test msg
Hello World!
''' % {'who': who}

sendSvr = SMTP(SMTPSVR)
errs = sendSvr.sendmail(who, [who], body)
sendSvr.quit()

assert len(errs) == 0, errs
sleep(10)  # 等待10秒

recSvr = POP3(POP3SVR)
recSvr.user('wesley')
recSvr.pass_('youllNeverGuss')
rsp, msg, siz = recSvr.retr(recSvr.stat()[0])
sep = msg.index('')
recvBody = msg[sep + 1:]
assert body == recvBody  # assert identical

3.3.10 Python 和 IMAP4

流程与之前一样:
1.连接到服务器。
2.登录。
3.发出服务请求。
4.退出。

Python 伪代码:

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

3.3.11 交互式 IMAP4 示例

>>> s = IMAP4('imap.python.is.cool') # default port: 143
>>> s.login('wesley', 'youllneverguess')
('OK', ['LOGIN completed'])
>>> rsp, msgs = s.select('INBOX', True)
>>> rsp
>'OK'
>>> msgs
['98']
>>> rsp, data = s.fetch(msgs[0], '(RFC822)')
>>> rsp
'OK'
>>> for line in data[0][1].splitlines()[:5]:
... print line
...
Received: from mail.google.com
by mx.python.is.cool (Internet Inbound) with ESMTP id
316ED380000ED
for ; Fri, 11 Mar 2011 10:49:06 -0500 (EST)
Received: by gyb11 with SMTP id 11so125539gyb.10
for ; Fri, 11 Mar 2011 07:49:03 -0800
(PST)
>>> s.close()
('OK', ['CLOSE completed'])
>>> s.logout()
('BYE', ['IMAP4rev1 Server logging out'])

3.3.12 imaplib.IMAP4 类中的常用方法

IMAP4 对象的常见方法

方 法 描 述
close() 关闭当前邮箱。如果访问权限不是只读,则本地删除的邮件在服务器端也会被丢弃
fetch(message_set, message_parts) 获取之前由 message_set 设置的电子邮件消息状态(或使用message_parts 获取部分状态信息)
login(user, password) 使用指定的用户名和密码登录
logout() 从服务器注销
noop() ping 服务器,但不产生任何行为
search(charset, *criteria) 查询邮箱中至少匹配一块 criteria 的消息。如果 charset 为 False,则默认使用 US-ASCII
select(mailbox= ‘INBOX ‘, read-only=False) 选择一个文件夹(默认是 INBOX),如果是只读,则不允许用户修改其中的内容

面是一些使用这些方法的示例。
• NOP、 NOOP 或“no operation”。这些内容表示与服务器保持连接状态。

>>> s.noop()
('OK', ['NOOP completed'])

获取某条消息的相关信息。

>>> rsp, data = s.fetch('98', '(BODY)')
>>> data[0]
'98 (BODY ("TEXT" "PLAIN" ("CHARSET" "ISO-8859-1" "FORMAT" "flowed"
"DELSP" "yes") NIL NIL "7BIT" 1267 33))'

获取某条消息的头。

>>> rsp, data = s.fetch('98', '(BODY[HEADER])')
>>> data[0][1][:45]
'Received: from mail-gy.google.com (mail-gy.go')

获取所有已读消息的 ID(也可以尝试使用“ALL”、“NEW”等)。

>>> s.search(None, 'SEEN')
('OK', ['1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 59 60 61 62
63 64 97'])

获取多条消息(使用冒号分隔,注意,右圆括号用来分隔结果)。

>>> rsp, data = s.fetch('98:100', '(BODY[TEXT])')
>>> data[0][1][:45]
'Welcome to Google Accounts. To activate your'
>>> data[2][1][:45]
'\r\n-b1_aeb1ac91493d87ea4f2aa7209f56f909\r\nCont'
>>> data[4][1][:45]
'This is a multi-part message in MIME format.'
>>> data[1], data[3], data[5]
(')', ')', ')')

3.4 实战

3.4.1 生成电子邮件

from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from smtplib import SMTP


# multipart alternative: text and html
def make_mpa_msg():
    email = MIMEMultipart('alternative')
    text = MIMEText('Hello World!\r\n', 'plain')
    email.attach(text)
    html = MIMEText(
        '

Hello World!

'
'', 'html') email.attach(html) return email # nultipart: images def make_img_msg(fn): f = open(fn, 'r') data = f.read() f.close() email = MIMEImage(data, name=fn) email.add_header('Content-Disposition', 'attachment:filename="%s"' % fn) return email def sendMsg(fr, to, msg): s = SMTP('localhost') errs = s.sendmail(fr, to, msg) s.quit() if __name__ == '__main__': SENDER = '' # todo 补全 RECIPS = [] # todo 补全 SOME_IMG_FILE = '' # todo 补全 print('Sending multipart alternative msg...') msg = make_mpa_msg() msg['From'] = SENDER msg['To'] = ', '.join(RECIPS) msg['Subject'] = 'multipart alternative test' sendMsg(SENDER, RECIPS, msg.as_string()) print('Sending image msg...') msg = make_img_msg(SOME_IMG_FILE) msg['From'] = SENDER msg['To'] = ', '.join(RECIPS) msg['Subject'] = 'image file test' sendMsg(SENDER, RECIPS, msg.as_string())

3.4.2 解析电子邮件

import email


def processMsg(entire_msg):
    body = ''
    # 解析消息
    msg = email.message_from_string(entire_msg)
    if msg.is_multipart():
        # 遍历消息的附件
        for part in msg.walk():
            # 获得正确 MIME 类型
            if part.get_content_type() == 'text/plain':
                body = part.get_payload()
                break
            else:
                body = msg.get_payload(decode=True)
    else:
        body = msg.get_payload(decode=True)
    return body
# msg.get_payload():从消息正文中获取特定的部分。通常 decode 标记会设为 True,即邮件正文根据每个 Content-Transfer-Encoding 头解码。

3.4.3 Yahoo! Mail

Yahoo! Mail SMTP、 POP、 IMAP 示例

from io import StringIO
from imaplib import IMAP4_SSL
from platform import python_version
from poplib import POP3_SSL, error_proto
from socket import error

# SMTP_SSL added in 2.6
release = python_version()
if release > '2.6.2':
    from smtplib import SMTP_SSL, SMTPServerDisconnected  # fixed in 2.6.3
else:
    SMTP_SSL = None

from secret import *  # provides MAILBOX, PASSWD

who = "%[email protected]" % MAILBOX
from_ = who
to = [who]

headers = [
    'From %s' % from_,
    'To: %s' % ', '.join(to),
    'Subject: test SMTP send via 587/TLS'
]

body = ['Hello', 'World!']

msg_to_be_sent = '\r\n\r\n'.join(('\r\n'.join(headers), '\r\n'.join(body)))


def getSubject(msg, default='(no subject line)'):
    """
        getSubject(msg) - iterate over 'msg' looking for Subject line;
        return if found otherwise 'default'
    """
    for line in msg:
        if line.startswith('Subject:'):
            return line.rstrip()
        if not line:
            return default
    return default


# SMTP/TLS
print("*** Doing SMTP send via TLS...")
if SMTP_SSL:
    try:
        s = SMTP_SSL('smtp.mail.yahoo.com', 465)
        s.login(MAILBOX, PASSWD)
        s.sendmail(from_, to, msg)
        s.quit()
        print(' SSL mail send!')
    except SMTPServerDisconnected:
        print(' error: server unexpectedly dis connected... try again')
else:
    print(' error: SMTP_SSL requires 2.6.3+')
# POP
print('*** Doing POP recv...')
try:
    s = POP3_SSL('pop.gmail.com', 995)
    s.user(MAILBOX)
    s.pass_(PASSWD)
    rv, msg, sz = s.retr(s.stat()[0])
    s.quit()
    line = getSubject(msg)
    print('    Received msg via POP: %r' % line)
except error_proto:
    print(' error: POP for Yahoo!Mail Plus subscribers only')

# IMAP
print('*** Doing IMAP recv...')
try:
    s = IMAP4_SSL('imap.gmail.com', 993)
    s.login(MAILBOX, PASSWD)
    rsp, msgs = s.select('INBOX', True)
    rsp, data = s.fetch(msgs[0], '(RFC822)')
    line = getSubject(StringIO(data[0][1]))
    s.close()
    s.logout()
    print('    Received msg via IMAP: %r' % line)
except error:
    print(' error: IMAP for Yahoo!Mail Plus subscribers only')

3.4.4 Gmail

Gmail 的 SMTP/TLS、 SMTL/SSL、 POP、 IMAP 示例

from io import StringIO
from imaplib import IMAP4_SSL
from platform import python_version
from poplib import POP3_SSL
from smtplib import SMTP

# SMTP_SSL added in 2.6
release = python_version()
if release > '2.6.2':
    from smtplib import SMTP_SSL  # fixed in 2.6.3
else:
    SMTP_SSL = None

from secret import *  # provides MAILBOX, PASSWD

who = "%[email protected]" % MAILBOX
from_ = who
to = [who]

headers = [
    'From %s' % from_,
    'To: %s' % ', '.join(to),
    'Subject: test SMTP send via 587/TLS'
]

body = ['Hello', 'World!']

msg_to_be_sent = '\r\n\r\n'.join(('\r\n'.join(headers), '\r\n'.join(body)))


def getSubject(msg, default='(no subject line)'):
    """
        getSubject(msg) - iterate over 'msg' looking for Subject line;
        return if found otherwise 'default'
    """
    for line in msg:
        if line.startswith('Subject:'):
            return line.rstrip()
        if not line:
            return default
    return default
# SMTP/TLS
print("*** Doing SMTP send via TLS...")
s = SMTP('smtp.gmail.com', 587)
if release < '2.6':
    s.ehlo()  # required in older releases
s.starttls()
if release < '2.5':
    s.ehlo  # required in older releases
s.login(MAILBOX, PASSWD)
s.sendmail(from_, to, msg_to_be_sent)
s.quit()
print('    TLS mail sent!')

# POP
print('*** Doing POP recv...')
s = POP3_SSL('pop.gmail.com', 995)
s.user(MAILBOX)
s.pass_(PASSWD)
rv, msg, sz = s.retr(s.stat()[0])
s.quit()
line = getSubject(msg)
print('    Received msg via POP: %r' % line)

msg_to_be_sent = msg_to_be_sent.replace('587/TLS', '465/SSL')

# SMTP/SSL
if SMTP_SSL:
    print('*** Doing SMTP send via SSL')
    s = SMTP_SSL('smtp.gmail.com', 465)
    s.login(MAILBOX, PASSWD)
    s.sendmail(from_, to, msg_to_be_sent)
    s.quit()
    print('    SSL mail sent!')

# IMAP
print('*** Doing IMAP recv...')
s = IMAP4_SSL('imap.gmail.com', 993)
s.login(MAILBOX, PASSWD)
rsp, msgs = s.select('INBOX', True)
rsp, data = s.fetch(msgs[0], '(RFC822)')
line=getSubject(StringIO(data[0][1]))
s.close()
s.logout()
print('    Received msg via IMAP: %r' % line)

3.5 相关模块

3.5.1 电子邮件

电子邮件相关模块

模 块 包 描 述
email 用于处理电子邮件的包(也支持 MIME)
smtpd SMTP 服务器
base64 Base-16、 32、 64 数据编码(RFC 3548)
mhlib 处理 MH 文件夹和消息的类
mailbox 支持 mailbox 文件格式解析的类
mailcap “mailcap”文件的处理模块
mimetools (废弃) MIME 消息解析工具(使用上面的 email 模块)
mimetypes 在文件名/URL 和相关的 MIME 类型之间转换的模块
MimeWriter (废弃) MIME 消息处理模块(使用上面的 email 模块)
mimify (废弃) MIME 消息处理工具(使用上面的 email 模块)
quopri 对 MIME 中引号括起来的可打印数据进行编码或解码
binascii 二进制和 ASCII 转换
binhex Binhex4 编码和解码支持

3.5.2 其他因特网客户端协议

因特网客户端协议相关模块

模 块 描 述
ftplib FTP 协议客户端
xmlrpclib XML-RPC 协议客户端
httplib HTTP 和 HTTPS 协议客户端
imaplib IMAP4 协议客户端
nntplib NNTP 协议客户端
poplib POP3 协议客户端
smtplib SMTP 协议客户端

(^▽^)

你可能感兴趣的:(《Python核心编程》笔记)