如何发送UTF-8电子邮件?

53 投票
6 回答
67385 浏览
提问于 2025-04-16 17:06

请问怎么发送UTF-8编码的电子邮件呢?

import sys
import smtplib
import email
import re

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

def sendmail(firm, fromEmail, to, template, subject, date):
    with open(template, encoding="utf-8") as template_file:
        message = template_file.read()

    message = re.sub(r"{{\s*firm\s*}}", firm, message)
    message = re.sub(r"{{\s*date\s*}}", date, message)
    message = re.sub(r"{{\s*from\s*}}", fromEmail, message)
    message = re.sub(r"{{\s*to\s*}}", to, message)
    message = re.sub(r"{{\s*subject\s*}}", subject, message)

    msg = MIMEMultipart("alternative")
    msg.set_charset("utf-8")
    
    msg["Subject"] = subject
    msg["From"] = fromEmail
    msg["To"] = to

    #Read from template
    html = message[message.find("html:") + len("html:"):message.find("text:")].strip()
    text = message[message.find("text:") + len("text:"):].strip()

    part1 = MIMEText(html, "html")
    part2 = MIMEText(text, "plain")
    
    msg.attach(part1)    
    msg.attach(part2)

    try:
        server = smtplib.SMTP("10.0.0.5")
        server.sendmail(fromEmail, [to], msg.as_string())
        return 0
    except Exception as ex:
        #log error
        #return -1
        #debug
        raise ex
    finally:
        server.quit()

if __name__ == "__main__":
    #debug
    sys.argv.append("Moje")
    sys.argv.append("newsletter@example.cz")
    sys.argv.append("subscriber@example.com")
    sys.argv.append("may2011.template")
    sys.argv.append("This is subject")
    sys.argv.append("This is date")

    
    if len(sys.argv) != 7:
        exit(-2)

    firm = sys.argv[1]
    fromEmail = sys.argv[2]
    to = sys.argv[3]
    template = sys.argv[4]
    subject = sys.argv[5]
    date = sys.argv[6]
    
    exit(sendmail(firm, fromEmail, to, template, subject, date))

输出

Traceback (most recent call last):
  File "C:\Documents and Settings\Administrator\Plocha\Newsletter-build-desktop\sendmail.py", line 69, in <module>
    exit(sendmail(firm, fromEmail, to, template, subject, date))   
  File "C:\Documents and Settings\Administrator\Plocha\Newsletter-build-desktop\sendmail.py", line 45, in sendmail
    raise ex
  File "C:\Documents and Settings\Administrator\Plocha\Newsletter-build-desktop\sendmail.py", line 39, in sendmail
    server.sendmail(fromEmail, [to], msg.as_string())
  File "C:\Python32\lib\smtplib.py", line 716, in sendmail
    msg = _fix_eols(msg).encode('ascii')
UnicodeEncodeError: 'ascii' codec can't encode character '\u011b' in position 385: ordinal not in range(128)

6 个回答

3

之前这里的回答对于Python 2和早期的Python 3版本来说是足够的。但从Python 3.6开始,新的代码一般应该使用现代的EmailMessage API,而不是旧的email.message.Message类或者相关的MIMEMultipartMIMEText等类。其实这个新的API在Python 3.3的时候就已经非正式地引入了,所以除非你需要兼容Python 2(或者3.2,反正没有人会想用这个),否则不需要再用旧的API了。

使用新的API后,你不再需要手动从各个部分组装一个明确的MIME结构,也不需要明确选择正文部分的编码等等。而且,Unicode也不再是个特殊情况;email库会自动选择合适的容器类型和编码来处理普通文本。

import sys
import re
import smtplib
from email.message import EmailMessage

def sendmail(firm, fromEmail, to, template, subject, date):
    with open(template, "r", encoding="utf-8") as template_file:
        message = template_file.read()

    message = re.sub(r"{{\s*firm\s*}}", firm, message)
    message = re.sub(r"{{\s*date\s*}}", date, message)
    message = re.sub(r"{{\s*from\s*}}", fromEmail, message)
    message = re.sub(r"{{\s*to\s*}}", to, message)
    message = re.sub(r"{{\s*subject\s*}}", subject, message)

    msg = EmailMessage()    
    msg["Subject"] = subject
    msg["From"] = fromEmail
    msg["To"] = to

    html = message[message.find("html:") + len("html:"):message.find("text:")].strip()
    text = message[message.find("text:") + len("text:"):].strip()

    msg.set_content(text)
    msg.add_alternative(html, subtype="html")

    try:
        server = smtplib.SMTP("10.0.0.5")
        server.send_message(msg)
        return 0
    # XXX FIXME: useless
    except Exception as ex:
        raise ex
    finally:
        server.quit()
        # Explicitly return error
        return 1

if __name__ == "__main__":
    if len(sys.argv) != 7:
        # Can't return negative
        exit(2)

    exit(sendmail(*sys.argv[1:]))

我不太确定这里的模板处理是否完全理解。如果有不同需求的人,可能应该查看Python email示例文档,里面有几个简单的例子,展示了如何实现常见的邮件使用场景。

这里的except是多余的,但我把它留着作为占位符,以便你可以看到如果有有用的内容可以放在这里,异常处理可能是什么样子的。

另外,注意smtplib允许你使用上下文管理器,比如可以这样写:with SMTP("10.9.8.76") as server:

7

马丁·德尔利克提问的这个问题已经有7年8个月了……如今,得益于Python的开发者们,编码问题在Python 3版本中得到了很好的解决。

因此,现在不再需要特别说明要使用utf-8编码了:

#!/usr/bin/python2
# -*- encoding: utf-8 -*-
...
    part2 = MIMEText(text, "plain", "utf-8")

我们只需简单地写:

#!/usr/bin/python3
...
    part2 = MIMEText(text, "plain")

最终的结果是:马丁·德尔利克的脚本运行得非常好!

不过,还是建议使用email.parser模块,具体可以参考email: 示例中的建议。

89

你只需要在你的 MIMEText 调用中加上 'utf-8' 这个参数(默认情况下它是 'us-ascii')。

比如说:

# -*- encoding: utf-8 -*-

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

msg = MIMEMultipart("alternative")
msg["Subject"] = u'テストメール'
part1 = MIMEText(u'\u3053\u3093\u306b\u3061\u306f\u3001\u4e16\u754c\uff01\n',
                 "plain", "utf-8")
msg.attach(part1)

print msg.as_string().encode('ascii')

撰写回答