Flask之电子邮件

  前言:本博客仅作记录学习使用,部分图片出自网络,如有侵犯您的权益,请联系删除 

目录

一、使用Flask-Mail发送电子邮件

1.1、配置Flask-Mail

1.2、构建邮件数据

1.3、发送邮件

二、使用事务邮件服务SendGrid

2.1、注册SendGrid

2.2、SendGrid SMTP转发

2.3、SendGrid Web API转发

三、电子邮件进阶

3.1、提供HRML正文

3.2、使用Jinja2模板组织邮件正文

3.3、异步发送邮件

致谢


示例邮件:

邮件字段字段值
发信方(Sender)Greygrey@helloflask.com
收信方(To)Zornzorn@example.com
邮件主体(Subject)Hello,World!
邮件正文(Body)Across the Great Wall we can reach every corner in the world.

一、使用Flask-Mail发送电子邮件

扩展Flask-Mail包装了Python标准库中的smtplib包,简化了在Flask程序中发送电子邮件的过程。

 pip install flask-mail

实例化Flask-Mail提供的Mail类并传入程序实例以完成初始化:

 from flask_mail import Mail​app = Flask(__name__)...mail = Mail(app)

1.1、配置Flask-Mail

Flask-Mail通过连接SMTP(Simple Mail Transfer Protocal,简单邮件传输协议服务器来发送邮件。在开发和测试阶段,我们使用邮件服务提供商的SMTP服务器(比如Gmail),这时我们需要对Flask-Mail进行配置。

Flask-Mail常用配置变量:

配置键说明默认值
MAIL_SERVER用于发送邮件的SMTP服务器localhost
MAIL_PORT发送端口25
MAIL_USE_TLS是否使用STRTTLSFalse
MAIL_USE_SSL是否使用SSL/TLSFalse
MAIL_USERNAME发送服务器的用户名None
MAIL_PASSWORD发送服务器的密码None
MAIL_DEFAULT_SENDER默认的发信人None

对发送的邮件进行加密可以避免在发送过程中被第三方截获和篡改。SSL(Security Socket Layer,安全套接字层)和TLS(Transport Layer Security,传输层安全)是两种常用的电子邮件安全协议。TLS继承了SSL,并在SSL的基础上做了一些改进。通过将MAIL_USE_SSL设置为True开启。STARTTLS是另一种加密方式,它会对不安全的连接进行升级。

 # 1、SSL/TLS加密:MAIL_USE_SLL = TrueMAIL_PORT = 465​# 2、STARTTLS加密:MAIL_USE_TLS = TrueMAIL_PORT = 587

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

常见的电子邮箱服务提供商的STMP配置信息:

电子邮件服务提供商MAIL_SERVERMAIL_USERNAMEMAIL_PASSWORD额外步骤
Gmailsmtp.gmail.com邮箱地址邮箱密码开启"Allow less secure apps",在本地设置VPN代理
QQ邮箱smtp.qq.com邮箱地址授权码开启SMTP服务并获取授权码
新浪邮件smtp.sina.com邮箱地址邮箱密码开启SMTP服务
163邮箱smtp.163.com邮箱地址授权码开启SMTP服务并设置授权码
Outlook/Hotmailsmtp.live.com或smtp.office365.com邮箱地址邮箱密码

(163邮箱的SMTP服务不支持STARTTLS,需要使用SSL/TLS加密。就是将MAIL_USE_SSL设为True,MAIL_PORT设为465)

Gmail、Outlook、QQ邮箱等这类服务被称为EPA(Email Service Procider),适用于个人业务使用,不适合用来发送事务邮件。对于需要发送大量邮件的事务性邮件任务,更好的选择是使用自己配置的SMTP服务器或是使用类似SendGrid、Mailgun的事务邮件服务提供商。

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

 import osfrom flask import Flaskfrom flask_mail import Mail​app = Flask(__name__)​app.config.update(MAIL_SERVER=os.getenv('MAIL_SERVER'),MAIL_PORT=587,MAIL_USE_TLS=True,MAIL_USERNAME=os.getenv('MAIL_USERNAME'),MAIL_PASSWORD=os.getenv('MAIL_PASSWORD'),MAIL_DEFAULT_SENDER=('Grey Li', os.getenv('MAIL_USERNAME')))mail = Mail(app)

在我们的配置中,邮箱账户和密码属于敏感信息,不能直接写在脚本中,所以设置为从系统环境变量中获取。

1.2、构建邮件数据

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

 $ flask shell>>> from flask_mail import Message>>> from app import mail

一封邮件至少包含主题、收件人、正文、发信人这几个。发信人在前用MAIL_DEFUALT_SENDER配置变量指定过了,剩下的分别通过Message类的构造方法中的subject、recipients、body关键字传入参数,其中recipients是包含一电子邮件地址的列表

 >>> message = Message(subject='Hello,World!',recipients=['Zorn <zorn@example.com>'],body='Across the Great Wall we can reach every corner in the world.')

(和发信人类似,收信人字符串有两种新式:'Zorn zorn@example.com'或'zorn@example.com')

1.3、发送邮件

通过对mail对象调用send()方法,传入我们在上面构建的邮件对象即可发送邮件:

 >>> mail.send(message)

完整的发送示例代码:

 from flask import Flaskfrom flask_mail import Mail,Message...message = Message(subject='Hello,World!',recipients=['Zorn <zorn@example.com>'],body='Across the Great Wall we can reach every corner in the world.')mail.send(message)

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

...
def send_mail(subject,to,body):message = Message(subject,recipients=[to],body=body)mail.send(message)

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

@app.route('/subscript',methods=['GET','POST'])
def subscript():form = SubscribeForm()if form.validate_on_submit():email = form.email.dataflash('Welcome on board!')send_mail('Subscribe Success!',email,'Hello,thank you for subscribing Flask Weekly!')return redirect(url_for('index'))return render_template('index.html',form=form)

二、使用事务邮件服务SendGrid

在生产环境中,除了自己安装运行邮件服务器外,更方便的做法是使用事务邮件服务,比如Mailgun、Sendgrid等。这两个邮件服务对免费账户分别提供每月1万封和3000封的免费额度,完全足够在测试使用或在小型程序中使用。Mailgun在注册免费账户时需要填写信用卡,而Sendgrid没有这一限制。

2.1、注册SendGrid

登录官网注册一个免费账户,访问https://app.sendgrid.com/signup,填写信息等完成注册。注册完成后,需要为当前的项目创建一个API密钥,用于在程序中发送邮件时进行认证。

创建成功后复制密钥,然后保存到.env文件中,待会使用它来作为发信账户的密码:

SENDRID_API_KEY=your_key_here

(API密钥被创建一次后仅显示一次,一旦关闭了显示界面,将无法再次查看。)

2.2、SendGrid SMTP转发

创建好API密钥后,就可以通过SendGrid提供的SMTP服务器发送电子邮件了。

MAIL_SERVER = 'smtp.sendgrid.net'
MAIL_PORT = 587
MAIL_USE_TLS = True
MAIL_USERNAME = 'apikey'
MAIL_PASSWORD = os.getenv('SENDGRID_API_KEY')	# 从环境变量中读取API密钥

2.3、SendGrid Web API转发

在程序中向SendGrid提供的Web API发出一个POST请求,并附带必要的信息,比如密钥、邮件主题、收件人、正文等。示例:

POST https://api.sendgrid.com/v3/mail/send
'Authorization: Bearer YOUR_API_KEY'
'Content-Type: application/json''{"personalizations":[{"to":[{"email":"zorn@example.com"}]}],"from":{"email":"noreply@helloflask.com"},"subject":"Hello,World!","content":[{"type":"text/plain","value":"Across the Great Wall we can reach every corner in the World."}]}'

在命令行中使用curl一类的工具,或是使用任意一个用于请求的Python库即可发送电子邮件,比如requests。为了更方便在Python中构建邮件内容和发送邮件,我们可以使用SendGrid提供的官方Python SDK---SendGrid-Python:

pip install sendgrid

2.3.1、创建发信对象

首先需要实例化SendGridAPIClient类创建一个发信客户端对象:

>>> from sendgrid import SendGridAPIClient
>>> sg = SendGridAPIClient(api_key=os.getenv('SENDGRID_API_KEY'))

2.3.2、构建邮件数据

from sendgrid.helpers.mail import Email,Content,Mail
from_email = Email('norn@helloflask.com')
to_email = Email('zorn@example.com')
subject = 'Hello World!'
content = Content('text/plain','Across the Great Wall we can reach every corner in the World.')
mail = Mail(from_email,subject,to_email,content)

2.3.3、发送邮件

通过对表示邮件对象的sg对象调用sg.client.mail.send.post()方法,并将表示数据的字典使用关键字request_body传入即可发送发信的POST请求:

sg.client.mail.send.post(request_body=mail.get())

发信的方法会返回响应,我们可以查看响应的内容:

response = sg.client.mail.send.post(request_body=mail.get())
print(response.status_code)
print(response.body)
print(response.headers)

同样的,可以创建一个发信函数用来在视图函数中调用:

import sendgrid
import os
from sendgrid.helpers.mail import *def send_email(subject,to,body):sg = SendGridAPIClient(api_key=os.getenv('SENDGRID_API_KEY'))from_email = Email('norn@helloflask.com')to_email = Email(to)content = Content('text/plain',body)mail = Mail(from_email,subject,to_email,content)response = sg.client.mail.send.post(request_body=mail.get())

三、电子邮件进阶

3.1、提供HRML正文

一封电子邮件的正文可以是纯文本(text/plain),也可以是HTML格式的文本(text/html)。出于更安全的考虑,一封电子邮件应该既包含纯文本又包含HTML格式的正文。HTML格式正文将被优先读取;对于HTML邮件正文的编写:

  • 使用Tabel布局,而不是Div布局
  • 使用行内(inline)样式定义,比如:
<span style="font-family:Arial,Helvetica,sans-serif;font-size:12px;color:##000000;">Hello Email!</span>
  • 使用比较基础的CSS属性,避免使用快捷属性(比如background)和定位属性(比如float、position)
  • 邮件正文的宽度不超过600px
  • 避免使用JavaScript代码
  • 避免使用背景图片

在Flask-Mail中,我们使用Message类实例来构建邮件。和纯文本正文类似,HTML正文可以在实例化时传入html参数指定:

message = Message(...,body='纯文本正文',html='<h1>HTML正文</h1>')

或是通过类属性message.html指定:

message = Message(...)
message.body = '纯正文文本'
message.html = '<h1>HTML文本</h1>'

在SendGrid-Python中,使用Content类构建邮件正文时传入的第一个type_ 参数指定了邮件正文的MIME类型。若想同时提供两种格式的正文,那么就在使用Mail类构建邮件数据时传入一个包含两个Content类实例的列表作为正文content的参数值:

from sendgrid.helpers.mail import *
...
text_content = Content("text/plain","纯正文文本")
html_content = Content("text/html","<h1>HTML文本</h1>")
mail = Mail(from_email,subject,to_email,content=[text=content,html_content])

3.2、使用Jinja2模板组织邮件正文

大多数情况下,我们需要动态构建邮件正文。示例一个纯文本邮件模板subscribe.txt:

Hello {{ name }},
Thank you for subscribing Flask weekly!
Enjoy the reading :)Visit this link to unsubscribe: {{ url_for('unsubscribe',_external=True) }}

