电子邮件

概述

MUA:Mail User Agent——邮件用户代理

MTA:Mail Transfer Agent——邮件传输代理,Email服务提供商(网易、新浪…)

MDA:Mail Delivery Agent——邮件投递代理,Email到达MDA后,就静静地躺在Email服务提供商的某个服务器上,存放在某个文件或特殊的数据库里,这个长期保存邮件的地方称之为电子邮箱。

一封电子邮件的旅程:

1
发件人 -> MUA -> MTA -> MTA -> 若干个MTA -> MDA <- MUA <- 收件人

要编写程序来发送和接收邮件,本质上就是:

  1. 编写MUA把邮件发到MTA;
  2. 编写MUA从MDA上收邮件。

发邮件时,MUA和MTA使用的协议就是SMTP:Simple Mail Transfer Protocol,后面的MTA到另一个MTA也是用SMTP协议。

收邮件时,MUA和MDA使用的协议有两种:POP:Post Office Protocol,目前版本是3,俗称POP3;IMAP:Internet Message Access Protocol,目前版本是4,优点是不但能取邮件,还可以直接操作MDA上存储的邮件,比如从收件箱移到垃圾箱,等等。


示例

SMTP——发送:mail_send.py

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
import smtplib                          # SMTP协议客户端,负责连接服务器、发送
from email.mime.text import MIMEText # 构建纯文本/HTML邮件正文
from email.header import Header # 处理邮件头部的中文编码
from email.utils import formataddr # 格式化"昵称 <邮箱>"的显示名

from_addr = 'me@163.com'
password = 'UKd9g3eQrF9PQa' # 163授权码(非登录密码)
to_addr = 'me@qq.com'
smtp_server = 'smtp.163.com' # 163的发信服务器地址

# 构建邮件
msg = MIMEText('hello, send by Python', 'plain', 'utf-8')
msg['From'] = formataddr(('发件人007', from_addr))
msg['To'] = formataddr(('收件人009', to_addr))
msg['Subject'] = Header('测试邮件标题', 'utf-8')

# 发送邮件
try:
server = smtplib.SMTP_SSL(smtp_server, 465) # 使用 SSL 465端口
server.set_debuglevel(1) # 打印所有SMTP通信日志(可上线后删掉)
server.login(from_addr, password)
server.sendmail(from_addr, [to_addr], msg.as_string())
print("✅ 发送成功")
except Exception as e:
print(f"❌ 发送失败: {e}") # 捕获所有异常并打印
finally:
server.quit() # 无论成功失败都断开连接,发送 QUIT 命令

POP——接收:mail_receive.py

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import poplib
from email.parser import BytesParser
from email.header import decode_header
from email.utils import parseaddr

# 配置
mail_host = 'pop.qq.com'
mail_user = 'me@qq.com'
mail_pass = 'zozyyffjbfdhhc'

def decode_str(s):
"""解码邮件头部中文"""
if not s:
return ''
value, charset = decode_header(s)[0]
if isinstance(value, bytes):
return value.decode(charset or 'utf-8')
return value

def decode_addr(s):
"""解码显示名 + 保留邮箱地址,输出:名字 <邮箱>"""
if not s:
return ''
name, addr = parseaddr(s)
decoded_name = decode_str(name)
if decoded_name:
return f"{decoded_name} <{addr}>"
return addr

# 收取邮件
server = None
try:
server = poplib.POP3_SSL(mail_host, 995) # 使用 SSL 995端口
server.set_debuglevel(1) # 打印POP3通信日志(上线后可删)
server.user(mail_user)
server.pass_(mail_pass)

msg_count, mailbox_size = server.stat()
print(f"📬 共 {msg_count} 封邮件,总大小 {mailbox_size} 字节\n")

for i in range(1, msg_count + 1):
print(f"{'='*40}")
print(f"📧 第 {i} 封邮件")

resp, lines, octets = server.retr(i)
msg = BytesParser().parsebytes(b'\r\n'.join(lines))

print(f"主题:{decode_str(msg['Subject'])}")
print(f"发件人:{decode_addr(msg['From'])}")
print(f"收件人:{decode_addr(msg['To'])}")
print(f"时间:{msg['Date']}")

# 提取纯文本正文
if msg.is_multipart():
for part in msg.walk():
if part.get_content_type() == 'text/plain':
charset = part.get_content_charset() or 'utf-8'
print(f"正文:\n{part.get_payload(decode=True).decode(charset)}")
else:
charset = msg.get_content_charset() or 'utf-8'
print(f"正文:\n{msg.get_payload(decode=True).decode(charset)}")

print()

except Exception as e:
print(f"❌ 收取失败: {e}")
finally:
if server:
server.quit() # 无论成功失败都断开连接

摘自:https://liaoxuefeng.com/books/python/email/index.html


电子邮件
https://76-s.github.io/2026/05/20/mail/
作者
76
发布于
2026年5月20日
许可协议