电子邮件

在web程序中,经常会需要发送电子邮件。比如,在用户注册账户时发送确认邮件;定期向用户发送热门内容或是促销信息等等。在Web程序中发送电子邮件并不复杂,借助扩展Flask-Mail或是第三方邮件服务,只需要几行代码就可以发送邮件。

下面例子中,我们使用一封示例邮件,邮件仅包含几个必要的字段,如下:

标准的收信方和发信方字符串由姓名和邮箱地址两部分组成,,二者由空格相隔,比如“姓名 <Email地址>”。字符串中的姓名是可选的,收信方一般可以不写姓名,这时可以直接写出邮箱地址,比如”hello@example.com”。

使用Flask-Mail发送电子邮件

扩展Flask-Mail包装了python标准库中的smtplib包,简化了Flask程序中发送电子邮件的过程。我们使用pipenv安装Flask-Mail:

(Lenovo-ezd1lI9Y) C:\Users\Lenovo>pipenv install flask-mail

Installing flask-mail...

Adding flask-mail to Pipfile's [packages]...

Installation Succeeded

和其他扩展类似,我们实例化Flask-Mail提供的Mail类并传入程序实例以完成初始化,如下所示:

from flask_mail import Mail

app = Flask(__name__)
mail = Mail(app)

配置Flask-Mail
Flask_Mail通过链接SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)服务器来发送邮件。因此,在开始发送电子邮件前,我们需要配置SMTP服务器。如果电脑上已经设置了SMTP服务器,那么无需过多的配置即可使用,默认的邮件服务器配置即为localhost,端口为25.
在开发和测试阶段,我们可以使用邮件服务提供商的SMTP服务器(比如Bmail),这时我们需要对Flask-Mail进行配置。下面列出了Flask-Mail提供的常用配置变量。



对发送的邮件进行加密可以避免邮件在发送过程中被第三方截获和篡改。SSL(Security Socket Layer,安全套接字层)和TLS(Transport Layer Sceurity,传输层安全)是两种常用的电子邮件安全协议。TLS继承了SSL,并在SSL的基础上做了一些改进(TLS是后期版本的SSL)。所以,在大多数情况下,名词SSL和TLS可以互换使用。他们通过将MAIL_USE_SSL设置为True开启。STARTTLS是另一种加密方式,它会对不安全的连接进行升级(使用SSL或TLS)。尽管它的名字中包含TLS,但也可能会使用SSL加密。根据加密的方式不同,端口也要相应改变,如下所示:
SSL/TLS加密

MAIL_USE_SSL = True

MAIL_PORT = 465

STARTTLS加密

MAIL_USE_TLS = True

MAIL_PORT = 587

当不对邮件进行加密时,邮件服务器的端口使用默认的25端口。

常用的电子邮箱服务提供商的SMTP配置信息如下所示:

163邮箱的SMTP服务器不支持STARTTLS,你需要使用SSL/TLS加密。集体来说就是将MAIL_USE_SSL设为True,MAIL_PORT设为465.

要使用这些邮箱服务,需要访问对应的网站注册一个账户。开启邮箱的SMTP服务和获取授权码等操作均可以在个邮箱主页-设置(-账户)中找到。

Gmail、Outlook、QQ邮箱等这类服务被称为EPA(Email Service Provider),只适用于个人业务使用,不适合用来发送事务邮件(Transactional Email)。对于需要发送大量邮件的事务型邮件任务,更好的选择是使用自己设置的SMTP服务器或是类似SendGrid、Mailgun的事务邮件服务提供商(Transactional Email Service),后边会具体介绍。

在程序中,随着配置逐渐增多,我们改用app.config对象的update()方法来加载配置:

app.py:邮件服务器配置

from flask import Flask
from flask_mail import Mail app = Flask(__name__)
app.config.update(
MAIL_SERVER = os.getenv('MAIL_SERVER'),
MAIL_PORT = 678,
MAIL_USE_TLS = True,
MAIL_USEERNAME = os.getenv('MAIL_USERNAME'),
MAIL_PASSWORD = os.getenv('MAIL_PASSWORD'),
MAIL_DEFAULT_SENDER = ('Grey Li', os.getenv('MAIL_USERNAME'))
) mail = Mail(app)

