当前位置:首页 > 文章列表 > 文章 > python教程 > Python email.headerregistry.Address 怎样区分邮箱显示名与地址

Python email.headerregistry.Address 怎样区分邮箱显示名与地址

来源:17golang原创 2026-10-27 13:44:41 0浏览 收藏

处理邮件头时,最容易混淆的是“别人看到的名字”和“真正用于投递的地址”。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 则只保留地址主体。两者都不是简单地对原始字符串做截取。

Python Address 对象拆分为显示名用户名域名和 addr_spec 的结构说明图
图1:Address 字段拆分说明图,展示显示名与实际地址部分的关系,不是运行截图。

单地址头怎样取出显示名和实际地址

从 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}")
邮件头解析层与 Address addresses Group 关系的结构图
图2:AddressHeader 与 addresses、Group 的关系结构图,帮助区分单地址读取和列表遍历,不是运行截图。

如果只关心实际收件地址,可以在循环里统一取 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 对象。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Python decimal.Context.flags 怎样记录计算中的舍入信号Python decimal.Context.flags 怎样记录计算中的舍入信号
上一篇
Python decimal.Context.flags 怎样记录计算中的舍入信号
Python email.policy.clone 怎样设置邮件序列化换行规则
下一篇
Python email.policy.clone 怎样设置邮件序列化换行规则
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    734次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    736次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    815次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    776次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    611次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码