自动化运维之使用Python3收发电子邮件~~~附源码
一、背景介绍
1.1 一些专业名称的解释
MUA——Mail User Agent,邮件用户代理。是用户与电子邮件系统的交互接口,一般来说它就是我们PC机上的一个程序,提供一个好的用户界面,它提取用户在其界面填写的各项信息,生成一封符合SMTP等邮件标准的邮件,然后采用SMTP协议将邮件发送到发送端邮件服务器。常见的通用型代理有OutLook Express、FoxMail、网易闪电邮等
MTA——Mail Transfer Agent,邮件传输代理。负责邮件的传输,它采用端到端的传输的传输方式,源端主机参与邮件传输的全过程,也可称之为SMTP服务器。其实就是那些Email服务提供商,比如网易、新浪等等。比如我使用的电子邮件是163.com,所以,发Email首先被投递到网易提供的MTA,再由网易的MTA发到对方服务商,可能是sina、qq或者其他的MTA,这个过程中间可能还会经过别的MTA
MDA——Mail Deliver Agent,邮件投递代理。Email到达收件人(比如使用的是新浪的邮箱)的MTA后,新浪的MTA会把Email投递到邮件的最终目的地MDA。Email到达MDA后,就静静地躺在新浪的某个服务器上,存放在某个文件或特殊的数据库里。Email不会直接到达对方的电脑,对方要取到邮件,必须通过MUA从MDA上把邮件取到自己的电脑上。
SMTP——Simple Mail Transfer Protocol,简单邮件传输协议 。是Internet上基于TCP/IP的应用层协议,定义了邮件发送方和接收方之间的连接传输,默认端口号TCP 25。SMTP有其一定的局限性,它只能传送ASCII文本文件,而对于一些二进制数据文件需要进行编码后才能传送。
POP3——Post Office Protocol Version 3,邮局协议的第三版本。主要用于支持使用客户端远程管理在服务器上的电子邮件。,它规定怎样将个人计算机连接到Internet的邮件服务器和下载电子邮件的电子协议。它是因特网电子邮件的第一个离线协议标准,POP3允许用户从服务器上把邮件存储到本地主机(即自己的计算机)上,同时删除保存在邮件服务器上的邮件
IMAP——Internet Message Access Protocol,因特网报文存取协议。它是跟POP3类似邮件访问标准协议之一。不同的是,开启了IMAP后,您在电子邮件客户端收取的邮件仍然保留在服务器上,同时在客户端上的操作都会反馈到服务器上,如:删除邮件,标记已读等,服务器上的邮件也会做相应的动作。所以无论从浏览器登录邮箱或者客户端软件登录邮箱,看到的邮件以及状态都是一致的。
1.2 电子邮件收发原理
有了上述基本概念,要编写程序来发送和接收邮件,本质上就是:
编写MUA把邮件发到MTA;
编写MUA从MDA上收邮件。
发邮件时,MUA和MTA使用的协议就是SMTP,后面的MTA到另一个MTA也是用SMTP协议。
收邮件时,MUA和MDA使用的协议有两种:POP3和IMAP。
邮件客户端软件在发邮件时,会让你先配置SMTP服务器,也就是你要发到哪个MTA上。假设你正在使用163的邮箱,你就不能直接发到新浪的MTA上,因为它只服务新浪的用户,所以,你得填163提供的SMTP服务器地址:smtp.163.com,为了证明你是163的用户,SMTP服务器还要求你填写邮箱地址和邮箱口令,这样,MUA才能正常地把Email通过SMTP协议发送到MTA。
类似的,从MDA收邮件时,MDA服务器也要求验证你的邮箱口令,确保不会有人冒充你收取你的邮件,所以,Outlook之类的邮件客户端会要求你填写POP3或IMAP服务器地址、邮箱地址和口令,这样,MUA才能顺利地通过POP或IMAP协议从MDA取到邮件。

1.3 编码前的准备工作
使用Python收发邮件前,请先准备好至少两个电子邮件,如xxx@163.com,xxx@sina.com,xxx@qq.com等,注意两个邮箱不要用同一家邮件服务商。
最后特别注意,目前大多数邮件服务商都需要手动打开SMTP发信和POP收信的功能,否则只允许在网页登录,以163邮箱为例配置步骤如下:
(1)登陆自己的163邮箱,在设置中选择“POP3/SMTP/IMAP”,配置如下图,并记住SMTP服务器地址,后续会用到