在实例化Mail类时,Flask-Mail会获取配置以创建一个用于发信的对象,所以确保在实例化Mail类之前加载配置。

在我们的配置中,邮箱账户的密码属于敏感信息,不能直接写在脚本中,所以设置为从系统变量中获取。另外,在生产环境中,我们通常会使用不同的邮件服务器地址,所以这里也从环境变量中读取。

你可以使用export/set命令设置环境变量,为了方便管理,我们把这些环境变量存储在.env文件中:

MAIL_SERVER = 'smtp.qq.com'
MAIL_USERNAME = '367224698@qq.com'
MAIL_PASSWORD = 'ljfitqzfphlibjdj'

注意MAIL_PASSWORD是在qq邮箱的设置-账户中通过手机发送短信,获取的授权码,并不是qq的密码,需要开发smtp服务


默认发信人由一个两元素元祖组成,即(姓名,邮箱地址),比如:

MAIL_DEFAULT_SENDER = ('Your Name', 'your_name@example.com')

需要注意,使用邮件服务提供商提供的SMTP服务器发信时,发信人字符串中的邮件地址必须和邮箱地址相同,你可以直接只用MAIL_USERNAME的值构建发信人地址:

MAIL_DEFAULT_SENDER = ('Your Name', os.getenv('MAIL_USERNAME'))

Flask-Mail会把这个元祖转换为标准的发信人格式,即Your Name <your_name@example.com>。 你可以直接以这种方式指定发信人,比如:

MAIL_DEFAULT_SENDER = 'Your Name <your_name@example.com>'

设置默认发信人后,在发信时就可以不用再指定发信人。

构建邮件数据

下面我们借助Python shell演示发送邮件的过程。邮件通过从Flask-Mail中导入的Message类表示,而发信功能通过我们在程序包的构造文件中创建的mail对象实现,我们先进性导入:

>>> from flask_mail import Message
>>> from app import mail

一封邮件至少要包含主题、收件人、正文、发信人这几个元素。发信人(sender)在前面我们已经使用MAIL_DEFAULT_SENDER配置变量指定过了,剩下的分别通过Message类的构造方法中的subject、recipients、body关键字传入参数,其中recipients为一个包含电子邮件地址的列表。

message = Message(subject = 'title', recipients=['367224698@qq.com'], body='body')
加载环境变量方式:

1、flask项目可以通过.env加载环境变量,加载方式是运行 flask run或flask shell时加载

2、pipenv也可以通过.env加载环境变量,进入pipenv shell虚拟环境后,修改.env环境变量后再启动flask app: flask run,flask还是会用原来的环境变量,原因是pipenv shell加载了环境变量并进行了缓存,然后flask加载环境变量时不会进行覆盖。

此时可以退出pipenv shell然后再重新进入

和发信人字符串类似,收信人字符串可以为两种形式:

‘Sam <sam@example.com>’或’sam@example.com’。

发送邮件
>>>mail.send(message)

完整的发送实例邮件的代码如下:

>>> from flask_mail import Message
>>> from app import mail
>>> app.config['MAIL_PORT']
''
>>> message = Message(subject = 'title', recipients=['367224698@qq.com'], body='body')
>>> mail.send(message)

在实际操作时,报了错误:SMTPSenderRefused: (503, 'Error: need EHLO and AUTH first !', u'367224698@qq.com'),原因尚未搞清楚,配置都感觉ok,但还是报错,怀疑是QQ邮箱服务器以为我的恶意的客户端。。。

