Flask之电子邮件

news2024/12/25 9:09:25

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

目录

一、使用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 = True
 MAIL_PORT = 465
 ​
 # 2、STARTTLS加密:
 MAIL_USE_TLS = True
 MAIL_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 os
 from flask import Flask
 from 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 Flask
 from 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.data
        flash('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 Message

def 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 Thread

def _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.coloradmin.cn/o/1877081.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] LYA的字符串拼接游戏(200分) - 三语言AC题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f…

动手学深度学习(Pytorch版)代码实践 -卷积神经网络-21多输入多输出通道

21多输入多输出通道 import torch from d2l import torch as d2ldef corr2d(X, K):"""计算二维互相关运算"""h, w K.shapeY torch.zeros((X.shape[0] - h 1, X.shape[1] - w 1))for i in range(Y.shape[0]):for j in range(Y.shape[1]):Y[i,…

【操作系统期末速成】 EP02 | 学习笔记(基于五道口一只鸭)

文章目录 一、前言&#x1f680;&#x1f680;&#x1f680;二、正文&#xff1a;☀️☀️☀️2.1 考点二&#xff1a;操作系统的功能及接口2.2 考点三&#xff1a;操作系统的发展及分类2.3 考点四&#xff1a;操作系统的运行环境&#xff08;重要&#xff09; 一、前言&#x…

私域流量的深度解析与电商应用

一、私域流量的核心价值 在当今数字化时代&#xff0c;流量成为了企业发展的重要资源。与公域流量相比&#xff0c;私域流量以其独有的私有性和可复用性&#xff0c;为企业提供了与用户建立深度联系的机会。私域流量不仅有助于企业精准触达目标用户&#xff0c;还能通过数据分…

小白学webgl合集-WebGL中给图片添加背景

一.实现效果 二.逻辑 为了在WebGL中给图片添加背景&#xff0c;主要的逻辑步骤包括初始化WebGL上下文、编写和编译着色器、创建和绑定缓冲区、加载和配置纹理以及绘制场景。以下是代码逻辑的详细说明&#xff1a; 1. 获取WebGL上下文 首先&#xff0c;通过获取<canvas>…

Qt信号槽的坑

1、重载的信号&#xff08;以QSpinBox为例&#xff09; 像是点击按钮之类的信号槽很好连接&#xff0c;这是因为它的信号没有重载&#xff0c;如果像SpinBox那样有重载信号的话&#xff08;Qt5.12的见下图&#xff0c;不过Qt5.15LTS开始就不再重载而是换信号名了&#xff09;&…

【融合ChatGPT等AI模型】Python-GEE遥感云大数据分析、管理与可视化及多领域案例应用

随着航空、航天、近地空间遥感平台的持续发展&#xff0c;遥感技术近年来取得显著进步。遥感数据的空间、时间、光谱分辨率及数据量均大幅提升&#xff0c;呈现出大数据特征。这为相关研究带来了新机遇&#xff0c;但同时也带来巨大挑战。传统的工作站和服务器已无法满足大区域…

微服务实战系列之云原生

前言 话说博主的微服务实战系列从去年走到今天&#xff0c;已过去了半年多了。本系列&#xff0c;博主主要围绕微服务实践过程中的主要组件或工具展开介绍。其中基本覆盖了我们项目或产品研发过程中&#xff0c;经常使用的中间件或第三方工具。至此&#xff0c;该系列也该朝着…

web刷题记录(7)

[HDCTF 2023]SearchMaster 打开环境&#xff0c;首先的提示信息就是告诉我们&#xff0c;可以用post传参的方式来传入参数data 首先考虑的还是rce&#xff0c;但是这里发现&#xff0c;不管输入那种命令&#xff0c;它都会直接显示在中间的那一小行里面&#xff0c;而实际的命令…

ProPainter – AI视频去水印工具,可以去除视频中的静态水印、动态物体/人物等 本地一键整合包下载

ProPainter是一个基于E2FGVI实现的AI视频编辑工具&#xff0c;它可以一键移除视频内的移动物体和水印。这个开源项目提供了一个简单而强大的解决方案&#xff0c;帮助用户轻松编辑和改善视频内容。 项目地址&#xff1a;https://github.com/sczhou/ProPainter 一键包下载&…

Debug 调试代码

我们使用 debug 的目的, 认为就是查看代码的执行过程的。 步骤&#xff1a; 1. 打断点 断点的意义是, debug 运⾏的时候, 代码会在断点处停下来不执行如果是想要查看代码的执行过程, 建议将断点放在第⼀行在代码 和 行号之间 点击,出现的红色圆点 就是断点, 再次点击可以取消 …

最逼真的简易交通灯设计

最逼真的简易交通灯设计 需要资料的请在文章末尾获取&#xff08;有问题可以私信我哦~~&#xff09; 01 资料内容 Proteus仿真文件程序源码实物制作&#xff0c;代码修改&#xff0c;功能定制&#xff08;需额外收费&#xff0c;价格实惠&#xff0c;欢迎咨询&#xff09; …

【ChatBI】text2sql-不需要访问数据表-超轻量Python库Vanna快速上手,对接oneapi

oneapi 准备 首先确保你有oneapi &#xff0c;然后申请 kimi的api 需要去Moonshot AI - 开放平台 然后添加一个api key 然后打开oneapi的渠道界面&#xff0c;添加kimi。 然后点击 测试&#xff0c; 如果能生成响应时间&#xff0c;就是配置正确。 然后创建令牌 http:…

动手学深度学习(Pytorch版)代码实践 -计算机视觉-46语义分割和数据集

46语义分割和数据集 # 图像分割和实例分割 """ 图像分割将图像划分为若干组成区域&#xff0c;这类问题的方法通常利用图像中像素之间的相关性。 它在训练时不需要有关图像像素的标签信息&#xff0c;在预测时也无法保证分割出的区域具有我们希望得到的语义。 图…

知识库在AI大模型中的使用流程

大模型知识库的使用流程通常包括以下关键步骤&#xff0c;大模型知识库的使用流程需要跨学科的知识和技能&#xff0c;包括自然语言处理、数据库管理、软件工程等。同时&#xff0c;也需要关注用户体验、性能优化、安全保护等方面&#xff0c;以提供高质量的知识服务。北京木奇…

# linux 系统中,使用 “ ll “ 命令报错 “ bash ll command not found “ 解决方法:

linux 系统中&#xff0c;使用 " ll " 命令报错 " bash ll command not found " 解决方法&#xff1a; 一、错误描述&#xff1a; 报错原因&#xff1a; 1、这个错误表明你尝试在 bash shell 中执行 ll 命令&#xff0c;但是系统找不到这个命令。ll 通常…

Flask之数据库

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 目录 一、数据库的分类 1.1、SQL 1.2、NoSQL 1.3、如何选择&#xff1f; 二、ORM魔法 三、使用Flask-SQLALchemy管理数据库 3.1、连接数据库服…

基于自组织长短期记忆神经网络的时间序列预测(MATLAB)

LSTM是为了解决RNN 的梯度消失问题而诞生的特殊循环神经网络。该网络开发了一种异于普通神经元的节点结构&#xff0c;引入了3 个控制门的概念。该节点称为LSTM 单元。LSTM 神经网络避免了梯度消失的情况&#xff0c;能够记忆更长久的历史信息&#xff0c;更能有效地拟合长期时…

STM32学习和实践笔记(38):RTC实时时钟实验

1.STM32F1 RTC介绍 STM32 的实时时钟&#xff08; RTC&#xff09;是一个独立的定时器。 STM32 的 RTC 模块拥有一组连续计数的计数器&#xff0c;在相应软件配置下&#xff0c;可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。 RTC模块和时钟配置…

c进阶篇(一):数据的存储

1.数据类型介绍 char // 字符数据类型 short // 短整型 int // 整形 long // 长整型 long long // 更长的整形 float // 单精度浮点数 double // 双精度浮点数 1.1整形家族&#xff1a; char unsigned char signed char …