为了同时支持纯文本和HTML格式的邮件正文,每一类邮件我们都需要分别创建HTML和纯文本格式的模板。

<div style="width: 580px; padding: 20px;"><h3>Hello {{ name }}</h3><p>Thank you for subscribing Flask Weekly!</p><p>Enjoy the reading :)</p><smail style="color: #868e96;">Click here to <a href="{{ url_for('unsubscribe',_external=True) }}">unsubscribe</a>.</smail>
</div>

以上面创建的发信函数为例,在发送邮件的视图函数中使用render_template()函数渲染邮件正文,并传入相应的变量:

from flask import render_template
from flask_mail import Messagedef send_subscribe_mail(subject,to,**kwargs):message = Message(subject,recipients=[to],sender='Flask Weekly <%s>' %os.getenv('MAIL_USERNAME'))message.body = render_template('emails/subscribe.txt',**kwargs)message.html = render_template('emails/subscribe.html',**kwargs)mail.send(message)

3.3、异步发送邮件

为了避免延迟,我们可以将发信函数放入后台线程异步执行,以Flask-Mail为例:

from threading import Threaddef _send_async_mail(app,message):with app.app_context():mail.send(message)def send_mail(subject,to,body):message = Message(subject,recipients=[to],body=body)thr = Thread(target=_send_async_mail,args=[app,message])thr.start()return thr

