Python 3 smtplib 发送带有Unicode字符的邮件

9

我在使用Python 3中的smtplib发送包含Unicode字符的邮件时遇到了问题。在3.1.1版本中失败,但在2.5.4版本中可以正常工作:

  import smtplib
  from email.mime.text import MIMEText

  sender = to = 'ABC@DEF.com'
  server = 'smtp.DEF.com'
  msg = MIMEText('€10')
  msg['Subject'] = 'Hello'
  msg['From'] = sender
  msg['To'] = to
  s = smtplib.SMTP(server)
  s.sendmail(sender, [to], msg.as_string())
  s.quit()

我尝试了文档中的一个示例,但也失败了。请参考http://docs.python.org/3.1/library/email-examples.html中的“将目录内容发送为MIME消息”的示例。你有什么建议吗?

澄清一下,在2.5.4中,它会发送但不会出现错误信息,但会用“?”替换“€”。 - foosion
2个回答

15

重点在于文档中:

class email.mime.text.MIMEText(_text, _subtype='plain', _charset='us-ascii')
MIMEText类是MIMENonMultipart的子类,用于创建主类型为文本的MIME对象。_text表示载荷字符串,_subtype表示次要类型,默认为plain。_charset表示文本的字符集,并作为参数传递给MIMENonMultipart构造函数;它默认为us-ascii。文本数据不会进行猜测或编码处理。因此,你需要的显然不是msg = MIMEText('€10'),而是:
msg = MIMEText('€10'.encode('utf-8'), _charset='utf-8')
虽然可能没有很清楚地记录下来,但是sendmail需要一个字节字符串而不是Unicode字符串(这是SMTP协议规定的)。看一下使用两种构建消息的方式中msg.as_string()的输出,你的方式中仍然包含欧元符号(没有办法将其转换为字节串),我的方式则没有,并且UTF-8在整个过程中都明确指定。

发送时没有生成错误消息。我发送到了Thunderbird和Gmail。Thunderbird只显示消息文本中的10。Gmail则显示完整的€10。Python发送时使用'content-transfer-encoding: base64',而Thunderbird将€10发送为'content-transfer-encoding: 8-bit',Gmail则发送为'multipart/alternative; boundary=...'。有什么建议可以生成Thunderbird可以解释的消息吗? - foosion
我不是Thunderbird专家,但请尝试其他编码,如iso-8859-15。虽然现在任何不能正确处理utf-8的程序都值得被扔进历史的垃圾箱!-) - Alex Martelli
问题似乎不是iso-8859-15或utf-8,而是内容传输编码。我检查的其他所有内容都使用8位,而Python使用base64。将标题强制转换为8位并没有帮助。使用quopri.encodestring()可能可以得到8位编码,但我还没有能够弄清楚如何使其正常工作。 - foosion

2
根据文档MIMEText_charset参数默认为us-ascii。由于不属于us-ascii集合,因此无法正常工作。
您尝试过的文档示例清楚地说明了:

对于本示例,假设文本文件仅包含ASCII字符。

您可以在消息上使用.get_charset方法来调查字符集,还有一个.set_charset方法。

正如您所说,字符集是us-ascii,其中不包括€。在msg上使用set_charset无法解决问题。问题(我应该更加精确)出现在sendmail行上 - UnicodeEncodeError:'ascii'编解码器无法在位置161中编码字符'\x80':序数不在范围内(128)。我理解这意味着我必须对文本进行编码,以便所有内容都在range(128)范围内,但我还没有找到如何做到这一点。 - foosion
我正在查看示例页面上的第三个示例,发送整个目录。我尝试使用该示例发送由单个zip文件组成的目录。但是失败了。 - foosion

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接