Python电子邮件

电子邮件介绍

电子邮件的运作流程基本可以参考传统信封邮寄的过程。
假设我的电子邮件地址是ABC@qq.com,对方的电子邮件地址是DEF@163.com。然后我们通过Foxmail等软件写好信息后点击发送,电子邮件就发送出去了。注意:这些电子邮件被称为MUAMail User Agent -- 邮件用户代理

邮件发送出去后并不是直接到达对方电脑,而是发到MTAMail Transfer Agent -- 邮件传输代理,也就是那些邮件服务提供商,如腾讯、网易等。由于我的电子邮件是@qq.com,属于腾讯,所以邮件就先发送到腾讯提供的MTA,再由腾讯的MTA发送给对方的即网易的MTA,当然可能中间会有其他MTA,我们不关心具体线路,只关心速度。

邮件到达网易的MTA后,网易的MTA会把邮件投递到最终目的地MDAMail Delivery Agent -- 邮件投递代理。邮件到达MDA后,就静静地躺在网易的某个服务器上,存放在某个文件或特殊的数据库里,我们将这个长期保存邮件的地方称为电子邮箱。

由于对方不一定开机,不一定联网。对方要取到邮件,必须通过MUAMDA上把邮件取到自己电脑上。

所以一封电子邮件的旅程是

发件人 -> MUA -> MTA -> MTA -> 若干个MTA -> MDA <- MUA <- 收件人

所以编写程序发送和收取邮件的本质就是

  • 编写MUA将邮件发送到MTA
  • 编写MUAMDA上获取邮件。

发邮件时,MUAMTA之间、MTAMTA之间使用的协议是SMTP -- Simple Mail Transfer Protocol
收邮件时,MUAMDA使用的协议有两种,POP -- Post Office Protocol,目前版本是3,俗称POP3IMAP -- Internet Message Access Protocol,目前版本是4,优点是不但能取邮件,还可以直接操作MDA上存储的邮件,比如从收件箱移到垃圾箱,等等。

邮件客户端软件在发送邮件时,需要配置SMTP服务器,也就是要发送到哪个服务器的MTA上,如我的电子邮件地址是@qq.com,需要配置腾讯的SMTP服务器,同时为了证明你是腾讯的用户,还需要填上邮箱地址和邮箱口令,这样才能将邮件通过MUA发送到MTA上。

同理,邮件客户端在收取邮件时,需要配置POP3或IMAP服务器,以及提供邮箱地址和邮箱口令来验证身份。

SMTP发送邮件

SMTP是发送邮件的协议,Python已经内置了对SMTP协议的支持,可以发送纯文本邮件HTML邮件以及带附件的邮件
Python的内置模块email负责构建邮件,smtplib负责发送邮件。

纯文本邮件

我们以从一个QQ邮箱发送到另一个QQ邮箱(换成163邮箱也是可以的)为例。QQ邮箱的SMTP服务器地址是smtp.qq.com,默认是开启了SSL,故端口是465

MIME构造邮件,用SMTP_SSL构造连接SMTP服务的客户端。

# -*- coding:UTF-8 -*-

import re
from smtplib import SMTP_SSL
from email.mime.text import MIMEText

# 构造邮件 第一个参数是内容 第二个参数是邮件内容类型plain表示纯文本  第三个参数是编码
m = MIMEText("hello, send by cute boy in python.", _subtype='plain', _charset="utf-8")

from_addr = input("请输入邮箱地址: ")
passwd = input("请输入邮箱口令: ")

smpt_server = input("请输入SMPT服务器地址: ")
smpt_port = int(input("请输入SMPT服务器端口: ").strip())

to_addr = input("请输入收件人地址(多个用逗号或空格隔开): ")
addr_list = re.split("[\s,]+", to_addr)

# 开启SSL加密的SMTP
server = SMTP_SSL(smpt_server, smpt_port)
# 打印出和SMTP服务器交互的所有信息
server.set_debuglevel(1)
# 用邮件地址和授权码登录
server.login(from_addr, passwd)
# 发送邮件 可以发送多人所以收件地址是个list
# 邮件正文是str 所以需要使用as_string()方法将MIME变成str
server.sendmail(from_addr, addr_list, m.as_string())
server.quit()

