登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  python教程

Python mailbox.get_bytes 怎样控制是否保留信封头

来源:17golang原创

时间:2026-11-10 07:50:39 309浏览 收藏

如果你用 Python 读取 mbox 邮件,关键不是改写返回结果,而是在调用 mbox.get_bytes() 时选择 from_ 参数:from_=False(默认值)会去掉每条记录最前面的 Unix From 信封行,from_=True 则把它保留下来。邮件头里的 From: 与这条信封行不是同一个字段。

官方地址:https://docs.python.org/3/library/mailbox.html

本文用一个最小 mbox 项目生成测试邮件、读取同一个 key,并打印两种字节结果。需要保留原始 mbox 记录边界时选 True;只想交给 email 解析器处理标准邮件头和正文时,通常选默认的 False。

先分清 mbox 信封行和邮件头

mbox 文件中的一条记录通常以 Unix “From ” 行开始,例如 From alice@example.com Tue Nov 10 10:00:00 2026。它是 mbox 存储格式用来分隔记录的信封信息,不是 RFC 5322 邮件头;真正的邮件头从 Subject:、Date:、From: 这类字段开始。

mailbox.Mailbox.get_bytes(key) 返回邮件的字节表示。对 mailbox.mbox 来说,方法多了一个 from_ 参数:默认不返回首行,传入 True 才保留首行。这个差异适合用字节首行直接观察,而不要用邮件头字典去判断。

mbox 文件经 get_bytes from_ 参数分成两种字节结果的结构图
图1:mbox 信封行、邮件头和正文的读取关系,这是静态结构说明图,不是运行截图。

准备一个可重复运行的小项目

先创建一个临时目录,再用 mailbox.mbox 写入一封测试邮件。示例显式调用 lock()、flush() 和 close(),这样不会把“正在写入”和“正在读取”的状态混在一起。

from email.message import EmailMessage
from pathlib import Path
import mailbox

# 使用项目目录中的 mbox 文件,重复运行前先清理旧测试数据。
mbox_path = Path("demo-mailbox.mbox")
if mbox_path.exists():
    mbox_path.unlink()

# 组装一封最小邮件,便于后面比较返回字节的首行。
message = EmailMessage()
message["From"] = "alice@example.com"
message["To"] = "bob@example.com"
message["Subject"] = "mailbox get_bytes demo"
message.set_content("保留或去掉 mbox 信封行都不应改变正文。")

# 写入 mbox 后刷新并关闭,确保读取阶段看到完整记录。
box = mailbox.mbox(mbox_path, create=True)
box.lock()
try:
    box.add(message)
    box.flush()
finally:
    box.close()

print(f"测试文件:{mbox_path}")

这段代码只负责准备输入。mbox.add() 返回的 key 会在读取阶段重新从 mailbox 中取得,不要假设 key 永远是字符串或从零开始的整数。

用 from_=False 去掉信封行

读取时先使用默认值。这里把返回结果按行拆开,只展示前几行并标出首行;由于 from_=False,第一行应该已经是邮件头,而不是以大写 From 开头的 mbox 分隔行。

import mailbox
from pathlib import Path

# 以只读方式打开准备好的 mbox,key 从实际内容中取得。
box = mailbox.mbox(Path("demo-mailbox.mbox"), create=False)
try:
    key = next(iter(box))

    # 默认 from_=False:去掉 mbox 记录顶部的 Unix From 信封行。
    raw_without_envelope = box.get_bytes(key, from_=False)
    first_line = raw_without_envelope.splitlines()[0].decode("ascii", "replace")
    print(f"首行:{first_line}")
finally:
    # 读取完成后关闭文件句柄;不需要写锁时不要额外修改 mailbox。
    box.close()
首行:From: alice@example.com

这里打印的 From: 是邮件头,保留它是解析发件人信息所需要的;被去掉的是 mbox 存储层的 From 信封行。若你的下游代码使用 email.parser.BytesParser,通常把默认结果交给解析器就够了。

