首页 > 解决方案 > 如何在 Python 3 中使用 smtplib.sendmail() 对收件人姓名(而非地址)中的国际字符进行编码?

问题描述

我在我的 Python 3 程序中使用标准 smtplib.sendmail() 调用来发送电子邮件,如下所示:

smtp_session.sendmail('The Sender <sender@domain.com>', ['The ÅÄÖ Recipient <recipient@domain.com>'], 'Simple test body here')

SMTP 会话在执行此代码行之前已经成功建立,并且只要收件人名称中没有“国际字符”,它也始终可以正常工作。

但是,一旦我在收件人名称中包含例如“ÅÄÖ”(甚至只是 8 位 ASCII 字符,甚至不是“真正的 unicode”或其他),如上所示,电子邮件就会消失并且永远不会到达发件人,尽管 sendmail() 方法没有返回或引发任何错误或异常,也没有其中的任何内容(我在调试器中单步执行此操作)。

我知道一个事实,我可以使用像 Thunderbird 这样的普通电子邮件客户端程序,通过我的这个完全相同的 SMTP 服务器发送收件人名称中包含此类字符的电子邮件,所以我只能假设这个问题与某些编码有关或类似的?

此外,该解决方案也不应该与那个mail_options=['SMTPUTF8']东西相关,因为如果我尝试使用它,服务器只会回复它不支持这个(同样,使用这些确切收件人名称的电子邮件仍然可以通过完全相同的方式发送)带有普通电子邮件客户端(如 Thunderbird)的 SMTP 服务器)。

那么,是否有一些简单的解决方案基于在收件人字符串上使用某种“MIME 相关”编码或类似的编码来解决这个问题,或者我如何才能从 Python 发送一封带有这样收件人名称的电子邮件?

标签: pythonpython-3.xemailcharacter-encodingsmtplib

解决方案


to 的参数不smtplib.sendmail()应该有人类可读的标签,只有地址终点。

smtp_session.sendmail('sender@domain.com', ['recipient@domain.com'],
    'Simple test body here')

email.headerregistryPython 3.6+中的模块具有仅提取电子邮件终端的功能,方法是将结构化标题解析为具有属性的对象。

from email.headerregistry import AddressHeader

hdr = dict()
AddressHeader.parse('To: The ÅÄÖ Recipient <recipient@domain.com>', hdr)
for grp in hdr['groups']:
    for addr in grp.addresses:
        print('{0}@{1}'.format(addr.username, addr.domain))

(我真的希望有一种不那么复杂的方式来访问这个功能,但至少这会产生预期的结果。)

在实际消息中,Python 负责正确地对任何带有 Unicode 内容的标头进行 RFC2047 编码(如果您使用email库中的正确方法来构造一个 prop0er MIME 消息);但这是纯粹的表示(RFC5322)而不是传输(RFC5321)。因此,在消息本身中,您可能会看到

From: The Sender <sender@domain.com>
To: The =?utf-8?Q?=C3=85=C3=84=C3=96_Recipient?= <recipient@domain.com>

但请记住,邮件内容不需要实际显示传输发件人或收件人标头。(对于批量电子邮件发件人,可能会切线看到标题“To:”


推荐阅读