Python email.headerregistry.Address 怎样区分邮箱显示名与地址
来源:17golang原创
时间:2026-10-27 13:44:41 308浏览 收藏
处理邮件头时,最容易混淆的是“别人看到的名字”和“真正用于投递的地址”。email.headerregistry.Address 把这两部分拆成了不同属性:display_name 是显示名,username 与 domain 组成邮箱主体,addr_spec 则是适合放进裸地址位置的完整字符串。
官方文档:https://docs.python.org/3/library/email/headerregistry.html
实际项目中可以先记住一个判断:要展示联系人,用 display_name;要拿实际邮箱,用 addr_spec;要批量处理 To、Cc 等字段,先访问地址头的 addresses。下面用一个解析场景把这几个属性串起来。
Address 的四个字段分别表示什么
假设地址写成 张三 。它不是一个需要手工切分的普通字符串,而是一个结构化的 Address 对象:
| 属性 | 含义 | 适合的用途 |
|---|---|---|
display_name | 显示名,去掉了外层引号 | 联系人列表、邮件 UI |
username | @ 左侧的用户名部分 | 需要拆分地址时使用 |
domain | @ 右侧的域名部分 | 域名统计或路由判断 |
addr_spec | 可作为裸地址使用的 username@domain | 去重、存储和下游接口 |
str(address) 表示符合邮件地址规则的完整形式,可能包含显示名;address.addr_spec 则只保留地址主体。两者都不是简单地对原始字符串做截取。

单地址头怎样取出显示名和实际地址
从 From 读取单个发件人时,默认邮件策略会把结构化头部交给 headerregistry。单地址头可以通过 address 取得一个 Address 对象,然后分别读取属性:
from email import policy
from email.parser import Parser
# 使用默认策略,让地址头返回结构化对象,而不是只返回普通字符串。
raw_message = "From: 张三 \nSubject: 测试\n\n正文"
message = Parser(policy=policy.default).parsestr(raw_message)
# From 是单地址场景,address 对象包含显示名和实际地址。
sender = message["From"].address
print(sender.display_name) # 张三:给读者看的名称
print(sender.addr_spec) # zhangsan@example.com:实际地址
print(sender.username) # zhangsan:@ 左侧
print(sender.domain) # example.com:@ 右侧
这里不要把 str(message["From"]) 当成邮箱地址保存,因为它可能包含显示名,也可能带有邮件头的格式化结果。需要给数据库、去重逻辑或下游服务使用时,通常应选择 sender.addr_spec。
多地址头为什么要遍历 addresses
To、Cc 可能包含多个地址,甚至包含 RFC 5322 的地址组。此时应该读取地址头的 addresses,而不是只取第一个对象。这个属性返回一个地址元组;如果原始头部含有组,组中的单个地址也会按出现位置展平到这个元组中。
from email import policy
from email.parser import Parser
# To 头包含两个独立地址,演示批量读取时保留显示名与裸地址的区别。
raw_message = "To: 张三 , li@example.net\n\n"
message = Parser(policy=policy.default).parsestr(raw_message)
# 多地址头使用 addresses;不要假设 address 属性代表整个列表。
for recipient in message["To"].addresses:
# 空显示名是正常情况,此时可以只展示 addr_spec。
label = recipient.display_name or recipient.addr_spec
print(f"{label} => {recipient.addr_spec}")

如果只关心实际收件地址,可以在循环里统一取 recipient.addr_spec;如果要生成联系人展示文本,再组合 recipient.display_name 和 recipient.addr_spec。这样可以避免把没有显示名的地址误判成解析失败。
构造 Address 时怎样选择参数
构造对象有两种常见写法。已知完整地址时,可以直接传 addr_spec,库会解析出 username 和 domain;如果已经分别拿到了各部分,也可以显式传 display_name、username 和 domain。
from email.headerregistry import Address
# 已经拿到完整地址时,让 Address 负责拆出 username 和 domain。
address_from_spec = Address(display_name="李四", addr_spec="lisi@example.org")
print(address_from_spec.display_name) # 李四
print(address_from_spec.addr_spec) # lisi@example.org
# 已经拆开字段时,可以明确传入用户名和域名。
address_from_parts = Address(
display_name="王五", # 显示给收件人的名字
username="wangwu", # @ 左侧用户名
domain="example.org", # @ 右侧域名
)
print(str(address_from_parts)) # 王五
addr_spec 是只读属性;如果需要改变邮箱主体,应创建新的 Address 对象,而不是试图给旧对象的 addr_spec 重新赋值。地址中的 Unicode 显示名可以被正确表示,但用户名部分仍需符合邮件地址语法,不能把任意 Unicode 文本直接当作用户名。
几个容易写错的判断
- 把显示名当成地址:“张三”只是
display_name,真正地址在addr_spec。 - 用字符串切分邮箱:带引号、编码词或地址组时,手工按尖括号和
@切分很脆弱,优先使用结构化属性。 - 多地址头只取一个:
To和Cc先看addresses,不要把单地址头的读取方式套到列表上。 - 把空显示名当成异常:没有显示名时,
display_name为空字符串是正常语义。
一段速查式完整写法
from email import policy
from email.parser import Parser
# 统一把收件人头部转换成可存储的裸地址记录。
raw_message = "To: 张三 , li@example.net\n\n"
message = Parser(policy=policy.default).parsestr(raw_message)
records = []
for address in message["To"].addresses:
# display_name 为空时回退到 addr_spec,保证展示字段非空。
records.append({
"display_name": address.display_name,
"username": address.username,
"domain": address.domain,
"addr_spec": address.addr_spec,
"label": address.display_name or address.addr_spec,
})
for record in records:
print(record["label"], record["addr_spec"])
这段写法的关键不在于多写几个属性,而在于先判断当前数据是单地址还是地址列表:单地址头取 address,多地址头取 addresses;展示层用 display_name,业务层通常保存 addr_spec。
相关问题
没有显示名时应该读取什么?读取 addr_spec 作为展示回退值,display_name 为空字符串并不表示地址无效。
为什么不直接保存 str(Address)?str() 可能包含显示名和格式化内容;如果下游只需要邮箱主体,保存 addr_spec 更明确。
一个地址头里出现多个收件人怎么办?访问该头部对象的 addresses 元组,逐个处理其中的 Address 对象。
-
144 收藏
-
243 收藏
-
文章 · python教程 | 52分钟前 | xml · Python教程 · Python SAX xml.sax.handler.ContentHandler characters XML文本解析382 收藏
-
501 收藏
-
483 收藏
-
276 收藏
-
208 收藏
-
182 收藏
-
401 收藏
-
324 收藏
-
221 收藏
-
434 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习