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

2020-05-09  本文已影响0人  superkmi

大师兄的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编程只需要关注三个环节:

1. 发送邮件
1.1 关于SMTP协议
1.2 关于smtplib模块

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协议
2.2 关于poplib模块

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包
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 解析邮件

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)

上一篇下一篇

猜你喜欢

热点阅读