如何利用python自动发送邮件?

447 阅读8分钟

前言

作为一个吃货,每周都想吃出新意,尝试不同的菜品。但是每次都要重复运行之前文章中的代码,很是麻烦。

所以,我们想到一个方法,把程序爬取到的数据以邮件的形式定时发送到邮箱,这样一来,不论何时何地,我们都可以收到程序爬取的结果。

本文将抽丝剥茧,一步步讲述如何使用python实现自动发送邮件。为防止影响观感、篇幅冗长,定时功能将在另外一篇文章中展示。

需求分析

我们可以把当前的项目分成两个模块:

  • 爬虫(爬取菜单)
  • 通知(邮件)

对爬虫部分,我们比较熟悉,这里不再多说;

对通知部分,选择的是用邮件来通知,我们选择smtplibemail库来实现这一需求,其中smtplib库主要负责的是横向的连接服务器、登录、发送和退出,而email库主要负责的是邮件主题和正文。

爬取菜单

参考这篇文章,最终我们可以得到一个菜谱的excel表格,其中的信息包括菜名,食材,以及菜品做法的详情页URL。

通知(以邮件形式)

先来模仿一下平时我们发邮件时计算机的操作:

模拟邮箱.png

以QQ邮箱为例,先来看连接服务器。

连接服务器需要用到smtplib库。为什么叫这个名字呢?其实,SMTP代表简单邮件传输协议,是一种计算机之间发邮件的约定,lib就是库的意思。

来看下具体怎么用smtplib库来连接服务器:

  # smtplib是python的一个内置库,不需要额外安装
import smtplib

 # 把qq邮箱的服务器地址赋值到变量mailhost上
mailhost='smtp.qq.com'

# 实例化一个SMTP_SSL对象
qqmail = smtplib.SMTP_SSL(host=mail_host) 

# 连接服务器,第一个参数是服务器地址,第二个参数是SMTP端口号。
qqmail.connect(host=mail_host, port=465) 

QQ邮箱的服务器地址可以在QQ邮箱的帮助中心查到,这里直接列出:

image.png

这样就拿到了qq邮箱的smtp地址。如果想用网易邮箱,也可以搜索网易邮箱的smtp服务器地址。

端口号的选择不是唯一的,具体的区别可以百度,这里不再赘述。这里我们使用推荐的端口号465,安全性更高一点。

连接服务器就讲完,接下来就是通过账号和密码登录邮箱

# 此处省略连接服务器的代码

 # 获取邮箱账号
account = input('请输入你的邮箱:')
 # 获取邮箱密码
password = input('请输入你的密码:')
 # 登录邮箱
qqmail.login(account,password)
 # 获取收件人的邮箱
receiver=input('请输入收件人的邮箱:')

解释一下password = input('请输入你的密码:')这行代码,它可不是我们平时登录邮箱的密码

这个密码需要我们去到这里获取:打开QQ邮箱官网,登录你的邮箱。

然后点击位于顶部的【设置】按钮,选择【账户设置】 image.png

然后下拉到这个位置,点击开启:

image.png

发送短信,通过验证:

image.png

验证成功后,QQ邮箱会提供给你一个授权码,注意保护好你的授权码:

image.png

接下来,在使用SMTP服务登录邮箱时,就可以输入这个授权码作为密码登录了。

填写主题和撰写正文,在这里需要用到email库。

先上代码:

# email 用于构建邮件内容
from email.mime.application import MIMEApplication
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# 用于构建邮件头
from email.header import Header


# 构建MIMEMultipart对象,代表邮件本身,可以往里面添加文本、图片、附件等
mime = MIMEMultipart()

# ---------------------------------------------------------------------

# 用input()获取邮件主题
subject_content = input('请输入你的邮件主题:')
# 设置发信邮箱,注意严格遵守格式
mime["From"] = account
# 设置接收邮箱,注意严格遵守格式
mime["To"] = receiver
# 实例化了一个Header邮件头对象,设置邮件主题
mime["Subject"] = Header(subject_content, 'utf-8')

# ---------------------------------------------------------------------

content = input('请输入邮件正文:')
# 实例化一个MIMEText邮件对象,输入你的邮件正文。该对象需要写进三个参数,分别是邮件正文,文本格式和编码.
message_text = MIMEText(content, 'plain', 'utf-8')
# 向MIMEMultipart对象中添加文本对象
mime.attach(message_text)

# ---------------------------------------------------------------------
# 二进制读取图片
image_filename = 'food.jpg'
image_file = open(image_filename, 'rb')
# 设置读取获取的二进制数据
message_image = MIMEImage(image_file.read())
message_image.add_header('Content-Disposition', 'attachment', filename=image_filename)
# 关闭刚才打开的文件
image_file.close()
# 添加图片文件到邮件信息当中去
mime.attach(message_image)

# ---------------------------------------------------------------------

# 构造附件
file_food = open('menu.xlsx', 'rb')
message_file = MIMEApplication(file_food.read())
# 设置附件信息
message_file.add_header('Content-Disposition', 'attachment', filename='menu.xlsx')
# 关闭刚才打开的文件
file_food.close()
# 添加附件到邮件信息当中去
mime.attach(message_file)

由于MIME有很多种类型,用起来有些麻烦。如果邮件包含图片,要用MIMEImage,如果包含音频,要用MIMEAudio,如果包含文本信息,要用MIMEText... image.png

所以有一个便捷的方法就是:

不管包含什么类型的内容,都使用MIMEMultipart来标示这个邮件是多个部分组成的,然后attach各个部分。如果是附件,则实例化一个MIMEApplication,然后使用add_header添加附件的声明。 抽取部分代码,可以比较一下图片和Excel表格异同

mime = MIMEMultipart()

# 图片
message_image = MIMEImage(image_file.read())
mime.attach(message_image)

# Excel表格
message_file = MIMEApplication(file_food.read())
mime.attach(message_file)

简单说一下这几行代码:

mime["From"] = account
mime["To"] = receiver
mime['Subject'] = Header(subject_content, 'utf-8')

我们可以看到用法是相似的,以其中一个为例,mime['Subject']代表着根据MIMEMultipart类里面的Subject的属性名取出该属性。

需要注意的是,不是每一个类都可以这样访问其属性的,之所以能这样访问是因为这个MIMEMultipart的类实现了这个功能。

所以,mime['Subject'] = Header(subject, 'utf-8') 就是在为mime['Subject']这个属性赋值。

有时候,我们想要在邮件的正文中插入图片,而不是将图片作为附件发送,那么如何实现呢?

首先,MIMEText无法直接显示图片的,但是,MIMEText支持HTML格式。 这样的话,我们完全可以通过HTML中的<img>标签显示数据,这样就能在正文中插入图片了。 如图(已经原作者许可):

image.png

详细大家可以参考这篇文章

接下来是最后两步:发送邮件和退出邮箱,代码很简单:

# 此处省略连接服务器的代码

# 此处省略登录邮箱的代码

# 此处省略主题与正文的代码

try:
    # 发送邮件,调用了sendmail()方法,写入三个参数,分别是发件人,收件人,和字符串格式的正文。
    qqmail.sendmail(account, receiver, mime.as_string())
    print("邮件发送成功")
except smtplib.SMTPException:
    print("Error: 无法发送邮件")
finally:
    # 退出邮箱
    qqmail.quit()

我们希望发送成功后能显示“邮件发送成功”,失败的时候能提示我们“邮件发送失败”,可以使用try-except-finally语句块来实现。

所以,将各部分代码组装起来:

# smtplib 用于邮件的发信动作
import smtplib
# email 用于构建邮件内容
from email.mime.application import MIMEApplication
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# 用于构建邮件头
from email.header import Header

# 配置发信服务器,把qq邮箱的服务器地址赋值到变量mail_host上
mail_host = 'smtp.qq.com'
# 实例化一个SMTP_SSL对象
qqmail = smtplib.SMTP_SSL(host=mail_host)
# 连接服务器,第一个参数是服务器地址,第二个参数是SMTP端口号。
qqmail.connect(host=mail_host, port=465)

# ---------------------------------------------------------------------

# 获取邮箱账号
account = input('请输入你的邮箱:')
# 获取邮箱密码
password = input('请输入你的密码:')
# 登录邮箱,第一个参数为邮箱账号,第二个参数为邮箱密码
qqmail.login(account, password)

# 获取收件人的邮箱
receiver = input('请输入收件人的邮箱:')

# 构建MIMEMultipart对象,代表邮件本身,可以往里面添加文本、图片、附件等
mime = MIMEMultipart()

# ---------------------------------------------------------------------

# 用input()获取邮件主题
subject_content = input('请输入你的邮件主题:')
# 设置发送者,注意严格遵守格式,里面邮箱为发件人邮箱
mime["From"] = account
# 设置接受者,注意严格遵守格式,里面邮箱为接受者邮箱
mime["To"] = receiver
# 实例化了一个Header邮件头对象,设置邮件主题
mime["Subject"] = Header(subject_content, 'utf-8')

# ---------------------------------------------------------------------

content = input('请输入邮件正文:')
# 实例化一个MIMEText邮件对象,输入你的邮件正文该对象需要写进三个参数,分别是邮件正文,文本格式和编码.
message_text = MIMEText(content, 'plain', 'utf-8')
# 向MIMEMultipart对象中添加文本对象
mime.attach(message_text)

# ---------------------------------------------------------------------
# 二进制读取图片
image_filename = 'food.jpg'
image_file = open(image_filename, 'rb')
# 设置读取获取的二进制数据
message_image = MIMEImage(image_file.read())
message_image.add_header('Content-Disposition', 'attachment', filename=image_filename)
# 关闭刚才打开的文件
image_file.close()
# 添加图片文件到邮件信息当中去
mime.attach(message_image)

# ---------------------------------------------------------------------

# 构造附件
file_food = open('menu.xlsx', 'rb')
message_file = MIMEApplication(file_food.read())
# 设置附件信息
message_file.add_header('Content-Disposition', 'attachment', filename='menu.xlsx')
# 关闭刚才打开的文件
file_food.close()
# 添加附件到邮件信息当中去
mime.attach(message_file)

# ----------------------------------------------------------------------

try:
    # 发送邮件,调用了sendmail()方法,写入三个参数,分别是发件人,收件人,和字符串格式的正文。
    qqmail.sendmail(account, receiver, mime.as_string())
    print("邮件发送成功")
except smtplib.SMTPException:
    print("Error: 无法发送邮件")
finally:
    # 退出邮箱
    qqmail.quit()

代码运行效果如图:

image.png

收到的邮件如图: image.png

END

本文主要讲述了如何使用python发送邮件,将本文的代码封装成类和函数,使用起来会更便捷。但仅仅是实现了自动发送邮件,并未实现定时功能,我会在后续文章讲述,各位敬请期待。

本人水平有限,非常欢迎各位掘友予以批评、指正。