>>> from flask_mail import Message
>>> from app import mail
>>> app.config['MAIL_PORT']
''
>>> message = Message(subject = 'title', recipients=['367224698@qq.com'], body='body')
>>> mail.send(message)
send: 'ehlo [172.20.10.4]\r\n'
reply: '250-smtp.qq.com\r\n'
reply: '250-PIPELINING\r\n'
reply: '250-SIZE 73400320\r\n'
reply: '250-AUTH LOGIN PLAIN\r\n'
reply: '250-AUTH=LOGIN\r\n'
reply: '250-MAILCOMPRESS\r\n'
reply: '250 8BITMIME\r\n'
reply: retcode (250); Msg: smtp.qq.com
PIPELINING
SIZE 73400320
AUTH LOGIN PLAIN
AUTH=LOGIN
MAILCOMPRESS
8BITMIME
send: u'mail FROM:<367224698@qq.com> size=263\r\n'
reply: '503 Error: need EHLO and AUTH first !\r\n'
reply: retcode (503); Msg: Error: need EHLO and AUTH first !
send: 'rset\r\n'
reply: '250 Ok\r\n'
reply: retcode (250); Msg: Ok
send: 'quit\r\n'
reply: '221 Bye\r\n'
reply: retcode (221); Msg: Bye
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "c:\users\lenovo\.virtualenvs\lenovo-ezd1li9y\lib\site-packages\flask_mail.py", line 492, in send
message.send(connection)
File "c:\users\lenovo\.virtualenvs\lenovo-ezd1li9y\lib\site-packages\flask_mail.py", line 427, in send
connection.send(self)
File "c:\users\lenovo\.virtualenvs\lenovo-ezd1li9y\lib\site-packages\flask_mail.py", line 192, in send
message.rcpt_options)
File "c:\python27\Lib\smtplib.py", line 737, in sendmail
raise SMTPSenderRefused(code, resp, from_addr)
SMTPSenderRefused: (503, 'Error: need EHLO and AUTH first !', u'367224698@qq.com')
flask_mail发送163邮件

下面用163邮箱试一下

先进入163邮件开启smtp服务,获取授权码

授权码就是客户端登录163邮箱时的密码,用户名就是邮箱地址

获取授权码后,记录下来

在.env文件中设置邮箱信息,在命令行中运行flask shell时,会自动到该文件中获取环境变量

email\.env:

FLASK_ENV=development
MAIL_SERVER = 'smtp.163.com'
MAIL_USERNAME = 'xiaxiaoxu1987@163.com'
MAIL_PASSWORD = 'FOREVER022941' email\app.py:
from flask import Flask from flask_mail import Mail, Message import os app = Flask(__name__)
app.jinja_env.trim_blocks = True
app.jinja_env.lstrip_blocks = True #设置app.config信息,重点是用户名和密码
app.config.update(
SECRET_KEY = "SECRET KEY",
MAIL_SERVER = os.getenv('MAIL_SERVER'),
MAIL_PORT = 465,
#MAIL_PORT = 587,
#MAIL_USE_TLS = True,
MAIL_USE_SSL = True,
MAIL_USERNAME = os.getenv('MAIL_USERNAME'),
MAIL_PASSWORD = os.getenv('MAIL_PASSWORD'),
MAIL_DEFAULT_SENDER = (os.getenv('MAIL_USERNAME'))
)

命令行界面,操作发送邮件

