[英]How do I send email to addresses with non-ASCII characters in Python?
经过大量研究,我使用Python 3.x中的email
和smtplib
模块,可以发送带有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 5321和RFC 5322中定义,这清楚表明,如何解释地址的“本地部分”完全取决于接收主机。 例如,大多数电子邮件服务器使用不区分大小写的名称。 许多允许“加标签”(例如, shule@gmail.com
和shule+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ñame
和ussernyame
用作同一邮箱的别名。 任何人谁不能处理SMTPUTF8会看到你(和有指你)作为ussernyame
。 (实际上,他们的邮件客户端会将您视为ussernyame@xn--dmain-0ta.com
,但是它可以解决最后一部分;如果第一部分在运输过程中丢失了,它将无能为力。)
截至2018年中,大多数主机不讲SMTPUTF8,许多客户端库也不讲。
从Python 3.5( 4)开始 ,标准库的smtplib
支持SMTPUTF8
。 如果您使用高级sendmail
功能:
如果
SMTPUTF8
包含在mail_options中,并且服务器支持它,则from_addr和to_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_ascii
和toaddr_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.