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

很多人学习python,不知道从何学起。
很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手。
很多已经做案例的人,却不知道如何去学习更加高深的知识。
那么针对这三类人,我给大家提供一个好的学习平台,免费领取视频教程,电子书籍,以及课程的源代码!
QQ群:101677771

而个人微信号的接口我没有合适的微信号可以登录,如果网页版微信没有被封的话,我想这个是表现形式与消息时效性结合的最好的方式。

环境

虽说就发邮件这么个小事,很容易兼容 Python 2, Python3, 但是大家还是拥抱Python3吧, 我这里没有做python2的兼容写法,所以需要python3以上。

邮件的格式

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

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

html就如其名, 是html的格式,相当于一个邮件就是一个 静态 的网页,这样的话可玩性就很高了,你可以通过css控制表现形式.

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

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

邮箱账号

无论是QQ邮箱抑或网易邮箱都是没有问题的,重要的是有一个可以通过smtp服务器发送邮件的账户名及密码,这里大家百度吧。

发送邮件的代码

因为发送邮件的代码在下面每个步骤都是一样的所以线贴出来

  1. def send_email(msg, mail_to, smtp_host, smtp_username, smtp_password, subject, from_):
  2.     msg["Subject"] = Header(subject, "utf-8")
  3.     msg["From"] = Header(from_, "utf-8")
  4.     if not isinstance(mail_to, list):
  5.         mail_to = [mail_to]
  6.     msg["To"] = COMMASPACE.join(mail_to)
  7.  
  8.     try:
  9.         print("准备连接smtp邮件服务器: %s" % smtp_host)
  10.         client = smtplib.SMTP(smtp_host)
  11.         print("连接成功")
  12.         # client = smtplib.SMTP("localhost")
  13.         # client.set_debuglevel(1)
  14.         # print(self.mail_user, self.mail_pass)
  15.         client.login(smtp_username, smtp_password)
  16.         print("登录成功")
  17.         # print("=====>", self.mail_from, mail_to)
  18.         print("通过邮箱[%s]发送邮件给 %s" % (smtp_username, COMMASPACE.join(mail_to)))
  19.         client.sendmail(smtp_username, mail_to, msg.as_string())
  20.         print("发送成功...")
  21.         return True
  22.     except Exception:
  23.         print("发送邮件失败")
  24.     finally:
  25.         client.quit()

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

发送本地图片

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

效果如下:

代码如下:

  1. EMAIL_IMAGE_TEMPLATE = """<html>
  2. <head>
  3. <title>Page Title</title>
  4. </head>
  5. <body>
  6. <h3>这是一张图片</h3>
  7. <p><img src="cid:{{image_name}}" height="112" width="200" ></p>
  8. </body>
  9. </html>
  10. """
  11.  
  12. def create_image_eamil_contant(fp):
  13.     tpl = Template(EMAIL_IMAGE_TEMPLATE)
  14.     if not path.exists(fp):
  15.         sys.exit("要发送的本地图片不存在")
  16.  
  17.     msg = MIMEMultipart("related")
  18.     image_name = "demo"
  19.  
  20.     with open(fp, "rb") as rf:
  21.         mime_image = MIMEImage(rf.read())
  22.         # 注意: 一定需要<>括号
  23.         mime_image.add_header("Content-ID", "<%s>" % image_name)
  24.         msg.attach(mime_image)
  25.  
  26.     # 渲染邮件文本内容
  27.     text = tpl.render(image_name=image_name)
  28.     msg_alternative = MIMEMultipart("alternative")
  29.     msg_alternative.attach(MIMEText(text, "html", "utf-8"))
  30.  
  31.     msg.attach(msg_alternative)
  32.  
  33.     return msg

如果你使用过python的web框架,你对文本的渲染一定不陌生,因为大多数web框架都支持文本渲染,这里使用的jinja2.

发送程序生成的照片

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

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

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

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

效果如下:

代码如下:

  1. EMAIL_ONLINE_IMAGE_TEMPLATE = """<html>
  2. <head>
  3. <title>Page Title</title>
  4. </head>
  5. <body>
  6. <h3>这是一张图片</h3>
  7. <p><img src="cid:{{image_name}}" ></p>
  8. </body>
  9. </html>
  10. """
  11.  
  12. def create_online_image_content():
  13.     from PIL import Image
  14.  
  15.     tpl = Template(EMAIL_ONLINE_IMAGE_TEMPLATE)
  16.     fp = "demo_base64.txt"
  17.     if not path.exists(fp):
  18.         sys.exit("要发送的base64编码的图片不存在")
  19.  
  20.     msg = MIMEMultipart("related")
  21.     image_name = "demo"
  22.  
  23.     with open(fp, "rb") as rf:
  24.         base64_data = rf.read()
  25.         img_data = base64.b64decode(base64_data)
  26.         # 因为open方法需要一个file-like文件对象,而我们解码后的对象类型是bytes类型
  27.         # bytes类型没有文件对象的read, close方法,所以我们需要通过BytesIO对象包装一下,它会返回一个file-like文件对象
  28.         img = Image.open(BytesIO(img_data))
  29.         img_width, img_height = img.size
  30.  
  31.         repeat_times = 5
  32.         # compose images
  33.         ret_img  = Image.new(img.mode, (img_width, img_height * repeat_times))
  34.         for index in range(repeat_times):
  35.             ret_img.paste(img, box=(0, index * img_height))
  36.  
  37.         # 因为MIMEImage需要一个bytes对象,所以们需要获取图片编码后的二进制数据而不是图片的array数据
  38.         img_bytes = BytesIO()
  39.         # 如果不指定图片格式,会因为没有文件名而报错
  40.         ret_img.save(img_bytes, "png")
  41.  
  42.         mime_image = MIMEImage(img_bytes.getvalue())
  43.         # 注意: 一定需要<>括号
  44.         mime_image.add_header("Content-ID", "<%s>" % image_name)
  45.         msg.attach(mime_image)
  46.  
  47.     # 渲染邮件文本内容
  48.     text = tpl.render(image_name=image_name)
  49.     msg_alternative = MIMEMultipart("alternative")
  50.     msg_alternative.attach(MIMEText(text, "html", "utf-8"))
  51.  
  52.     msg.attach(msg_alternative)
  53.  
  54.     return msg

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

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

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

效果如下:

代码如下:

  1. EMAIL_TEMPLATE = """<html>
  2. <head>
  3.     <style type="text/css">
  4.         table
  5.         {
  6.             border-collapse: collapse;
  7.             margin: 0 auto;
  8.             text-align: center;
  9.         }
  10.  
  11.         table td, table th
  12.         {
  13.             border: 1px solid #cad9ea;
  14.             color: #666;
  15.             height: 30px;
  16.         }
  17.  
  18.         table thead th
  19.         {
  20.             background-color: #CCE8EB;
  21.             width: 100px;
  22.         }
  23.  
  24.         table tr:nth-child(odd)
  25.         {
  26.             background: #fff;
  27.         }
  28.  
  29.         table tr:nth-child(even)
  30.         {
  31.             background: #F5FAFA;
  32.         }
  33.     </style>
  34. </head>
  35. <body>
  36. <p>一共有以下{{record_size}}条数据</p>
  37. <table width="90%" class="table">
  38.     <thead>
  39.         <tr>
  40.         {% for label in labels %}
  41.             <th>{{label}}</th>
  42.         {% endfor %}
  43.         </tr>
  44.     </thead>
  45.     <tbody>
  46. {% for item in items %}
  47.     <tr>
  48.     {% for value in item %}
  49.         <td>{{value}}</td>
  50.     {% endfor %}
  51.     </tr>
  52. {% endfor %}
  53.     </tbody>
  54. </table>
  55. </html>"""
  56.  
  57. def create_html_content():
  58.     tpl = Template(EMAIL_TEMPLATE)
  59.  
  60.     record_size = 10
  61.     label_size = 5
  62.     labels = ["label-%s" % i for i in range(label_size)]
  63.     items = []
  64.  
  65.     for _ in range(record_size):
  66.         item = ["item-%s" % value_index for value_index in range(label_size)]
  67.         items.append(item)
  68.  
  69.     text = tpl.render(record_size=record_size, items=items, labels=labels)
  70.     msg = MIMEText(text, "html", "utf-8")
  71.     return msg

源代码地址

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