>>> from flask_mail import Message
>>> from app import mail
>>> msg = Message('text', sender = 'xiaxiaoxu1987@163.com', recipients=['367224698@qq.com'])
>>> msg.body = 'body'
>>> mail.send(msg)
send: 'ehlo [172.20.10.4]\r\n'
reply: '250-mail\r\n'
reply: '250-PIPELINING\r\n'
reply: '250-AUTH LOGIN PLAIN\r\n'
reply: '250-AUTH=LOGIN PLAIN\r\n'
reply: '250-coremail 1Uxr2xKj7kG0xkI17xGrU7I0s8FY2U3Uj8Cz28x1UUUUU7Ic2I0Y2UrHJsU2UCa0xDrUUUUj\r\n'
reply: '250-STARTTLS\r\n'
reply: '250 8BITMIME\r\n'
reply: retcode (250); Msg: mail
PIPELINING
AUTH LOGIN PLAIN
AUTH=LOGIN PLAIN
coremail 1Uxr2xKj7kG0xkI17xGrU7I0s8FY2U3Uj8Cz28x1UUUUU7Ic2I0Y2UrHJsU2UCa0xDrUUUUj
STARTTLS
8BITMIME
send: 'AUTH PLAIN AHhpYXhpYW94dTE5ODdAMTYzLmNvbQBGT1JFVkVSMDIyOTQx\r\n'
reply: '235 Authentication successful\r\n'
reply: retcode (235); Msg: Authentication successful
send: u'mail FROM:<xiaxiaoxu1987@163.com>\r\n'
reply: '250 Mail OK\r\n'
reply: retcode (250); Msg: Mail OK
send: u'rcpt TO:<367224698@qq.com>\r\n'
reply: '250 Mail OK\r\n'
reply: retcode (250); Msg: Mail OK
send: 'data\r\n'
reply: '354 End data with <CR><LF>.<CR><LF>\r\n'
reply: retcode (354); Msg: End data with <CR><LF>.<CR><LF>
data: (354, 'End data with <CR><LF>.<CR><LF>')
send: 'Content-Type: text/plain; charset="utf-8"\r\nMIME-Version: 1.0\r\nContent-Transfer-Encoding: 7bit\r\nSubject: text\r\nFrom: xiaxiaoxu1987@163.com\r\nTo: 367224698@qq.com\r\nDate: Wed, 10 Apr 2019 21:13:57 +0800\r\nMessage-ID: <155490203514.14300.10708927723623278864@DESKTOP-F82U4NJ>\r\n\r\nbody\r\n.\r\n'
reply: '250 Mail OK queued as smtp11,D8CowACXrmER7K1cTRnVFA--.36353S2 1554902035\r\n'
reply: retcode (250); Msg: Mail OK queued as smtp11,D8CowACXrmER7K1cTRnVFA--.36353S2 1554902035
data: (250, 'Mail OK queued as smtp11,D8CowACXrmER7K1cTRnVFA--.36353S2 1554902035')
send: 'quit\r\n'
reply: '221 Bye\r\n'
reply: retcode (221); Msg: Bye

查看邮箱发送结果:

为了方便重用,我们把这些代码包装成一个通用的发信函数send_mail(),如下所示:

app.py: 通用发信函数

from flask import Flask
from flask_mail import Mail, Message
import os app = Flask(__name__)
app.config.update(
MAIL_SERVER = os.getenv('MAIL_SERVER'),
MAIL_PORT = '',
#MAIL_USE_TLS = True,
MAIL_USE_SSL = True,
MAIL_USEERNAME = os.getenv('MAIL_USERNAME'),
MAIL_PASSWORD = os.getenv('MAIL_PASSWORD'),
MAIL_DEFAULT_SENDER = (os.getenv('MAIL_USERNAME'))
) mail = Mail(app) def send_mail(subject, to, body):
message = Message(subject, recipients = [to], body = body)
mail.send(message)

假设我们的程序时一个周刊订阅程序,当用户在表单中填写了正确的email地址时,我们就发送一封邮件来通知用户订阅成功。通过在index视图中调用send_email()即可发送邮件,如下所示: 在视图函数中发送邮件

@app.route('/subscribe', methods = ['GET', 'POST'])
def subscribe():
form = SubscribeForm()
if form.validate_on_submit():
name = form.name.data
email = form.email.data
send_subscribe_mail('Subscribe Success!', email, name = name)
flash('Confirmation email have been sent! Check your inbox.')
return redirect(url_for('subscribe'))
return render_template('subscribe.html', form = form)
												

