• 自动化运维之使用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文本文件,而对于一些二进制数据文件需要进行编码后才能传送。

    MIME——Multipose Internet Mail Extensions,多途径Internet邮件扩展协议。是一种编码标准,它解决了SMTP只能传送ASCII文本的限制。MIME定义了各种类型数据,如声音、图像、表格、二进制数据等的编码格式,通过对这些类型的数据编码并将它们作为邮件中的附件进行处理,以保证这些部分内容完整、正确地传输。因此,MIME增强了SMTP的传输功能,统一了编码规范。

    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.comxxx@sina.comxxx@qq.com等,注意两个邮箱不要用同一家邮件服务商。

    最后特别注意,目前大多数邮件服务商都需要手动打开SMTP发信和POP收信的功能,否则只允许在网页登录,以163邮箱为例配置步骤如下:

    (1)登陆自己的163邮箱,在设置中选择“POP3/SMTP/IMAP”,配置如下图,并记住SMTP服务器地址,后续会用到

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

    二、用python3发邮件

    SMTP是发送邮件的协议,Python对SMTP支持有smtplibemail两个模块,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

    邮件正文是一个stras_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解析原始文本,还原为邮件对象。

  • 相关阅读:
    Python解析excel文件并存入sqlite数据库
    python简单爬虫
    Python开发简单记事本
    Django初体验——搭建简易blog
    LeetCode — (1)
    机器学习(九)—FP-growth算法
    机器学习(八)—Apriori算法
    linux下debug工具
    linux同步与通信
    后台开发之IO缓冲区管理
  • 原文地址:https://www.cnblogs.com/aby321/p/10366205.html
Copyright © 2020-2023  润新知