用Python发一封图文并茂的邮件的更多相关文章

  1. [转]简单三步,用 Python 发邮件

    https://zhuanlan.zhihu.com/p/24180606 0. 前言 发送电子邮件是个很常见的开发需求.比如你写了个监控天气的脚本,发现第二天要下雨,或者网站上关注的某个商品降价了, ...

  2. python发邮件遇到的端口号问题

    在学习使用python发邮件的过程中, 遇到了一个问题:由于测试的时候使用的是QQ邮箱,要求必须使用SSL/TLS加密,所以有了下面的代码, from email.mime.text import M ...

  3. 使用python发邮件

    使用python发邮件 网上有很多发邮件的例子,本人在网上找了一份,稍加修改后使用 上源码 # encoding=utf-8 from email.mime.image import MIMEImag ...

  4. 如何用python发邮件

    python发送各类邮件的主要方法 一.相关模块介绍 发送邮件主要用到了smtplib和email两个模块,这里首先就两个模块进行一下简单的介绍:     1.smtplib模块 smtplib.SM ...

  5. Python 发邮件例子

    Python 发邮件例子 例子 #!/usr/bin/env python # -*- coding: utf-8 -*- # @Date : 2019-04-23 16:12:33 # @Autho ...

  6. python发QQ邮件

    python发qq邮件相对比较简单,网上教程一大把:固定套路,后面封装看自己怎么方便可以怎样进行封装:原版代码如下: """ # -*- coding : utf-8 - ...

  7. Python爬虫突破封禁的6种常见方法

    转 Python爬虫突破封禁的6种常见方法 2016年08月17日 22:36:59 阅读数:37936 在互联网上进行自动数据采集(抓取)这件事和互联网存在的时间差不多一样长.今天大众好像更倾向于用 ...

  8. Python发送邮件(常见四种邮件内容)

    Python发送邮件(常见四种邮件内容) 转载 2017年03月03日 17:17:04   转自:http://lizhenliang.blog.51cto.com/7876557/1875330 ...

  9. 用DataBaseMail发图片并茂的邮件

    不知道各位的老板有没有这样的要求, 一些系统中的数据需要定时发出邮件提醒, 如呆料就要到期或者一些待办的事项提醒. 当然这些用SSRS报表订阅可以实现,但有些公司没有设定相应的报表服务,又或者只是一些 ...

随机推荐

  1. linux的文件处理(匹配 正则表达式 egrep awk sed)和系统、核心数据备份

    文件处理 1.处理方式 匹配 正则表达式 egrep awk sed 2.文件中的处理字符 \n  新行符 换行 \t  制表符 tab键 缺省8个空格 \b  退格符 backspace键 退格键 ...

  2. idea中运行Tomcat后控制台出现乱码(统一设置成UTF-8)

    出现问题:运行Tomcat后控制台出现乱码,输出语句乱码(idea 2019.3版本) 解决方法: 方案1:File----->Settings..----->Editor-----> ...

  3. 你该知道的Docker-compose

    Docker-compose介绍 前几篇文章和小伙伴们,分享了使用Dockerfile来构建镜像,使用docker run等命令来手动启动镜像.docker stop停止镜像.docker kill杀 ...

  4. JVM系列之:从汇编角度分析NullCheck

    目录 简介 一个普通的virtual call 普通方法中的null check 反优化的例子 总结 简介 之前我们在讲Virtual call的时候有提到,virtual call方法会根据传递的参 ...

  5. JS 动画笔记

    动画实现原理     核心原理:通过定时器setInterval()不断地移动盒子的位置 缓动动画原理     缓动的核心算法~~~~~~~~~~(目标值-现在的位置)/10     停止的条件是~~ ...

  6. 论文结果图:matplotlib和seaborn实现

    在论文中,可视化结果往往很重要,毕竟文字太抽象,需要图片向审稿人直观的展现出我们的结果.我也写了俩篇论文和一篇专利的申请,其中也有一些画图的程序,因此记录,防止以后忘了.由于篇幅原因,文章就不贴代码, ...

  7. 拓展欧几里得求 ax + by = c的通解(a >=0, b >= 0)

    #include <iostream> #include <cstdio> #include <algorithm> #include <vector> ...

  8. Springboot快速入门篇,图文并茂

    Springboot快速入门篇,图文并茂 文章已托管到GitHub,大家可以去GitHub查看阅读,欢迎老板们前来Star!搜索关注微信公众号 [码出Offer] 领取各种学习资料! image-20 ...

  9. 初始化itable

    在InstanceKlass::link_class_impl()方法中完成方法连接后会继续初始化vtable与itable,之前已经介绍过vtable与itable,并且在类解析过程中已经完成了大小 ...

  10. 图论算法(五)最小生成树Prim算法

    最小生成树\(Prim\)算法 我们通常求最小生成树有两种常见的算法--\(Prim\)和\(Kruskal\)算法,今天先总结最小生成树概念和比较简单的\(Prim\)算法 Part 1:最小生成树 ...