繁体   English   中英

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

[英]How do I send email to addresses with non-ASCII characters in Python?

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

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

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

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

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

没有; 其实恰恰相反。 电子邮件地址 ASCII只。 它们旨在成为Unicode,而我们正在那里。 这只是一个缓慢的过渡。


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


DNS的上一次更新是在1987年的RFC 1035中 ,它规定了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,因此在邮箱名称中也只能使用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ñameussernyame用作同一邮箱的别名。 任何人谁不能处理SMTPUTF8会看到你(和有指你)作为ussernyame (实际上,他们的邮件客户端会将您视为ussernyame@xn--dmain-0ta.com ,但是它可以解决最后一部分;如果第一部分在运输过程中丢失了,它将无能为力。)

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

从Python 3.5( 4)开始 ,标准库的smtplib支持SMTPUTF8 如果您使用高级sendmail功能:

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

因此,您要做的是这样的:

try:
    server.sendmail([fromaddr], [toaddr], msg, mail_options=['SMTPUTF8'])
except SMTPNotSupportedError:
    server.sendmail([fromaddr_ascii], [toaddr_ascii], msg)

(从理论上讲,最好使用has_extn检查EHLO响应,但是在实践中,只需尝试似乎更平稳即可。随着服务器生态系统和/或smptlib未来改进,这种情况可能会改变。)

fromaddr_asciitoaddr_ascii哪里可以得到? 这取决于您的程序。 在DNS部分,您只使用IDNA,但是对于邮箱部分,则没有这样的规则。 您必须知道邮箱的备用ASCII邮箱名称。 也许你问用户。 也许您有一个数据库,其中存储了具有EAI和传统地址的联系人。 也许您只担心一个特定的域,并且知道它使用了可以实施的某些规则。


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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM