用Python发一封图文并茂的邮件

最近使用了很多通信工具的接口, 好比企业微信机器人,钉钉,微信公众号的接口(未认证的订阅公众号),相对于邮件来讲,它们的表现形式太弱。好比没有更丰富的版本方式。固然了,并非说表现形式越棒就是约好的通知手段,这个依我的状况而定,而我偏偏须要比较丰富的表现形式,最终仍是回到了邮件,邮件真香!css

而我的微信号的接口我没有合适的微信号能够登陆,若是网页版微信没有被封的话,我想这个是表现形式与消息时效性结合的最好的方式。html

环境

虽然说就发邮件这么个小事,很容易兼容Python2, Python3, 可是你们仍是拥抱Python3吧, 我这里没有作python2的兼容写法,因此须要python3以上。python

邮件的格式

邮件的格式主要就两种: plain和htmlgit

plain就像一个普通的文本, 没有格式。github

html就如其名, 是html的格式,至关于一个邮件就是一个静态的网页,这样的话可玩性就很高了,你能够经过css控制表现形式.web

注意: 这里的css虽然语法同样,但,是否与浏览器渲染结果彻底一致, 是不必定的。浏览器

那么可能有人要问了,我要发一个动态的网页怎么办? 发个连接呀服务器

邮箱帐号

不管是QQ邮箱抑或网易邮箱都是没有问题的,重要的是有一个能够经过smtp服务器发送邮件的帐户名及密码,这里你们百度吧。微信

发送邮件的代码

由于发送邮件的代码在下面每一个步骤都是同样的因此线贴出来app

def send_email(msg, mail_to, smtp_host, smtp_username, smtp_password, subject, from_):
    msg["Subject"] = Header(subject, "utf-8")
    msg["From"] = Header(from_, "utf-8")
    if not isinstance(mail_to, list):
        mail_to = [mail_to]
    msg["To"] = COMMASPACE.join(mail_to)

    try:
        print("准备链接smtp邮件服务器: %s" % smtp_host)
        client = smtplib.SMTP(smtp_host)
        print("链接成功")
        # client = smtplib.SMTP("localhost")
        # client.set_debuglevel(1)
        # print(self.mail_user, self.mail_pass)
        client.login(smtp_username, smtp_password)
        print("登陆成功")
        # print("=====>", self.mail_from, mail_to)
        print("经过邮箱[%s]发送邮件给 %s" % (smtp_username, COMMASPACE.join(mail_to)))
        client.sendmail(smtp_username, mail_to, msg.as_string())
        print("发送成功...")
        return True
    except Exception:
        print("发送邮件失败")
    finally:
        client.quit()

若是遇到邮件发送的问题能够将client.set_debuglevel(1)的注释取消,这样会显示足够多的debug信息用于排查问题。

发送本地图片

这里发送图片的意思是指, 图片内嵌在邮件中而不是以附件的形式出现。

效果以下:

用Python发一封图文并茂的邮件

代码以下:

EMAIL_IMAGE_TEMPLATE = """<html>
<head>
<title>Page Title</title>
</head>
<body>
<h3>这是一张图片</h3>
<p><img src="cid:{{image_name}}" height="112" width="200" ></p>
</body>
</html>
"""

def create_image_eamil_contant(fp):
    tpl = Template(EMAIL_IMAGE_TEMPLATE)
    if not path.exists(fp):
        sys.exit("要发送的本地图片不存在")

    msg = MIMEMultipart("related")
    image_name = "demo"

    with open(fp, "rb") as rf:
        mime_image = MIMEImage(rf.read())
        # 注意: 必定须要<>括号
        mime_image.add_header("Content-ID", "<%s>" % image_name)
        msg.attach(mime_image)

    # 渲染邮件文本内容
    text = tpl.render(image_name=image_name)
    msg_alternative = MIMEMultipart("alternative")
    msg_alternative.attach(MIMEText(text, "html", "utf-8"))

    msg.attach(msg_alternative)

    return msg

若是你使用过python的web框架,你对文本的渲染必定不陌生,由于大多数web框架都支持文本渲染,这里使用的jinja2.

发送程序生成的照片

其实这里跟上面没什么区别的,惟一的区别就是是否保存在本地,既然能发送本地图片,我就先保存到本地而后再按照上面的方式不久能够了么? 首先这个方法是没有问题的,不过多了一次IO, 能在内存中解决的事为何要放到本地呢?

这种状况主要是应对回去图片的方式是从其余接口获取到的,或者实时生成的时候。虽然很简单,但以为说说也挺有意思的。