flask 电子邮件Flask-Mail的更多相关文章

  1. flask 电子邮件进阶实践-用模板发送163邮件

    电子邮件进阶实践 下面来学习构建邮件的HTML正文,并使用模板组织内容. 一封电子邮件的正文可以是纯文本(text/plain),也可以是HTML格式的文本(text/html).处于全面的考虑,一封 ...

  2. Python flask 基于 Flask 提供 RESTful Web 服务

    转载自 http://python.jobbole.com/87118/ 什么是 REST REST 全称是 Representational State Transfer,翻译成中文是『表现层状态转 ...

  3. [Flask]学习Flask第三天笔记总结

    from flask import Flask,render_template,request from others import checkLogin app = Flask(__name__) ...

  4. Flask01 初识flask、flask配置

    1 什么是flask Flask是一个使用 Python 编写的轻量级 Web 应用框架.其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 . 百度百科:点击前往 中文文档: ...

  5. 【Flask】Flask学习笔记(一) 应用基本结构

    初始化 使用前必须创建一个应用实例 from flask import Flask app = Flask(__name__) 路由和视图函数 请求流程 客户端(web浏览器)-->  web服 ...

  6. python框架之Flask(1)-Flask初使用

    Flask是一个基于 Python 开发并且依赖 jinja2 模板和 Werkzeug WSGI 服务的一个微型框架,对于 Werkzeug 本质是 Socket 服务端,其用于接收 http 请求 ...

  7. cannot import name 'Flask' from 'flask'

    今天发现了智障的真我. 刚入门flask,建了一个文件命名叫flask.py 在virtualenv的容器里运行该py文件,报错cannot import name 'Flask' from 'fla ...

  8. flask框架----flask中的wtforms使用

    一.简单介绍flask中的wtforms WTForms是一个支持多个web框架的form组件,主要用于对用户请求数据进行验证. 安装: pip3 install wtforms 二.简单使用wtfo ...

  9. flask框架----flask基础

    知识点回顾 1.flask依赖wsgi,实现wsgi的模块:wsgiref,werkzeug,uwsgi 2.实例化Flask对象,里面是有参数的 app = Flask(__name__,templ ...

随机推荐

  1. Go语言基础(一)

    Go语言基础(一) 国庆体验一下大名鼎鼎的Go语言,IDE使用IEDA+Go插件,边敲代码边体会,感觉Go语言好酷 一.Hello World 和Java类似,go文件需要一个package包含,代码 ...

  2. KMP Algorithm 字符串匹配算法KMP小结

    这篇小结主要是参考这篇帖子从头到尾彻底理解KMP,不得不佩服原作者,写的真是太详尽了,让博主产生了一种读学术论文的错觉.后来发现原作者是写书的,不由得更加敬佩了.博主不才,尝试着简化一些原帖子的内容, ...

  3. elasticsearch视频

    简单的集群管理 (1)快速检查集群的健康状况 es提供了一套api,叫做cat api,可以查看es中各种各样的数据 GET /_cat/health?v epoch timestamp cluste ...

  4. java学习(五)--- 方法

    方法的定义 修饰符 返回值类型 方法名(参数类型 参数名){ ... 方法体 ... return 返回值; } 注意:非void方法必须有返回值 方法重载: 可以声明方法相同,但是参数类型不同的方法 ...

  5. 在U8菜单中增加自定义项

    --.NET实现的脚本 If Exists (Select 1 From UFSystem..UA_Menu where cMenu_id = 'SAM06') delete from UFSyste ...

  6. Selenium 3----定位一组元素+多表单切换+多窗口切换

    定位一组元素 和定位单个元素类似,WebDriver提供了8种用于定位一组元素的方法.定位一组元素的方法与定位单个元素的方法类似,唯一的区别是在单词element后面多了一个s表示复数. find_e ...

  7. 秒杀linux下系统调用fork()面试题(转)

    https://blog.csdn.net/chdhust/article/details/10579001 https://www.cnblogs.com/clover-toeic/p/375443 ...

  8. 关于python字符串基本操作

    python字符串基本操作,比如字符串的替换.删除.截取.复制.连接.分割等.都是一些关于字符串的一些方法.下面来列举一些,相信对学习python还是有些帮助的. 1.去除空格--strp(): &g ...

  9. Oracle 基础概念

    数据库是一系列物理文件的集合(数据文件,控制文件,联机日志,参数文件等) --查询当前数据库名:select name from v$database; 数据库实例是一组Oracle后台进程/线程以及 ...

  10. 随手科技(随手记)2017招聘Java工程师笔试题

    一  如何解决多台web服务器粘性会话的问题? 粘性session:web服务器会把某个用户的请求,交给tomcat集群中的一个节点,以后此节点就负责该保存该用户的session,如果此节点挂掉,那么 ...