【问题标题】:Python send email with "quoted-printable" transfer-encoding and "utf-8" content-encodingPython 使用“quoted-printable”传输编码和“utf-8”内容编码发送电子邮件
【发布时间】:2015-10-21 05:55:35
【问题描述】:

python 的email.mime 倾向于使用编码base647bitus-ascii。我想使用quoted-printableutf-8,因为这样更便于人类阅读和调试。

目前,我的电子邮件看起来像

--===============6135350048414329636==
MIME-Version: 1.0
Content-Type: text/plain
Content-Transfer-Encoding: base64

IyEvYmluL2Jhc2gKCmZvciBpIGluIHs4Mjg4Li44N

--===============0756888342500148236==
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit

happy face =E2=98=BA

我希望原始电子邮件采用带引号的可打印 unicode 格式,以便人们阅读。

--===============5610730199728027971==
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset="utf-8"

happy face ☺

【问题讨论】:

标签: python python-2.7 email smtp


【解决方案1】:

简短回答

设置content-transfer-encoding

创建MIMEText 对象时,将附加到MIMEMultipart 对象,首先将content-transfer-encoding 设置为值quoted-printable,然后执行set_payload。操作顺序很重要。

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

# first create MIMEText, then set content-transfer-encoding, then set payload
mt = MIMEText(None, _subtype='plain')
mt.replace_header('content-transfer-encoding', 'quoted-printable')
mt.set_payload(u'happy face ☺', 'utf-8')

# create the parent email object and the MIMEMultipart extension to it
email = MIMEMultipart('mixed')
inline = MIMEMultipart('alternative')

# assemble the objects
inline.attach(mt)
email.attach(inline)

设置邮箱charset和各种编码

cs = charset.Charset('utf-8')
cs.header_encoding = charset.QP
cs.body_encoding = charset.QP
email.set_charset(cs)

结果

这会创建人类可读的原始电子邮件(base64 编码的文件附件除外)

>>> print(email)
--===============5610730199728027971==
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset="utf-8"

happy face ☺

--===============5610730199728027971==--

--===============0985725891393820576==
Content-Type: text/x-sh
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="test.sh"

Zm9vYmFyc2RmYXNkZmtqaGFzZGZrbGhhc2ZrbGpoYXNma2xqaGFzZmtsaGZkYXNmCg==

--===============0985725891393820576==--

长答案

以下是一个较长的脚本,为之前的代码 sn-ps 提供更多上下文。

此脚本将发送以 UTF-8 编码的 text/plain 部分。为了好玩,它还会附加一个文件。 这产生的原始电子邮件将是人类可读的(文件附件除外)。

from __future__ import print_function

from email import charset    
from email.encoders import encode_base64
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import mimetypes

# create the parent email object
email = MIMEMultipart('mixed')
# set email charset and email encodings
cs_ = charset.Charset('utf-8')
cs_.header_encoding = charset.QP
cs_.body_encoding = charset.QP
email.set_charset(cs_)

# create the 'text/plain' MIMEText
# first create MIMEText, then set content-transfer-encoding, then set payload
mt = MIMEText(None, _subtype='plain')
mt.replace_header('content-transfer-encoding', 'quoted-printable')
mt.set_payload(u'happy face ☺', 'utf-8')

# assemble the parts
inline = MIMEMultipart('alternative')
inline.attach(mt)
email.attach(inline)

# for fun, attach a file to the email
my_file = '/tmp/test.sh'
mimetype, encoding = mimetypes.guess_type(my_file)
mimetype = mimetype or 'application/octet-stream'
mimetype = mimetype.split('/', 1)
attachment = MIMEBase(mimetype[0], mimetype[1])
attachment.set_payload(open(my_file, 'rb').read())
encode_base64(attachment)
attachment.add_header('Content-Disposition', 'attachment', filename=os.path.basename(my_file))
email.attach(attachment)

结果

这会创建人类可读的原始电子邮件(base64 编码的文件附件除外)

>>> print(email)
--===============5610730199728027971==
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset="utf-8"

happy face ☺

--===============5610730199728027971==--

--===============0985725891393820576==
Content-Type: text/x-sh
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="test.sh"

Zm9vYmFyc2RmYXNkZmtqaGFzZGZrbGhhc2ZrbGpoYXNma2xqaGFzZmtsaGZkYXNmCg==

--===============0985725891393820576==--

(奖励)发送电子邮件

使用smtplib,可以发邮件。

import smtplib

# set email address headers
email['From'] = 'me@email.com'
email['To'] = 'you@email.com'
email['Subject'] = 'hello'

# send the email
smtp_srv = smtplib.SMTP('localhost')
smtp_srv.set_debuglevel(True)
print(mesg_html, end='\n\n')
print(email.as_string(), end='\n\n')
smtp_srv.sendmail('me@email.com', 'you@email.com', email.as_string())
smtp_srv.quit()

【讨论】:

  • 该示例似乎根本没有引用可打印的编码。正确的 QP 看起来像 happy face =E2=98=BA —— 或者,在 Python 的默认 QP 实现中令人讨厌的过度热情的空格编码,happy=20face=20=E2=98=BA
  • 我猜你可以用mt.set_payload(u'happy face ☺'.encode('quoted-printable'), 'utf-8')得到后者。
  • 另外你应该有_subtype='plain' 而不是text/plaintext 已经隐含,不是子类型的一部分)。
  • "该示例似乎根本没有引用可打印的编码" 谢谢@tripleee,我将在下周内进行审查和更正。我已经更正了_subtype
  • 这不是必须的。取而代之的是从链接的建议副本之一中获取代码。
【解决方案2】:

在尝试更改现有消息(email.Message 对象)的正文并将其编码设置为 quoted-printable 时,我发现这个问题比我预期的要付出更多的努力。

import email
#... 'part' is the Message object
content = part.get_payload(decode=True)
#... Modify content
part['Content-Transfer-Encoding'] = '8bit'
part.set_payload(content, 'UTF-8')
del part['Content-Transfer-Encoding']
email.encoders.encode_quopri(part)

现在,我为什么要设置然后删除 Content-Transfer-Encoding 标头? set_payload 调用将设置 Content-Transfer-Encoding 标头并在不存在标头时将数据编码(到 Base64)。否则,set_payload 调用将假定调用者已经对数据进行了编码,并且不会更改它(通过编码)。所以,我设置 Content-Transfer-Encoding 标头的值实际上并不重要,只是我不会将其留空。

但是为什么我需要删除标题呢? email.encoders.encode_quopri 调用只会添加一个标头,因此消息会产生多个 Content-Transfer-Encoding 标头。

因此,对于没有Content-Transfer-Encoding 标头的消息,仅使用set_payload 然后encode_quopri 将导致Base64 字符串的可引用打印表示,对于具有现有Content-Transfer-Encoding 标头的消息将导致带有重复标题的消息。使用encode_quopri 然后set_payload 可能会导致重复的标头,但不会对消息进行编码。因此添加/删除 rigamarole 以避免陷入the quopri module

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-31
    • 2018-07-09
    • 2015-02-10
    • 1970-01-01
    • 2017-08-27
    • 1970-01-01
    • 2018-02-01
    相关资源
    最近更新 更多