Python脚本发送邮件和outlook客户端的坑
in Python with 0 comment
Python脚本发送邮件和outlook客户端的坑
in Python with 0 comment

前因后果

在使用新Outlook客户端接收爬虫自动发送的数据成功运作了一段时间,但由于新Outlook客户端同步接收邮件延迟明显,又换回了旧Outlook客户端。但在旧客户端上邮件文本内容正常接收,但附件不显示。一开始以为是客户端设置的问题,更改了很多选项都没法正常显示,又重新看了脚本代码。

原因分析

Python编写脚本定时发送邮件,常用smtplibemail两个模块,前者负责邮件投递,后者使用MIME类型构造邮件对象。
MIME (Multipurpose Internet Mail Extensions) 是描述消息内容类型的标准,用来表示文档、文件或字节流的性质和格式。
MIME类型通用结构:

type/subtype

MIME 的组成结构非常简单,由类型与子类型两个字符串中间用 / 分隔而组成,不允许有空格。type 表示可以被分多个子类的独立类别,subtype 表示细分后的每个类型。常见两种默认类型:
text/plain 表示文本文件的默认值。
application/octet-stream 表示其它二进制数据或特定文件的默认值。
常见子类型表示文件:
text/plain:纯文本文件
text/html:HTML 文件
image/jpeg:JPEG 图像文件
image/png:PNG 图像文件
audio/mpeg:MP3 音频文件
video/mp4:MP4 视频文件
application/json:JSON 数据文件
application/xml: XML 数据格式
application/pdf: PDF 文档
application/zip: ZIP 压缩文件
对于邮件体来说,常用的有纯文本TEXT和超文本HTML,如果需要混合多种类型使用,如带内嵌图片或者附件,则需要构造multipart类型。multipart类型邮件体被分为多个段,每个段又包含段头和段体两部分,这两部分之间也以空行分隔。常见的multipart类型有三种:multipart/mixed, multipart/related和multipart/alternative,它们之间的层次关系可归纳为下图所示:
MIME 消息能包含文本、图像、音频、视频以及其他应用程序专用的数据。
05013854-cd6385c5dc05405bafa9be56ba22ae31.jpg

如果在邮件中要添加附件,必须定义multipart/mixed段;如果存在内嵌资源,至少要定义multipart/related段;如果纯文本与超文本共存,至少要定义multipart/alternative段。
在Python中,MIMEMultipart类默认值为'mixed',但自己写的脚本却设置成'alternative'。

class MIMEMultipart(MIMEBase):
    """Base class for MIME multipart/* type messages."""

    def __init__(self, _subtype='mixed', boundary=None, _subparts=None,
                 *, policy=None,
                 **_params):
        """Creates a multipart/* type message.

        By default, creates a multipart/mixed message, with proper
        Content-Type and MIME-Version headers.

        _subtype is the subtype of the multipart content type, defaulting to
        `mixed'.

        boundary is the multipart boundary string.  By default it is
        calculated as needed.

        _subparts is a sequence of initial subparts for the payload.  It
        must be an iterable object, such as a list.  You can always
        attach new subparts to the message by using the attach() method.

        Additional parameters for the Content-Type header are taken from the
        keyword arguments (or passed into the _params argument).
        """
        MIMEBase.__init__(self, 'multipart', _subtype, policy=policy, **_params)

        # Initialise _payload to an empty list as the Message superclass's
        # implementation of is_multipart assumes that _payload is a list for
        # multipart messages.
        self._payload = []

        if _subparts:
            for p in _subparts:
                self.attach(p)
        if boundary:
            self.set_boundary(boundary)

解决方法

如果邮件携带了附件,且类型设置为'alternative'或'related'的情况下,现代邮件客户端一般也能正确识别附件并显示。然而在RFC1341中,当指定了Content-Type:multipart/alternative时,邮件客户端如果觉得自己不能够展示附件相应的类型,可以选择不显示。在旧版outlook客户端上,如果Content-Type,附件就无法显示,新OUTLOOK又能正确显示。

The article has been posted for too long and comments have been automatically closed.