美文网首页
大师兄的Python学习笔记(十七): Mail编程

大师兄的Python学习笔记(十七): Mail编程

作者: superkmi | 来源:发表于2020-05-09 15:34 被阅读0次

    大师兄的Python学习笔记(十六): FTP与ftplib
    大师兄的Python学习笔记(十八): Python与HTTP

    一、关于电子邮件

    1. 电子邮件的传输概念
    简称 全称 含义
    MUA Mail User Agent 接收邮件所使用的邮件客户端,使用IMAP或POP3协议与服务器通信
    MTA Mail Transfer Agent 通过SMTP协议发送、转发邮件
    MDA Mail Deliver Agent 将MTA接收到的邮件保存到磁盘或指定地方,通常会进行垃圾邮件及病毒扫描
    MRA Mail Receive Agent 负责实现IMAP与POP3协议,与MUA进行交互
    SMTP Simple Mail Transfer Protocol 传输发送邮件所使用的标准协议
    IMAP Internet Message Access Protocol 接收邮件使用的标准协议之一
    POP3 Post Office Protocol 3 接收邮件使用的标准协议之一
    2. 电子邮件的发送之路

    1) Email从MUA发出

    • 发送方客户端:比如outlook等。

    2) Email发到MTA

    • 发送方的Email服务提供商,比如hotmail、新浪等。
    • 可能会途经多个MTA。

    3)Email发送到MDA

    • 接收方的Email服务提供商,比如网易、gamil等。
    • 可能会途径多个MTA。

    4)通过MUA将邮件接收到本地

    • 接收方客户端:比如foxmail等


    二、Mail编程

    Mail编程只需要关注三个环节:

    • 从发送方的MUA把邮件发到MTA
    • 从接收方的MUA把邮件从MDA接收到本地
    • 解析和撰写邮件
    1. 发送邮件
    1.1 关于SMTP协议
    • SMTP是一种提供可靠且有效的电子邮件传输的协议。
    • SMTP是建立在FTP文件传输服务上的一种邮件服务,主要用于系统之间的邮件信息传递,并提供有关来信的通知。
    • SMTP协议位于OSI七层模型的应用层。
    1.2 关于smtplib模块
    • smtplib模块是Python提供的工具,他对SMTP协议做了简单的封装。
    • 全部方法参考Python官方手册

    1) smtplib.SMTP(host, port, local_hostname)对象

    • 创建SMTP对象。
    • host: SMTP服务器主机。
    • port: SMTP服务使用的端口号,通常为25。
    • local_hostname: 如果SMTP在本地,可以提供本地名称。

    2) SMTP.sendmail(from, tos, msg)方法

    • 发送邮件
    • from: 发送方地址(可以不提供或自定义)
    • tos:接收方地址
    • msg:邮件内容,通常是Message对象。

    3) SMTP.login(user,password)方法

    • 如果SMTP服务器需要验证用户名密码。

    4) SMTP.starttls(keyfile=None, certfile=None, context=None)方法

    • 将连接加入TLS模式,对套接字进行SSL加密。
    >>>import smtplib
    >>>from email.mime.text import MIMEText
    
    >>>host = "邮箱host,比如xxx@163.com"
    >>>port = "邮箱port,比如25"
    >>>user = '邮箱用户名'
    >>>pwd = '邮箱密码'
    
    >>>send_from = "发送方地址"
    >>>send_to = "接收方地址"
    
    >>>def sort_msg(func):
    >>>    # 处理邮件内容
    >>>    def wrapper(arg):
    >>>        func(MIMEText(arg, 'plain', 'utf-8'))
    >>>    return wrapper
    
    >>>@sort_msg
    >>>def send_msg(msg):
    >>>    try:
    >>>        with smtplib.SMTP(host,port) as smtpObj: # 创建smtp对象
    >>>            print('登录邮件服务器.')
    >>>            smtpObj.login(user,pwd) # 登录邮件服务器
    >>>            print('发送邮件...')
    >>>            smtpObj.sendmail(send_from,send_to,msg.as_string()) # 发送邮件
    >>>        print('断开连接.')
    >>>    except smtplib.SMTPException as e:
    >>>        print(f'发送邮件失败:{e}')
    >>>
    >>>if __name__ == '__main__':
    >>>    send_msg('发送邮件测试...') # msg也可以是更复杂的Message对象
    登录邮件服务器.
    发送邮件...
    断开连接.
    
    2. 接收邮件
    2.1 关于POP协议
    • POP协议,邮局协议(Post Office Protocol), 用于电子邮件的接收。
    • 常用第三版 ,所以简称为POP3。
    2.2 关于poplib模块
    • poplib模块是Python提供的内置工具。
    • 可以通过套接字将邮件从MDA抓取到本地。
    • 全部方法参考Python官方手册

    1) poplib.POP3(host,port,timeout)

    • 创建POP3连接对象。

    2) poplib.POP3_SSL()

    • SSL加密。

    3) POP3.set_debuglevel(level)

    • 设置调试模式,可以看到与服务器的交互信息。

    4) POP3.getwelcome()

    • 获取邮件服务器欢迎信息。

    5) POP3.user(username)

    • 用户名验证。

    6) POP3.pass_(password)

    • 密码验证。

    7) POP3.apop(user, secret)

    • 用更安全的apop模式登陆。

    8) POP3.rpop(user)

    • 用rpop模式登陆。

    9) POP3.stat()

    • 获取邮箱中信件信息。

    10) POP3.list([which])

    • 返回邮件数量和每个邮件的大小。

    11) POP3.retr(which)

    • 收取邮件。

    12) POP3.dele(which)

    • 删除邮件。

    13) POP3.rset()

    • 服务器将重置所有标记为删除的邮件,用于撤消POP3.dele()命令。

    14) POP3.delePOP3.noop()

    • 什么也不做,用于保持在线。

    15) POP3.quit()

    • 确认变更,登出邮箱,断开服务器。

    16) POP3.top(which, howmuch)

    • 检索邮件标题,以及<howmuch>行内容简要。
    • 这个指令不会让邮件变成已读状态。
    >>>import poplib
    >>>from pprint import pprint
    
    >>>class Client:
    >>>    def __init__(self):
    >>>        self.host = '邮箱地址'
    >>>        self.user = '用户名'
    >>>         self.pwd = '密码'
    
    >>>     def login(self):
    >>>         print('登录邮件服务...')
    >>>         try:
    >>>             server = poplib.POP3(self.host)
    >>>             # 登录邮箱
    >>>             server.user(self.user)
    >>>             server.pass_(self.pwd)
    >>>             self.server = server
    >>>             print(server.getwelcome())
    >>>         except Exception as e:
    >>>             print(f'登录失败:e')
    
    >>>    def get_mail_list(self):
    >>>        # 获得邮箱目录
    >>>        if(not self.server): return
    >>>        msgCount,msgBytes = self.server.stat()
    >>>        print(f'共有:{msgCount}封邮件,大小:{msgBytes}字节')
    >>>        print(self.server.list())
    >>>        print(f'{"-"*20}')
    
    >>>    def get_last_mail(self):
    >>>        # 读取最新的一封邮件
    >>>        if (not self.server): return
    >>>        msgCount, msgBytes = self.server.stat()
    >>>        head,msg,octets = self.server.retr(msgCount)
    >>>        msg = b'\r\n'.join(msg).decode('utf-8')
    >>>        print(f'邮件标题:{head.decode()}')
    >>>        print(f'大小:{octets}字节')
    >>>        pprint(f'{msg}') // msg还需要被解析成为Message对象
    
    >>>    def quit(self):
    >>>        if (not self.server): return
    >>>        self.server.quit()
    >>>        print('断开服务器.')
    
    >>>if __name__ == '__main__':
    >>>    client = Client()
    >>>    client.login()
    >>>    client.get_mail_list()
    >>>    client.get_last_mail()
    >>>    client.quit()
    
    3. 解析和撰写邮件
    3.1 关于email包
    • email包是一个强大的工具集合,能够自动完成解析和撰写邮件信息背后的繁杂工作。
    • email包的接口操作围绕着Message对象。
    • email包的Python官方文档
    3.2 关于Message对象
    标题 说明
    类型 内容的类型,文本、HTML、图像,用MIME主类型及子类型表示,比如:text/html。
    题头 一个类似字典的映射接口,比如From、to、Cc等常用邮件题头。
    内容 可以是简单的字符串,也可以是Message对象组成的列表。
    3.3 撰写邮件

    1) 撰写简单邮件

    >>>from email.message import Message
    
    >>>mail = Message() # 创建MESSAGE对象
    >>>mail['subject'] = 'mail title'
    >>>mail['from'] = 'sender@test.com'
    >>>mail['to'] = 'receiver1@test.com'
    >>>mail['Cc'] = 'receiver2@test.com'
    >>>mail.set_payload('Hello World!')
    >>>print(mail)
    subject: mail title
    from: sender@test.com
    to: receiver1@test.com
    Cc: receiver2@test.com
    
    Hello World!
    

    2) 撰写带附件的邮件

    • Message对象的负载部分由字符串变为Message对象组成的列表。
    >>>from email.mime.multipart import MIMEMultipart
    >>>from email.mime.text import MIMEText
    >>>import os
    
    >>># 根Message
    >>>mail_top = MIMEMultipart()
    >>>mail_top['subject'] = 'mail title'
    >>>mail_top['from'] = 'sender@test.com'
    >>>mail_top['to'] = 'receiver1@test.com'
    >>>mail_top['Cc'] = 'receiver2@test.com'
    
    >>># 子Message1,文字信息
    >>>mail_sub1 = MIMEText('Hello World!\n')
    >>>mail_top.attach(mail_sub1)
    
    >>># 子Message2,附件
    >>>mail_sub2 = MIMEText(open(os.path.join('d:\\','test.txt')).read())
    >>>mail_sub2.add_header('Content-Disposition','attachment',fiename='test.txt')
    >>>mail_top.attach(mail_sub2)
    
    >>>print(mail_top)
    Content-Type: multipart/mixed; boundary="===============1263352342158858646=="
    MIME-Version: 1.0
    subject: mail title
    from: sender@test.com
    to: receiver1@test.com
    Cc: receiver2@test.com
    
    --===============1263352342158858646==
    Content-Type: text/plain; charset="us-ascii"
    MIME-Version: 1.0
    Content-Transfer-Encoding: 7bit
    
    Hello World!
    
    --===============1263352342158858646==
    Content-Type: text/plain; charset="us-ascii"
    MIME-Version: 1.0
    Content-Transfer-Encoding: 7bit
    Content-Disposition: attachment; fiename="test.txt"
    
    
    --===============1263352342158858646==--
    

    3) 撰写带图片附件的邮件

    >>>from email.mime.multipart import MIMEMultipart
    >>>from email.mime.image import MIMEImage
    >>>import os
    
    >>># 根Message
    >>>mail_top = MIMEMultipart()
    >>>mail_top['subject'] = 'mail title'
    >>>mail_top['from'] = 'sender@test.com'
    >>>mail_top['to'] = 'receiver1@test.com'
    >>>mail_top['Cc'] = 'receiver2@test.com'
    
    >>># 子Message图片附件
    >>>mail_sub2 = MIMEImage(open(os.path.join('d:\\','test.jpg'),'rb').read())
    >>>mail_sub2.add_header('Content-Disposition','attachment',fiename='test.jpg')
    >>>mail_top.attach(mail_sub2)
    
    >>>print(mail_top.as_string())
    Content-Type: multipart/mixed; boundary="===============9092138279492849864=="
    MIME-Version: 1.0
    subject: mail title
    from: sender@test.com
    to: receiver1@test.com
    Cc: receiver2@test.com
    >
    --===============9092138279492849864==
    Content-Type: image/png
    MIME-Version: 1.0
    Content-Transfer-Encoding: base64
    Content-Disposition: attachment; fiename="test.jpg"
    
    iVBORw0KGgoAAAANSUhEUgAAAiwAAADrCAIAAAGyYGoNAAAAAXNSR0IArs4c6QAA>AARnQU1BAACx
    jwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAEdZSURBVHhe7Z35l61VeefzF/RavVb/>0qtXJyaU
    ... ...
    
    3.4 解析邮件
    • 将邮件内容解析成为Message对象。

    1) 解析邮件

    • 使用内置工具Parser()一步就可以将邮件解析成Message对象。
    • 即是是包含附件的邮件现在也可以解析。
    >>>from email.parser import Parser
    
    >>>received_text= "subject: mail title,\nfrom: sender@test.com,\nto: receiver1@test.com,\nCc: >>>>receiver2@test.com,\nHello World!" # 示例字符串
    >>>mail = Parser().parsestr(received_text) # 解析成为Message对象
    >>>print(mail['subject'])
    >>>print(mail['to'])
    mail title,
    receiver1@test.com,
    

    2) 解析地址

    • 使用email.tuils.parseaddr函数可以将标准邮件地址解析成名字和地址。
    >>>from email.parser import Parser
    >>>from email.utils import parseaddr
    
    >>>text= mail_top.as_string()
    >>>mail = Parser().parsestr(text) # 解析成为Message对象
    >>>print(mail['to']) # 获取的地址
    "Donald Trump" <trump@dump.com>
    
    >>>name,addr = parseaddr(mail['to']) # 解析地址
    >>>print(f'name:{name}')
    >>>print(f'address:{addr}')
    name:Donald Trump
    address:trump@dump.com
    

    3) 名称解码

    • 使用email.header.decode_header在不转码的情况下获得编码的邮件内容,比如subject。
    >>>from email.header import decode_header
    >>>s = decode_header('=?iso-8859-1?q?p=F6stal?=')
    >>>print(s)
    [(b'p\xf6stal', 'iso-8859-1')]
    

    4) 遍历Message对象

    • 可以使用Message.walk()方法获得Message对象树的生成器。
    >>>from email.mime.multipart import MIMEMultipart
    >>>from email.mime.image import MIMEImage
    >>>from email.mime.text import MIMEText
    >>>import os
    
    >>># 根Message
    >>>mail_top = MIMEMultipart()
    >>>mail_top['subject'] = b'+OK Welcome to coremail Mail Pop3 Server (163coms[10774b260cc7a37d26d71b52404dcf5cs])'
    >>>mail_top['from'] = 'test <test@test.com>'
    >>>mail_top['to'] = '"Donald Trump" <trump@dump.com>'
    >>>mail_top['Cc'] = 'receiver2@test.com'
    
    >>># 子Message文字部分
    >>>mail_sub1 = MIMEText('Hello World!','plain', 'utf-8')
    >>>mail_top.attach(mail_sub1)
    
    >>># 子Message图片附件
    >>>mail_sub2 = MIMEImage(open(os.path.join('d:\\','test.jpg'),'rb').read())
    >>>mail_sub2.add_header('Content-Disposition','attachment',fiename='test.jpg')
    >>>mail_top.attach(mail_sub2)
    
    >>>for part in mail_top.walk(): # 获得对象树的生成器
    >>>    print(part.get_content_type())
    multipart/mixed
    text/plain
    image/png
    

    参考资料



    本文作者:大师兄(superkmi)

    相关文章

      网友评论

          本文标题:大师兄的Python学习笔记(十七): Mail编程

          本文链接:https://www.haomeiwen.com/subject/czhjghtx.html