这里的模拟方式是假设在网上获取到了多张base64编码的图片,须要将其组合在一块儿,而后在不保存在本地状况下直接发送这张照片。

这个base64编码的图片已经保存在本地了,名字是demo_base64.txt

效果以下:

用Python发一封图文并茂的邮件

代码以下:

EMAIL_ONLINE_IMAGE_TEMPLATE = """<html>
<head>
<title>Page Title</title>
</head>
<body>
<h3>这是一张图片</h3>
<p><img src="cid:{{image_name}}" ></p>
</body>
</html>
"""

def create_online_image_content():
    from PIL import Image

    tpl = Template(EMAIL_ONLINE_IMAGE_TEMPLATE)
    fp = "demo_base64.txt"
    if not path.exists(fp):
        sys.exit("要发送的base64编码的图片不存在")

    msg = MIMEMultipart("related")
    image_name = "demo"

    with open(fp, "rb") as rf:
        base64_data = rf.read()
        img_data = base64.b64decode(base64_data)
        # 由于open方法须要一个file-like文件对象,而咱们解码后的对象类型是bytes类型
        # bytes类型没有文件对象的read, close方法,因此咱们须要经过BytesIO对象包装一下,它会返回一个file-like文件对象
        img = Image.open(BytesIO(img_data))
        img_width, img_height = img.size

        repeat_times = 5
        # compose images
        ret_img  = Image.new(img.mode, (img_width, img_height * repeat_times))
        for index in range(repeat_times):
            ret_img.paste(img, box=(0, index * img_height))

        # 由于MIMEImage须要一个bytes对象,因此们须要获取图片编码后的二进制数据而不是图片的array数据
        img_bytes = BytesIO()
        # 若是不指定图片格式,会由于没有文件名而报错
        ret_img.save(img_bytes, "png")

        mime_image = MIMEImage(img_bytes.getvalue())
        # 注意: 必定须要<>括号
        mime_image.add_header("Content-ID", "<%s>" % image_name)
        msg.attach(mime_image)

    # 渲染邮件文本内容
    text = tpl.render(image_name=image_name)
    msg_alternative = MIMEMultipart("alternative")
    msg_alternative.attach(MIMEText(text, "html", "utf-8"))

    msg.attach(msg_alternative)

    return msg

这里颇有意思一点是用BytesIO模拟file-like对象。这里须要安装PIL哦

发送一个带样式的静态网页

前面的代码已经足够说明图片怎么发了,这里经过一个写了css样式的表格进行演示

效果以下:

用Python发一封图文并茂的邮件

代码以下:

EMAIL_TEMPLATE = """<html>
<head>
    <style type="text/css">
        table
        {
            border-collapse: collapse;
            margin: 0 auto;
            text-align: center;
        }

        table td, table th
        {
            border: 1px solid #cad9ea;
            color: #666;
            height: 30px;
        }

        table thead th
        {
            background-color: #CCE8EB;
            width: 100px;
        }

        table tr:nth-child(odd)
        {
            background: #fff;
        }

        table tr:nth-child(even)
        {
            background: #F5FAFA;
        }
    </style> 
</head>
<body>
<p>一共有如下{{record_size}}条数据</p>
<table width="90%" class="table">
    <thead>
        <tr>
        {% for label in labels %}
            <th>{{label}}</th>
        {% endfor %}
        </tr>
    </thead>
    <tbody>
{% for item in items %}
    <tr>
    {% for value in item %}
        <td>{{value}}</td>
    {% endfor %}
    </tr>
{% endfor %}
    </tbody>
</table>
</html>"""

def create_html_content():
    tpl = Template(EMAIL_TEMPLATE)

    record_size = 10
    label_size = 5
    labels = ["label-%s" % i for i in range(label_size)]
    items = []

    for _ in range(record_size):
        item = ["item-%s" % value_index for value_index in range(label_size)]
        items.append(item)

    text = tpl.render(record_size=record_size, items=items, labels=labels)
    msg = MIMEText(text, "html", "utf-8")
    return msg

源代码地址

https://github.com/youerning/blog/tree/master/sendmail

若是期待后续文章能够关注个人微信公众号(又耳笔记),头条号(又耳笔记),github.

后记

其实发送一个附件也是不错的方式,好比发送一个生成的PDF, PDF是一个很棒的文件格式。可是PDF暂时没用到,之后有机会再说吧。最后要注意的是,手机端的显示效果跟电脑网页版的显示效果是不同的。

参考连接

https://www.runoob.com/python/python-email.html

相关文章
相关标签/搜索