效果图如下所示
在这里插入图片描述
收到的邮件如下所示。
在这里插入图片描述
我们发现收到的邮件没有主题信息,收件人名字没有显示,也没有友好地显示发件人地址。其原因就在于,邮件主题、如何显示收/发件人信息并不是通过SMTP协议发给MTA,而是包含在发给MTA的文本中的。我们需要将FromToSubject信息放在MIMEText里,这样才是一封完整的邮件。

使用自定义函数myformataddr来格式化FromToSubject信息。
m['To']接收的是字符串而不是list,如果有多个邮件地址,用,分隔即可。
完整代码如下,其实只是添加了格式化函数和FromToSubject信息。

# -*- coding:UTF-8 -*-

import re
from smtplib import SMTP_SSL
from email.mime.text import MIMEText
from email.header import Header
from email.utils import parseaddr, formataddr

# 格式化邮件地址 如果有中文就需要用Header进行编码
def myformataddr(s):
    name, addr = parseaddr(s)
    return formataddr((Header(name, charset="utf-8").encode(), addr))

# 构造邮件 第一个参数是内容 第二个参数是邮件内容类型plain表示纯文本  第三个参数是编码
m = MIMEText("hello, send by cute boy in python.", _subtype='plain', _charset="utf-8")

from_addr = input("请输入邮箱地址: ")
passwd = input("请输入邮箱口令: ")

smpt_server = input("请输入SMPT服务器地址: ")
smpt_port = int(input("请输入SMPT服务器端口: ").strip())

to_addr = input("请输入收件人地址(多个用逗号或空格隔开): ")
addr_list = re.split("[\s,]+", to_addr)

m['From'] = myformataddr("Gentleman <{}>".format(from_addr))
# 多个收件人用,分隔
to_str=",".join((myformataddr("beautiful girl-{} <{}>".format(i+1, addr)) for i,addr in enumerate(addr_list)))
m['To'] = to_str
# 如果有中文 需要用Header进行格式化
m['Subject'] = Header("to beautiful girl", charset="utf-8").encode()

# 开启SSL加密的SMTP
server = SMTP_SSL(smpt_server, smpt_port)
# 打印出和SMTP服务器交互的所有信息
server.set_debuglevel(1)
# 用邮件地址和授权码登录
server.login(from_addr, passwd)
# 发送邮件 可以发送多人所以收件地址是个list
# 邮件正文是str 所以需要使用as_string()方法将MIME变成str
server.sendmail(from_addr, addr_list, m.as_string())
server.quit()
HTML邮件

发送HTML邮件很简单,只需要把发送内容编程html内容,内容类型变成html即可。如下所示。
其他不变,只是需要修改MIMEText即可。

content = """\
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>文档标题</title>
</head>
<body>
    <h1>我的第一个HTML页面</h1>
    <p><a href="https://www.liaoxuefeng.com/wiki/1016959663602400/1017790702398272">Python教程</a></p>
</body>
</html>
"""
m = MIMEText(content, _subtype='html', _charset="utf-8")
带附件的邮件

譬如想发送一个带附件的邮件,邮件类型是HTML,然后HTML内容是可以引用附件的文件的如图片。

我们可以构造一个MIMEMultipart对象,然后往该对象添加一个MIMEText作为邮件正文,再继续往里面添加表示附件的MIMEBase对象。

