邮件系统包含两部分子系统:用户代理(user agent)和 邮件传输代理(message transfer agent) 。用户代理面向用户,一般指 Outlook 或 Foxmail 等客户端,传输代理为邮件服务器,传输代理之间运行的协议即为 SMTP。
邮件格式
RFC 5322/822
邮件由一个基本的信封(envelope),几个邮件头(header) 和一个邮件体(body)组成。邮件头和邮件体之间由一个空行分隔。这个结构与 HTTP 相似。字段名与值之间由 :
分隔。
信封保存的是传输代理需要的传输控制信息,如目标地址,优先级等;头包含着用户代理需要的控制信息,如 邮件主题;邮件体则直接呈现给用户。
由于 RFC 5322 的前身 RFC 822 并没有将信封字段与头字段明确区分,因此现在的信封字段往往是传输代理自己从用户代理构造的头之中抽取的。也因此信封与头之间并没有空行分隔。信封字段主要含:
To
Cc
(Carbon copy)Bcc
(blind cc)From
Sender
可选字段,当发送人与撰写人不一致时Received
传输路径上每个传输代理节点将自己填入其中Return-Path
由最后一个传输代理添加指示如何返回发件人,一般留空
常用头字段:
Date
Reply-To
回复时发送给谁,可以和 From 不同Message-Id
邮件列表中不同邮件的一个IDIn-Reply-To
当前邮件回复对象的 IDReferences
其他相关邮件的IDKeyords
用户选择的关键字Subject
主题
用户还可以自定义头字段,这些字段以 X-
打头。
一封最简单的,单对单、纯文本邮件可以是这样的:
From: John Doe <jdoe@machine.example>
To: Mary Smith <mary@example.net>
Subject: Saying Hello
Date: Fri, 21 Nov 1997 09:55:06 -0600
Message-ID: <1234@local.machine.example>
This is a message just to say hello.
So, "Hello".
MIME
MIME(Multipurpose Internet Mail Extensions) 将邮件体的格式从纯 ASCII 文本扩展出了结构性。MIME 定义了 5 种新的邮件头:
MIME-Version
Content-Description
描述邮件内容的可读字符串,如 “图片”Content-Id
唯一标识符,与 Message-Id 格式相同Content-Transfer-Encoding
指明传输时如何打包,如是否可直接传递二进制流还是需要 base64 编码Content-Type
邮件内容的类型和格式,类型与子类型间由/
间隔,如 “video/mpeg”
SMTP
SMTP 使用 ASCII 文本进行会话,因此语义十分利于理解。首先客户端发起连接,连接建立后服务器需要报告就绪状态,否则客户端会断开此连接并重试。之后的会话可以在 shell 中手打进行,服务器的返回其实只有 数字码 会被真正关心,后面的文本只是起说明作用,内容也没有硬性约定:
C: HELO abc.com
S: 250 says hello
C: MAIL FROM: <alice@foo.com>
S: 250 sender ok
C: RCPT TO: <bob@bar.com>
S: 250 recipient ok
C: DATA
S: 354 send main; end with "." on a line by itself
C: From: alice@foo.com
C: To: bob@bar.com
C: Subject: Hello
C:
C: world!
C: .
S: 250 message accepted
C: QUIT
S: 221 closing connection
客户端命令被严格限制为 4 个字符,后来为了增加一些如认证、加密和二进制直传等功能,而修订 SMTP,增加了一个扩展。带有扩展的 SMTP 称为 ESMTP。想要使用 ESMTP 的客户端需要将第一次 HELO 命令改为 EHLO 来判断服务器是否接受新的扩展命令。扩展名伶包括:
AUTH
BINARYMIME
CHUNKING
SIZE
STARTTLS
UTF8SMTP
IMAP
IMAP(Internet Mail Access Protocol) 与用于发送邮件的 SMTP 不同,是用户代理从传输代理取用户自己的邮件的主要协议。他是对 POP3(Post Office Protocol version 3) 的改进版。
Python
使用 Python 发邮件主要考虑两个部分:实际的 SMTP 执行和邮件本体的构建。如果只是服务器日志这样的纯文本内容的话,第二步还可以省略。
smtplib
smtplib 包定义了一个 SMTP 客户端会话对象用以向邮件服务器发送邮件。除了这个主体类外,smtplib 里定义的其他对象就只剩一些异常和扩展类(如 ESMTP)了。
class smtplib.SMTP(host='', port=0, local_hostname=None, [timeout, ]source_address=None)
SMTP 对象还支持上下文管理器。
构建 MIME 消息可以使用 email 包,其提供了很多构建/解析邮件的工具。详情略。。。
sample
from smtplib import SMTP
from email.mime.text import MIMEText
with SMTP('smtp.exmail.qq.com', timeout=5) as smtp:
msg = MIMEText('hello, world.')
msg['Subject'] = 'for test'
msg['From'] = 'alice@foo.com'
msg['To'] = 'bob@bar.com'
smtp.login('alice@foo.com', 'password1')
smtp.send_message(msg)