用 from_=True 保留原始信封行

当目标是导出 mbox 原始记录、保留记录边界,或者把字节交给仍然期待 Unix From 行的下游工具时,传入 from_=True。下面把两种结果的首行放在一起比较。

import mailbox
from pathlib import Path

# 同一个 key 读取两次,只改变 from_,方便比较存储层首行。
box = mailbox.mbox(Path("demo-mailbox.mbox"), create=False)
try:
    key = next(iter(box))
    raw_without_envelope = box.get_bytes(key, from_=False)
    raw_with_envelope = box.get_bytes(key, from_=True)

    # 用 replace 处理不可见的回车换行,输出只承担验收作用。
    line_without = raw_without_envelope.splitlines()[0].decode("ascii", "replace")
    line_with = raw_with_envelope.splitlines()[0].decode("ascii", "replace")
    print("from_=False:", line_without)
    print("from_=True :", line_with)
finally:
    # get_bytes 不会替代 mailbox 的生命周期管理,读取结束仍要关闭对象。
    box.close()
from_=False: From: alice@example.com
from_=True : From alice@example.com Tue Nov 10 10:00:00 2026

两份结果的邮件头和正文内容相同,区别集中在最前面的信封行。实际时间字符串由 mbox 写入过程决定,示例输出只用于说明形状,不应把时间当成业务字段。

from_ false 与 true 返回字节首行差异的对比说明图
图2:两种参数只在 mbox 顶部信封行的保留与否上产生差异,这是静态结果说明图,不是运行截图。

按场景选择参数,不要混淆邮件格式

场景建议调用原因
解析邮件头和正文get_bytes(key)默认去掉存储层信封行,输入更接近标准邮件内容。
导出一条完整 mbox 记录get_bytes(key, from_=True)保留记录分隔所需的 Unix “From ” 首行。
使用 Maildir、MH 等 mailboxget_bytes(key)不要把 mbox 的额外参数直接套到所有 Mailbox 子类。

官方文档把 from_ 标为 mbox.get_bytes() 的额外参数;mailbox.MMDF 也提供同类控制。其他 mailbox 类型的 get_bytes() 签名并不保证接受这个参数,所以跨格式代码应把是否保留信封行作为 mbox/MMDF 分支处理。

运行验收和常见问题

把前面的代码保存为 mailbox_get_bytes_demo.py 后,在空目录运行:

# 运行最小项目,先生成 mbox,再比较两种 from_ 结果。
python3 mailbox_get_bytes_demo.py

验收只看两个事实:from_=False 的首行应是 From: 等邮件头,from_=True 的首行应是以 From 开头的信封行。如果调用的 key 不存在,官方 API 会抛出 KeyError,这说明读取对象和 key 生命周期需要重新对应,不是 from_ 参数失效。

为什么看到两个 From?

From: 是邮件头字段,From (没有冒号)是 mbox 记录的 Unix 信封行。它们可以同时存在,不能只按单词相同就认为是重复字段。

能不能把 from_ 传给 mailbox.Maildir?

不要这样写。from_ 是 mbox/MMDF 针对存储格式提供的额外参数;对 Maildir 使用通用的 get_bytes(key),需要原始格式时再按对应 mailbox 类型的文档设计处理。

读取后还要 flush 吗?

纯读取不需要为了 get_bytes() 额外调用 flush(),但打开对象应在结束时 close()。只有在你对 mailbox 做了新增、删除或修改并希望落盘时,才按写入流程加锁、刷新和关闭。

小结

Python mailbox.get_bytes() 控制信封头的答案就是 mbox 的 from_ 参数:默认 False 去掉 Unix “From ” 行,True 保留它。把这个选择放在“标准邮件解析”与“原始 mbox 记录导出”的边界上,就能避免把存储格式信息误当成邮件头,也能让跨 mailbox 类型的代码更稳妥。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>