类之间的关系图如下
我们可以用MIMEMultipart对象去attach附件,MIMENonMultipart只能被attach
附件类型是图片时可以用子类MIMEImage,是audio类型时可以用MIMEAudio,当不清楚类型是什么的时候可以用MIMEApplicationMIMEApplication默认子类型是·application/octet-stream`,表明这是个二进制文件,让收件箱服务器根据文件扩展名猜测如何处理。

Message
+- MIMEBase
   +- MIMEMultipart
   +- MIMENonMultipart
      +- MIMEMessage
      +- MIMEText
      +- MIMEImage
      +- MIMEApplication
      +- MIMEAudio
        
# -*- coding:UTF-8 -*-

import re
from smtplib import SMTP_SSL
from email import encoders
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.mime.base import MIMEBase
from email.header import Header
from email.utils import parseaddr, formataddr

# 格式化邮件地址 如果有中文就需要用Header进行编码
def myformataddr(s):
    name, addr = parseaddr(s)
    return formataddr((Header(name, charset="utf-8").encode(), addr))


def attach_pic(m, fpath, fname, ftype, fid):
    with open(fpath, "rb") as f:
        # 设置附件的MIME和文件名,这里是jpg类型  也可用MIMEImage
        mime = MIMEBase("image", ftype, filename=fname)
        # 加上必要的头信息
        mime.add_header('Content-Disposition', 'attachment', filename=fname)
        mime.add_header('Content-ID', '<{}>'.format(fid))
        mime.add_header('X-Attachment-Id', fid)
        # 读取附件内容
        mime.set_payload(f.read())
        # 用Base64编码
        encoders.encode_base64(mime)
        m.attach(mime)


def attach_normalfile(m, fpath, fname, fid):
    with open(fpath, "rb") as f:
        # Create an application/* type MIME document
        mime = MIMEApplication(f.read())
        # 加上必要的头信息
        mime.add_header('Content-Disposition', 'attachment', filename=fname)
        mime.add_header('Content-ID', '<{}>'.format(fid))
        mime.add_header('X-Attachment-Id', fid)
        m.attach(mime)



def create_mime_obj():

    content = """\
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>文档标题</title>
    </head>
    <body>
        <h1>我的第一个HTML页面</h1>
        <p><a href="https://www.liaoxuefeng.com/wiki/1016959663602400/1017790702398272">Python教程</a></p>
        <p><img src="cid:0"></p>
    </body>
    </html>
    """

    m = MIMEMultipart()
    # 添加文本信息 注意这里我添加的是html文本
    m.attach(MIMEText(content, _subtype='html', _charset="utf-8"))

    # 添加图片
    attach_pic(m, "d:/imgs/th.jpg", "th.jpg", "jpg", "0")
    attach_pic(m, "d:/imgs/IMG_7743.JPG", "IMG_7743.JPG", "jpg", "1")
    # 添加普通文件
    attach_normalfile(m, "d:/imgs/python简明教程中文.pdf", "python简明教程中文.pdf", "2")
    attach_normalfile(m, "d:/imgs/Hive优化.sql", "Hive优化.sql", "3")
    attach_normalfile(m, "d:/imgs/群星 - 心痛2010.mp3", "群星 - 心痛2010.mp3", "4")
    return m

m = create_mime_obj()

from_addr = input("请输入邮箱地址: ")
passwd = input("请输入邮箱口令: ")

smpt_server = input("请输入SMPT服务器地址: ")
smpt_port = int(input("请输入SMPT服务器端口: ").strip())

to_addr = input("请输入收件人地址(多个用逗号或空格隔开): ")
addr_list = re.split("[\s,]+", to_addr)

m['From'] = myformataddr("Gentleman <{}>".format(from_addr))
# 多个收件人用,分隔
to_str=",".join((myformataddr("beautiful girl-{} <{}>".format(i+1, addr)) for i,addr in enumerate(addr_list)))
m['To'] = to_str
# 如果有中文 需要用Header进行格式化
m['Subject'] = Header("to beautiful girl", charset="utf-8").encode()

# 开启SSL加密的SMTP
server = SMTP_SSL(smpt_server, smpt_port)
# 打印出和SMTP服务器交互的所有信息
server.set_debuglevel(1)
# 用邮件地址和授权码登录
server.login(from_addr, passwd)
# 发送邮件 可以发送多人所以收件地址是个list
# 邮件正文是str 所以需要使用as_string()方法将MIME变成str
server.sendmail(from_addr, addr_list, m.as_string())
server.quit()

在这里插入图片描述

POP3收取邮件

Python内置一个poplib模块,实现了POP3协议,可以直接用来收邮件。
由于用python收取邮件不常用,此处就不在这里贴实践代码了,更多细节可以参考廖雪峰老师的Python教程中关于POP3收取邮件这一部分

参考网址

廖雪峰老师的Python教程

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值