(2)找到设置中的“客户端授权码”,开启这项功能并设置授权码,系统会提示不允许与登陆密码相同

二、用python3发邮件
SMTP是发送邮件的协议,Python对SMTP支持有smtplib和email两个模块,email负责构造邮件,smtplib负责发送邮件
2.1. 最简单的示例,发送纯文本邮件(无附件、正文里无图片和html语句)
需要导入的包如下:(关于第一个包后续展开讲,在这个最简单的例子中使用这个)
from email.mime.text import MIMEText
import smtplib
"""
发送频繁的话会被当做垃圾邮件,运行程序显示554错误
""" from email.mime.text import MIMEText
from email.utils import formataddr
import smtplib
# 输入发送人地址和口令:
from_addr = '***@163.com'
password = '***'#授权码而非登陆密码
# 输入收件人地址:
to_addr = '***@qq.com'
# 输入发件人SMTP服务器地址:
smtp_server = 'smtp.163.com'
#构造邮件
msg = MIMEText('这里写正文的内容……','plain','utf-8') #构造MIMEText对象
msg['From'] = formataddr(["发件人的昵称",from_addr]) #括号里的对应发件人邮箱昵称、发件人邮箱账号
msg['To'] = formataddr(["收件人的昵称",to_addr]) #括号里的对应收件人邮箱昵称、收件人邮箱账号
msg['Subject'] = "这里写主题" #邮件的主题,也可以说是标题
server = smtplib.SMTP(smtp_server, 25) # SMTP协议默认端口是25
#server.set_debuglevel(1)#打印出和SMTP服务器交互的所有信息
server.login(from_addr, password)#登录SMTP服务器
#sendmail()方法就是发邮件,由于可以一次发给多个人,所以传入一个list,邮件正文是一个str,as_string()把MIMEText对象变成str
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()#退出SMTP服务器
tips1:
from email.utils import formataddr中的formataddr使用help(formataddr)查看
可以看到formataddr和parseaddr是互逆的两个过程

举个例子:
formataddr(['我', 'learningatcisco-noreply@cisco.com'])的结果是:=?utf-8?b?5oiR?= <learningatcisco-noreply@cisco.com>
parseaddr('"我"<learningatcisco-noreply@cisco.com>')的结果是:'我', 'learningatcisco-noreply@cisco.com'
tips2:
password要填写授权码(也就是自己设置的),而不是邮箱登陆密码 tips3:
构造MIMEText对象时,第一个参数就是邮件正文,第二个参数是MIME的subtype,传入'plain'表示纯文本,最终的MIME就是'text/plain',最后一定要用utf-8编码保证多语言兼容性 tips4:
SMTP协议就是简单的文本命令和响应。login()方法用来登录SMTP服务器sendmail()方法就是发邮件,由于可以一次发给多个人(用“,”隔开),所以传入一个list邮件正文是一个str,as_string()把MIMEText对象变成str
查看收件箱的结果如下:

为什么没有显示昵称我也不太清楚……只试验了自己的邮箱,并没有进行大范围的测试
2.2 第二个示例——邮件正文中包含html语句(当然也可以包含纯文本文字)
在构造MIMEText对象时,把HTML字符串传进去,再把第二个参数由plain变为html就可以了
msg = MIMEText('<html><body><h1>Hello</h1>' +
'<p>send by <a href="http://www.python.org">Python</a>...</p>' +
'</body></html>', 'html', 'utf-8')#发送html
查看邮件结果:

2.3 发送不同格式的附件(当然也可以包含纯文本)
基本思路就是,使用MIMEMultipart来标示这个邮件是多个部分组成的,然后attach各个部分。如果是附件,则add_header加入附件的声明。
在python中,MIME的这些对象的继承关系如下:
官方参考:https://docs.python.org/3/library/email.mime.html

