邮件客户端与服务器
Python 标准库
Python 内置了完善的邮件支持:
smtplib:通过 SMTP 发送邮件imaplib:通过 IMAP 读取和管理邮件email:构造和解析邮件(EmailMessage、email.parser)mailbox:读取本地邮箱格式(Maildir、mbox)smtpd:已弃用的模拟 SMTP 服务器(Python 3.12 中已移除)socketserver:底层 TCP 服务器,可用于自行实现模拟 SMTP
发送(smtplib)
import smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg["From"] = "[email protected]"
msg["To"] = "[email protected]"
msg["Subject"] = "Hello"
msg.set_content("Body text here")
with smtplib.SMTP("smtp.example.com", 587) as smtp:
smtp.starttls()
smtp.login("user", "password")
smtp.send_message(msg)
读取(imaplib)
import imaplib, email
with imaplib.IMAP4_SSL("imap.example.com") as imap:
imap.login("user", "password")
imap.select("INBOX")
_, ids = imap.search(None, "ALL")
for uid in ids[0].split():
_, data = imap.fetch(uid, "(RFC822)")
msg = email.message_from_bytes(data[0][1])
print(msg["subject"], msg["from"])
模拟 SMTP 服务器(推荐 aiosmtpd)
from aiosmtpd.controller import Controller
from aiosmtpd.handlers import AsyncMessage
class MyHandler(AsyncMessage):
async def handle_message(self, message):
print(f"Subject: {message['subject']}")
controller = Controller(MyHandler(), hostname="127.0.0.1", port=1025)
controller.start()
JavaScript / TypeScript
JS/TS 没有邮件标准库。这门语言诞生于浏览器,而浏览器禁止直接访问 TCP socket。Node.js 有底层能力(net、tls),但邮件功能一直没有加入核心。
Node.js 提供了什么
| 模块 | 相关用途 |
|---|---|
net |
原始 TCP socket,可以手工实现 SMTP |
tls |
为 SMTPS/IMAPS 提供 TLS 封装 |
crypto |
认证机制所需的哈希计算 |
stream |
处理较大的邮件正文 |
生态中的答案(npm)
| 需求 | 包 |
|---|---|
| 发送 | nodemailer |
| 模拟 SMTP 服务器 | smtp-server(nodemailer 家族) |
| IMAP/接收 | imapflow |
| 解析原始邮件 | mailparser |
邮件会话如何组织
每封邮件都是独立的消息。会话关系是在此之上,通过邮件头建立的:
Message-ID: <[email protected]> ← unique ID for this message
In-Reply-To: <[email protected]> ← ID of the message being replied to
References: <xyz789@...> <abc123@...> ← full chain of ancestor IDs
- 服务器平铺存储各封独立邮件,没有树结构
- 客户端根据这些邮件头构建关系图,重建会话
- 回退方式:缺少
In-Reply-To时,客户端按Subject匹配(去掉Re:、Fwd:等前缀)。这种模糊匹配可能导致错误归组 - Gmail 扩展:添加专有的
X-GM-THRID邮件头,让其 IMAP 服务器直接返回一个会话的全部邮件,无需客户端重建
第三方库与应用(Python)
库
| 库 | 用途 |
|---|---|
aiosmtpd |
现代异步 SMTP 服务器,替代已弃用的 smtpd |
mailbox(标准库) |
读写 Maildir、mbox 格式 |
flanker |
稳健的邮件地址与 MIME 解析 |
imaplib2 |
支持异步、可直接替换标准库 imaplib 的实现 |
本地邮件服务器应用
| 工具 | 用途 |
|---|---|
| Mailpit | 带 Web UI 的轻量 SMTP 捕获工具,仅用于开发/测试 |
| smtp4dev | 与 Mailpit 类似,支持跨平台 |
| Postfix + Dovecot | 生产级 SMTP + IMAP,较重但功能完整 |
| Maildrop | 极简的本地投递 agent |
Mailpit
一个面向开发和测试的轻量 SMTP 捕获工具,以单个二进制文件分发。
它是什么
- 在
localhost:1025接受 SMTP 连接 - 捕获所有邮件,除非明确配置,否则不会投递出去
- 在
localhost:8025提供 Web UI,并提供用于集成测试的 REST API - 还提供可选的 POP3 服务器
发送/中继模式
| 模式 | 行为 |
|---|---|
| 默认 | 捕获邮件,不向任何地方发送 |
| SMTP Relay(手动) | 捕获邮件,再通过 Web UI 手动“释放”到真实 SMTP 服务器 |
| Relay All | 捕获邮件,并通过真实 SMTP 服务器自动转发所有邮件 |
| SMTP Forwarding | 捕获邮件,并自动向固定地址发送副本 |
中继需要配置文件:
host: smtp.gmail.com
port: 587
starttls: true
auth: plain
username: [email protected]
password: your-app-password
Mailpit 不支持什么
- 多个邮箱:无论收件人是谁,捕获的所有邮件都进入同一个共享收件箱。这个功能曾有人提出,但已被以“不计划支持”关闭。绕行办法是,在不同端口运行多个实例。
- 获取外部邮件:Mailpit 没有 IMAP 客户端,无法连接 Gmail 或任何外部服务器,只接收直接发给它的邮件。
Mailpit 不是邮件客户端
它不能用来读取真实收件箱(Gmail、Outlook 等)。要做这些,你需要:
- 真正的 IMAP 客户端库(
imaplib、imapflow) - 或桌面客户端(Thunderbird、Spark 等)
- 或终端客户端(aerc、neomutt)
- 或同步工具(
mbsync、offlineimap),把邮件拉到本地 Maildir