如何在Python中将电子邮件发送到具有非ASCII字符的地址?

时间:2018-09-02 03:26:10

标签: python python-3.x email unicode

在Python 3.x中使用emailsmtplib模块后,经过大量研究,我可以发送带有Unicode主题,文本主体和名称的电子邮件(对于发件人和收件人收件人),这很棒,但它不允许我将电子邮件发送到本身包含Unicode(或其他非ASCII)字符的地址。它似乎不受支持(如果您查看email.utils中的注释,它会说很多:即“地址(按RFC)必须为ascii,因此,如果不是,则引发UnicodeError。”)无论如何,任何尝试(包括但不仅限于BCC收件人,以绕过任何消息头限制)的尝试均以Unicode错误形式或另一种形式失败。该评论没有说明哪个RFC(我不认为它们都指定电子邮件地址应仅使用ASCII。)

还有其他方法可以做到吗,据传闻这样的地址可以在某些地方存在:úßerñame@dómain.com?我的意思是,还有其他支持该功能的电子邮件模块吗?

如果我的问题的前提不正确,那么电子邮件地址是否打算在整个世界范围内仅使用ASCII(尽管有传言称其中某些地址使用其他字符)?

我在其他语言中看到了这个问题,但在Python中却没有。

1 个答案:

答案 0 :(得分:4)

  

电子邮件地址是否打算成为全世界唯一的ASCII码?

否;其实恰恰相反。电子邮件地址 仅ASCII。他们打算成为Unicode,而我们正步入正轨。只是一个缓慢的过渡。


在现代电子邮件中,电子邮件地址分为两部分: 1 DNS主机名(@之后的部分)和该主机上的邮箱( @)。它们受完全不同的标准支配,因为DNS除了电子邮件之外,还必须适用于HTTP和其他各种功能。


DNS的最后一次更新是在RFC 1035中,它在1987年进行了更新,其中规定了受限制的ASCII子集(并且不区分大小写)。

但是,RFC 5890中指定的IDNA(应用程序国际域名)允许应用程序有选择地将Unicode字符集的很大一部分映射到DNS名称,以呈现给用户。

因此,您不能拥有域名dómain.com。但是您可以拥有域名xn--dmain-0ta.com。许多应用程序会接受用户输入的dómain.com并自动进行翻译,然后接受来自网络的xn--dmain-0ta.com并将其显示给dómain.com 2

在Python中,一些用于互联网协议的库会自动为您IDNA编码域名;否则不会。如果没有,您可以手动进行操作,如下所示:

>>> 'dómain.com'.encode('idna')
b'xn--dmain-0ta.com'

请注意,在3.x中,这是bytes,而不是str;如果您需要str,则可以随时执行以下操作:

>>> 'dómain.com'.encode('idna').decode('ascii')
'xn--dmain-0ta.com'

邮箱名称由SMTP定义,最近一次在RFC 5321RFC 5322中定义,这清楚地表明,如何解释地址的“本地部分”完全取决于接收主机。例如,大多数电子邮件服务器使用不区分大小写的名称。许多允许“加标签”(例如shule@gmail.comshule+so@gmail.com是同一邮箱);一些(例如gmail)忽略所有点;等

问题在于SMTP从未指定标头使用什么字符集。传统的SMTP服务器仅是7位ASCII,因此,直到最近,实际上,您只能在标头(因此在邮箱名称中)使用ASCII。

RFC 6530和相关提案中指定的

EAI(电子邮件地址国际化)允许在SMTP会话中协商UTF-8。在UTF-8会话中,标头以及这些标头中的地址被解释为UTF-8。 (主机名不需要IDNA编码,但仍然允许。)

那太好了,但是如果您的客户端,服务器,收件人的服务器或任何中继服务器在使用过程中都不讲SMTPUTF8,该怎么办?为了处理这种情况,每个拥有UTF-8邮箱的人也都具有该邮箱的ASCII名称。理想情况下,该消息将与消息一起发送,并且当链上的第一个SMTPUTF8程序遇到第一个非SMTPUTF8程序时,将切换为ASCII替换。更常见的是,它只是收到一条错误消息,然后将其传播回用户以进行手动处理。 3

这个想法是,最终,Internet上的大多数主机都会使用SMTPUTF8,因此您可以úßerñame@dómain.com-但与此同时,dómain.com上的服务器具有úßerñame和{{1} }作为同一邮箱的别名。任何无法处理SMTPUTF8的人都将以ussernyame的身份看到您(并且必须将您引荐给您)。 (实际上,他们的邮件客户端会将您视为ussernyame,但它可以修复最后一部分;如果丢失了第一部分,它就无能为力了。)

截至2018年中,大多数主机都不讲SMTPUTF8,也没有很多客户端库。

从Python 3.5开始, 4 标准库的ussernyame@xn--dmain-0ta.com支持smtplib。如果您使用的是高级sendmail函数:

  

如果mail_options中包含SMTPUTF8,并且服务器支持它,则 from_addr to_addrs 可能包含非ASCII字符。

所以,您要做的就是这样:

SMTPUTF8

(从理论上讲,最好使用has_extn检查EHLO响应,但实际上,只是尝试使其看起来更平稳。随着服务器生态系统和/或{{1} }。

您从哪里得到try: server.sendmail([fromaddr], [toaddr], msg, mail_options=['SMTPUTF8']) except SMTPNotSupportedError: server.sendmail([fromaddr_ascii], [toaddr_ascii], msg) smptlib?这取决于您的程序。在DNS部分,您只使用IDNA,但是对于邮箱部分,则没有这样的规则。您必须知道邮箱的备用ASCII邮箱名称。也许你问用户。也许您有一个数据库,其中存储了具有EAI和传统地址的联系人。也许您只担心一个特定的域,并且知道它使用了可以实施的某些规则。


1。实际上,addr-spec有两个部分: 地址是地址规范,加上可选的显示名称和注释。但是没关系。

2。有一些例外。例如,如果键入fromaddr_ascii,则浏览器可能会警告您,西里尔小写Es代替拉丁小写Cee可能是劫持尝试。或者,如果您尝试导航到toaddr_ascii,则错误页面告诉您该域不存在,可能会显示http://staсkoverflow.com,因为这对于调试更有用。

3。这是希望随着时间的推移会变得更好的事情之一,但是可能直到变得无所谓后,它才能变得不够好……

4。如果您使用的是Python 3.4或2.7,该怎么办?那么您就没有SMTPUTF8支持。升级,找到第三方库而不是http://dómain.com,或编写您自己的SMTP代码。