因为Flask_Mail的send()方法内部的调用逻辑中使用了current_app变量,而这个变量只在激活的程序上下文中才存在,这里在后台线程调用发信函数,但是后台线程并没有程序上下文存在。为了正常实现发信功能,我们传入程序实例app作为参数,并调用app,app_context()手动激活程序上下文

致谢

在此,我要对所有为知识共享做出贡献的个人和机构表示最深切的感谢。同时也感谢每一位花时间阅读这篇文章的读者,如果文章中有任何错误,欢迎留言指正。 

学习永无止境,让我们共同进步!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/371850.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

element-ui输入框如何实现回显的多选样式?

废话不多说直接上效果&#x1f9d0; 效果图 <template><div><el-form:model"params"ref"queryForm"size"small":inline"true"label-width"68px"><el-form-item label"标签" prop"tag&q…

安全防御(防火墙)

第二天&#xff1a; 1.恶意程序---一般会具有一下多个或则全部特点 1.非法性&#xff1a;你未经授权它自动运行或者自动下载的&#xff0c;这都属于非法的。那恶意程序一般它会具有这种特点&#xff0c; 2.隐蔽性&#xff1a;一般隐藏的会比较深&#xff0c;目的就是为了防止…

MySQL性能优化 一、系统配置优化

数据库优化纬度有四个&#xff1a; 硬件升级、系统配置、表结构设计、SQL语句及索引。 优化选择&#xff1a; 优化成本&#xff1a;硬件升级 > 系统配置 > 表结构设计 > SQL语句及索引优化效果&#xff1a;硬件升级 < 系统配置 < 标结果设计 < SQL语句及索…