一般来说,不会用到MIMEBase,而是直接使用它的继承类。MIMEMultipart有attach方法,而MIMENonMultipart没有,只能被attach。
MIME有很多种类型,这个略麻烦,如果附件是图片格式,要用MIMEImage,如果是音频,要用MIMEAudio等等……但是可以作为附件的文件类型实在是太多了,记起来又很麻烦,这里有一个懒人方法就是,不管什么类型的附件,都用MIMEApplication,MIMEApplication默认子类型是application/octet-stream。
application/octet-stream表明“这是个二进制的文件,希望你们那边知道怎么处理”,然后客户端,比如qq邮箱,收到这个声明后,会根据文件扩展名来猜测。
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.mime.image import MIMEImage
#from email.mime.image import MIMEAudio #并没有找到MIMEAudio这个库 # 如名字所示Multipart就是分多个部分 # 构造一个MIMEMultipart对象代表邮件本身
msg = MIMEMultipart() # ---这是文字部分---
part1 = MIMEText("纯文本内容")
msg.attach(part1) # xlsx类型附件
part2 = MIMEApplication(open('痒痒鼠.xlsx', 'rb').read())
part2.add_header('Content-Disposition', 'attachment', filename="痒痒鼠.xlsx")
msg.attach(part2) #图片作为附件的时候,有时会出现图片不完整的情况
# jpg类型附件
part3 = MIMEApplication(open('qqq.jpg', 'rb').read())
part3.add_header('Content-Disposition', 'attachment', filename="qqq.jpg")
msg.attach(part3)
# jpg类型附件另一种方法
part33 = MIMEImage(open('qqq.jpg', 'rb').read())
part33.add_header('Content-Disposition', 'attachment', filename="qqq.jpg")
msg.attach(part33) # pdf类型附件
part4 = MIMEApplication(open('马蜂窝庐山.pdf', 'rb').read())
part4.add_header('Content-Disposition', 'attachment', filename="马蜂窝庐山.pdf")
msg.attach(part4) # mp3类型附件
part5 = MIMEApplication(open('天哪.mp3', 'rb').read())
part5.add_header('Content-Disposition', 'attachment', filename="天哪.mp3")
msg.attach(part5)
# mp3类型附件另一种方法,因为无法导入MIMEAudio这个库,失败
总结一下用法,可直接套用以下模板:
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication msg = MIMEMultipart()
#纯文本内容
part1 = MIMEText("纯文本内容")
msg.attach(part1)
# 添加附件
part2 = MIMEApplication(open('附件***.类型', 'rb').read())
part2.add_header('Content-Disposition', 'attachment', filename="附件***.类型")
msg.attach(part2)
查看邮件结果:

tips1:可以添加多个附件
tips2:附件为图片的时候有可能不完整
2.4 加密传输
使用标准的25端口连接SMTP服务器时,使用的是明文传输,发送邮件的整个过程可能会被窃听。要更安全地发送邮件,可以加密SMTP会话,实际上就是先创建SSL安全连接,然后再使用SMTP协议发送邮件。
某些邮件服务商,例如Gmail,提供的SMTP服务必须要加密传输。我们来看看如何通过Gmail提供的安全SMTP发送邮件。
必须知道,Gmail的SMTP端口是587,因此,修改代码如下:
smtp_server = 'smtp.gmail.com'
smtp_port = 587
server = smtplib.SMTP(smtp_server, smtp_port)
server.starttls()
# 剩下的代码和前面的一模一样:
server.set_debuglevel(1)
只需要在创建SMTP对象后,立刻调用starttls()方法,就创建了安全连接。后面的代码和前面的发送邮件代码完全一样。
三、用python3收邮件
讲完了发送邮件,现在讲解收取邮件
收取邮件就是编写一个MUA作为客户端,从MDA把邮件获取到用户的电脑或者手机上。收取邮件最常用的协议是POP协议,目前版本号是3,俗称POP3。
Python内置一个poplib模块,实现了POP3协议,可以直接用来收邮件。
注意到POP3协议收取的不是一个已经可以阅读的邮件本身,而是邮件的原始文本,这和SMTP协议很像,SMTP发送的也是经过编码后的一大段文本。
要把POP3收取的文本变成可以阅读的邮件,还需要用email模块提供的各种类来解析原始文本,变成可阅读的邮件对象。
所以,收取邮件分两步:
第一步:用poplib把邮件的原始文本下载到本地;
第二部:用email解析原始文本,还原为邮件对象。
自动化运维之使用Python3收发电子邮件~~~附源码的更多相关文章
- 自动化运维工具之ansible
自动化运维工具之ansible 一,ansible简介 ansible是新出现的自动化运维工具,基于Python开发,集合了众多运维工具(puppet.cfengine.chef.func.fab ...
- Day1 老男孩python自动化运维课程学习笔记
2017年1月7日老男孩python自动化运维课程正式开课 第一天学习内容: 上午 1.python语言的基本介绍 python语言是一门解释型的语言,与1989年的圣诞节期间,吉多·范罗苏姆为了在阿 ...
- Ansible_自动化运维《Ansible之初识-1》
1.Ansible简介 1.1 Ansible介绍 Ansible 是一个简单的自动化运维管理工具,基于Python开发,集合了众多运维工具(puppet.cfengine.chef.func.fab ...
- 企业级自动化运维工具应用实战-ansible
背景 公司计划在年底做一次大型市场促销活动,全面冲刺下交易额,为明年的上市做准备.公司要求各业务组对年底大促做准备,运维部要求所有业务容量进行三倍的扩容,并搭建出多套环境可以共开发和测试人员做测试,运 ...
- 项目实战10.1—企业级自动化运维工具应用实战-ansible
实战环境: 公司计划在年底做一次大型市场促销活动,全面冲刺下交易额,为明年的上市做准备.公司要求各业务组对年底大促做准备,运维部要求所有业务容量进行三倍的扩容,并搭建出多套环境可以共开发和测试人员做测 ...
- 【实战小项目】python开发自动化运维工具--批量操作主机
有很多开源自动化运维工具都很好用如ansible/salt stack等,完全不用重复造轮子.只不过,很多运维同学学习Python之后,苦于没小项目训练.本篇就演示用Python写一个批量操作主机的工 ...
- 自动化运维工具fabric使用教程
摘要:当需要同时管理许多服务器时,如果我们一台一台登陆上去操作会显得费时又费力.此时我们可以用fabric这个包提供的API来编写python脚本完成服务器集群的统一管理. 核心原理:fabric为主 ...
- 部署MySQL自动化运维工具inception+archer
***************************************************************************部署MySQL自动化运维工具inception+a ...
- python自动化运维之路~DAY5
python自动化运维之路~DAY5 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.模块的分类 模块,用一砣代码实现了某个功能的代码集合. 类似于函数式编程和面向过程编程,函数 ...
随机推荐
- Python list, dict, set, tuple
list方法 append: 添加一个新的元素到末尾 extend: 扩展元素 insert: 在任何位置插入元素 pop: 弹出末尾的元素 remove: remove first occurren ...
- SQL 分页实现
--通用分页 ALTER PROCEDURE [dbo].[Sys_Pagination_1] @tblName VARCHAR(2000) , -- 表名 @strGetFields VARCHAR ...
- spring boot 基础 多环境配置
对于多环境的配置,各种项目构建工具的思路基本上一致,都是通过配置多份不同环境的配置文件来区分. 1. 首先我们先创建不同环境下的属性文件,截图如下: application.properties 是 ...
- Educational Codeforces Round 51 (Rated for Div. 2)
做了四个题.. A. Vasya And Password 直接特判即可,,为啥泥萌都说难写,,,, 这个子串实际上是忽悠人的,因为每次改一个字符就可以 我靠我居然被hack了???? %……& ...
- ubuntu安装robo3t
直接在官网下载 解压文件(使用命令 tar -zxvf robo3t-1.2.1-linux-x86_64-3e50a65.tar.gz) 打开解压后的文件,进入bin文件,直接在终端运行 ./rob ...
- Centos_linux系统的区别及实际查看
在Linux系统查看系统版本为 32 位还是 64 位 [root@localhost ~]# cat /etc/redhat-release CentOS release 6.8 (Final) [ ...
- python基础-数据运算
*按位取反运算规则(按位取反再加1) 详解http://blog.csdn.net/wenxinwukui234/article/details/42119265 详细内容ht ...
- 703. 数据流中的第 K 大元素
设计一个找到数据流中第 K 大元素的类(class).注意是排序后的第 K 大元素,不是第 K 个不同的元素. 你的 KthLargest 类需要一个同时接收整数 k 和整数数组 nums 的构造器, ...
- sql server 索引总结三
一.非聚集索引维护 非聚集索引的行定位器值保持相同的聚集索引值,即使该聚集索引列物理上重新定位后,也是如此. 为了优化这个维护开销,SQL Server添加一个指向旧数据页的指针,以在页面分割之后指向 ...
- linux下安装和卸载mysql
卸载: 1 . rpm -qa | grep -i mysql命令查看已经安装过的组件. 2. 使用yum -y remove命令卸载已经安装的MySQL组件,使用下面的命令,对于上面已经安装 ...