无线领夹麦克风品牌排名,揭秘国产领夹麦克风哪个品牌好

在自媒体行业迅猛发展的浪潮中&#xff0c;领夹麦克风作为音频采集的关键设备&#xff0c;其市场需求正经历着前所未有的激增。面对市场上众多品牌和型号的选择&#xff0c;如何做出既符合个人需求又不失专业水准的决策&#xff0c;成为了消费者亟待解决的问题。 我特意为大家…

springboot+vue+mybatis图书销售管理系统+PPT+论文+讲解+售后

在Internet高速发展的今天&#xff0c;我们生活的各个领域都涉及到计算机的应用&#xff0c;其中包括图书销售管理系统的网络应用&#xff0c;在外国图书销售管理系统已经是很普遍的方式&#xff0c;不过国内的管理网站可能还处于起步阶段。图书销售管理系统具有网上图书信息管…

Python学习中使用循环(for, while)

在Python编程语言中&#xff0c;循环是一个非常重要的概念&#xff0c;可以帮助我们在代码中重复执行某些操作。Python支持两种主要的循环结构&#xff1a;for 循环和 while 循环。 1. for 循环 for 循环用于遍历一个序列&#xff08;如列表、元组、字符串&#xff09;或其他…

Java反射与Fastjson的危险反序列化

什么是Java反射&#xff1f; 在前文中&#xff0c;我们有一行代码 Computer macBookPro JSON.parseObject(preReceive,Computer.class); 这行代码是什么意思呢&#xff1f;看起来好像就是我们声明了一个名为 macBookPro 的 Computer 类&#xff0c;它由 fastjson 的 parseObje…

浅谈OpenCV的多对象匹配透明图像的实现,以及如何匹配半透明控件

引子 OpenCV提供的templateMatch只负责将&#xff08;相关性等&#xff09;计算出来&#xff0c;并不会直接提供目标的对应坐标&#xff0c;一般来说我们直接遍历最高的相关度&#xff0c;就可以得到匹配度最高的坐标。但是这样一般只能得到一个坐标。在实际操作中&#xff0c;…

Hadoop-12-Hive 基本介绍 下载安装配置 MariaDB安装 3台云服务Hadoop集群 架构图 对比SQL HQL

章节内容 上一节我们完成了&#xff1a; Reduce JOIN 的介绍Reduce JOIN 的具体实现DriverMapperReducer运行测试 背景介绍 这里是三台公网云服务器&#xff0c;每台 2C4G&#xff0c;搭建一个Hadoop的学习环境&#xff0c;供我学习。 之前已经在 VM 虚拟机上搭建过一次&am…

Atom CMS v2.0 SQL 注入漏洞(CVE-2022-25488)

前言 CVE-2022-25488 是一个发现于 Telesquare SDT-CW3B1 设备中的命令注入漏洞。这一漏洞可以被未经认证的远程攻击者利用&#xff0c;通过特殊构造的 HTTP 请求在设备上执行任意命令。以下是关于该漏洞的详细信息&#xff1a; 漏洞详细信息 漏洞编号: CVE-2022-25488影响范…

记录一次ffmpeg手动编译出现的问题

前言部分 使用环境: ubuntu 22.04 最近手动编译了一次的ffmpeg&#xff08;参考博客ffmpeg学习&#xff1a;ubuntu下编译ffmpeg(全网最懒的编译脚本)&#xff09;&#xff0c;但是过程出现了一些问题&#xff0c;因此在此记录一下&#xff0c;若有疑问&#xff0c;欢迎讨论~。 …

【MotionCap】pycharm 远程在wsl2 ubuntu20.04中root的miniconda3环境

pycharm wsl2 链接到pycharmsbin 都能看到内容,/root 下内容赋予了zhangbin 所有,pycharm还是看不到/root 下内容。sudo 安装了miniconda3 引发了这些问题 由于是在 root 用户安装的miniconda3 所以安装路径在/root/miniconda3 里 这导致了环境也是root用户的,会触发告警 WA…

【MySQL】MySQL锁冲突排障纪要

【MySQL】MySQL锁冲突排障纪要 开篇词&#xff1a;干货篇&#xff1a;1.查看当前innodb status,里面包含事务,锁占用情况2.查看mysql进程状态3.查看innodb事务&#xff0c;锁&#xff0c;锁等待情况4.定位持有锁的线程信息 总结篇&#xff1a;一、锁冲突的原因二、锁冲突的表现…

BigDecimal(double)和BigDecimal(String)有什么区别?BigDecimal如何精确计数?

BigDecimal(double)和BigDecimal(String)的区别 double是不精确的&#xff0c;所以使用一个不精确的数字来创建BigDecimal&#xff0c;得到的数字也是不精确的。如0.1这个数字&#xff0c;double只能表示他的近似值。所以&#xff0c;当我们使用new BigDecimal(0.1)创建一个Bi…

一.2.(4)放大电路静态工作点的稳定;

1.Rb对Q点及Au的影响 输入特性曲线&#xff1a;Rb减少&#xff0c;IBQ&#xff0c;UBEQ增大 输出特性曲线&#xff1a;ICQ增大&#xff0c;UCEQ减少 AUUO/Ui分子减少&#xff0c;分母增大&#xff0c;但由于分子带负号&#xff0c;所以|Au|减少 2.Rc对Q点及Au的影响 输入特性曲…

Google账号输入用户名和密码后提醒要到手机通知点是,还要点击数字,但是我手机收不到

有一些朋友换了一个新的电脑后手机登录谷歌账号时&#xff0c;用户名和密码都正确输入以后&#xff0c;第三步弹出一个提示&#xff0c;要在手机上的通知栏点击是&#xff0c;并且点击手机上相应的数字才能继续登录。 但是自己的手机上下拉通知栏却没有来自谷歌的通知&#xf…

Qt(MSVC)下报“语法错误缺少“}““语法错误缺少“常数“ 的解决办法

1.现象 目前我在工程中试图使用QHttpServer时&#xff0c;一编译&#xff0c;就报了一堆奇奇怪怪的错误&#xff1a; D:\Qt\httpServer\Qt5.15.2\include\QtHttpServer\qhttpserverrequest.h:75: error: C2143: 语法错误: 缺少“}”(在“(”的前面) D:\Qt\httpServer\Qt5.15.…

0/1背包问题总结

文章目录 &#x1f347;什么是0/1背包问题&#xff1f;&#x1f348;例题&#x1f349;1.分割等和子集&#x1f349;2.目标和&#x1f349;3.最后一块石头的重量Ⅱ &#x1f34a;总结 博客主页&#xff1a;lyyyyrics &#x1f347;什么是0/1背包问题&#xff1f; 0/1背包问题是…

实现ubuntu的任务计划反弹shell

1.实验目的 使用Ubuntu定时任务反弹shell 2实验环境 ubuntu&#xff1a;ip地址&#xff1a;192.168.80.133 kali&#xff1a;ip地址&#xff1a;192.168.80.134 3.编写crontab计划任务 在ubuntu的系统中使用crontab -e命令编写计划任务 作用&#xff1a;是将一个交互式的…

【基于R语言群体遗传学】-12-超显性与次显性

欢迎先看前面的博客&#xff0c;再继续进行后面的内容&#xff1a; 群体遗传学_tRNA做科研的博客-CSDN博客 当杂合子的适应度超出纯合子的范围时&#xff0c;二倍体能够展现出更多令人着迷的选择实例。这种形式的一种是杂合子优势&#xff0c;或称为“超显性”&#xff0c;其…