当前位置: 首页 > news >正文

Day12:Python实现邮件自动发送

一、为什么要做这个小工具?

在Day10,我们用pandas汇总了Excel。

在Day11,我们用docxtpl批量生成了Word报告。

假如这些报告,我们要通过邮件发送出去,Python能不能帮上忙,不让我们一份一份地、手动添加附件、手动发送出去呢?

当然可以。

我们干的就是把重复劳动这四个字从字典里彻底删掉。

今天,我们就用Python自动登录我们的邮箱,把那些报告一份份精准地发出去。

实际的工作中也确实有一些跟邮件有关的工作。

比如每天早上9点,自动把日报模板发给所有组员。

又或者每周五下午5点,自动把汇总好的周报发给leader。

二、先发一封邮件

这个版本我们先把核心流程大打通,那就是先发送一封存文本的邮件出去,看能不能发送成功,接收到。

2.1. 关键的第一步

第一步是本问最最最重要的一步,也是所有新手都会卡住的地方。那就是获取邮箱授权码。

出于安全考虑,几乎所有邮箱(QQ、163、Gmail)都不允许我们直接用登录密码通过Python脚本来发邮件。

我们必须去邮箱的设置里面,开启一个叫SMTP的服务,然后生成一个专门给程序用的应用密码(或者交授权码)。

以QQ邮箱为例,流程如下:

登录QQ邮箱,进入账号与安全:

image-20251024135306596

进入安全设置,页面拖到最后:

image-20251024135608088

点击开启服务(POP3/IMAP/SMTP/Exchange/CardDAV 服务):

image-20251024135652440

这串16位的码,就是我们的授权码。把他复制下来,这就是我们等下要用的密码,不是我们的QQ登录密码。

2.2. 敲代码

import smtplib
from email.message import EmailMessage
import sslSMTP_SERVER = "smtp.qq.com"
SENDER_EMAIL = "1234@qq.com"
SENDER_PASS = "这里换成我们刚申请的16位授权码"
RECEIVER_EMAIL = "1234@qq.com"msg = EmailMessage()
msg["Subject"] = "来自懒惰蜗牛的问候!"
msg["From"] = SENDER_EMAIL
msg["To"] = RECEIVER_EMAIL
msg.set_content("这是一封由懒惰蜗牛Python脚本自动发送的邮件!")print("正在连接到服务器...")
context = ssl.create_default_context()
with smtplib.SMTP_SSL(SMTP_SERVER, 465, context=context) as smtp:smtp.login(SENDER_EMAIL, SENDER_PASS)print("登录成功!准备发送...")smtp.send_message(msg)print("邮件发送成功!")smtp.quit()

如果你是用的163邮箱,服务器就是smtp.163.com,其他逻辑一样的。

2.3. 运行结果

邮箱里面收到了邮件:

image-20251024141626552

2.4. 代码讲解

import smtplib导入Python内置的邮件模块,专门负责连接邮件服务器(SMTP)并发送邮件。

from email.message…是导入Python内置的类似信纸的模块,专门负责构建邮件的内容,比如标题、正文等。

ssl是Python标准库的一部分,主要提供对SSL/TLS协议的支持,用来加密网络通信。

SMTP_SERVER定义的是接收邮件的域名地址。

SENDER_EMAIL是发送人的邮箱地址,RECEIVER_EMAIL是接收人的邮箱地址。

这两个可以一样,就是用自己的邮箱给自己发送邮件。

SENDER_PASS是我们刚申请的授权码。

msg = EmailMessage()是实例化了一个msg对象,这个对象就是用来操作邮件内容的。

比如msg[“Subject”] 就是在设置邮件的标题,msg[“From”]在设置邮件的发送人邮箱地址。

像Subject、From、To这些特定的字符串不是Python定的,而是一个叫IETF的国际组织定的。

他们会出官方说明书RFC,指定标准的邮件里面包含什么东西,然后大家就按照这个规则来玩。

ssl.create_default_context()是创建一个默认安全配置的SSL上下文对象。

如果你没有计算机基础,不知道SSL这东西,暂时可以这么理解 ,用了SSL,我们和邮箱服务器之间的通信就会变成密文,就像两个人用密码本传纸条,就算被别人截住了信息,他拿去也看不懂。安全。

又碰到了with … as smtp:就是自动管理连接的,中间的smtplib.SMTP_SSL(SMTP_SERVER, 465, context=context)就是在我们跟邮箱服务器之间创建一个安全的连接。

465是端口,访问一个具体的应用,我们通过对方的域名(会映射到IP)能找到这台服务器,但是这个服务器上可能跑着很多的应用。

我们得通过端口号来确定,访问的具体是哪个应用。

我们怎么知道是465这个端口呢?

image-20251024144733026

其实一般情况下,市面上的邮箱服务器都会约定是这些端口。

接着往下看,连接建立好了,那服务器怎么知道我们是不是他的合法用户呢?

这就需要smtp.login把发送者邮箱地址和之前的授权码发给服务器,让他验证下,如果验证过了,那就登录成功了。

登陆成功,就剩下把我们准备好的邮件内容发过去了,smtp.send_message(msg)就是在发送邮件。

三、带上我们的附件

纯文本邮件太单调了。

我们的目标是发送上一篇生成的Word报告。

要发送附件,我们的信纸EmailMessage就要用更高级的写法了。

3.1. 敲代码

import smtplib
import ssl
from email.message import EmailMessageSMTP_SERVER = "smtp.qq.com"
SENDER_EMAIL = "1234@qq.com"
SENDER_PASS = "这里换成我们刚申请的16位授权码"
RECEIVER_EMAIL = "1234@qq.com"msg = EmailMessage()
msg["Subject"] = "【重要】您的X月绩效通知书"
msg["From"] = SENDER_EMAIL
msg["To"] = RECEIVER_EMAIL
msg.set_content("您好,附件为您的5月绩效通知书,请查收。")attachment_path = "绩效通知-张三-销售A组.docx"with open(attachment_path, "rb") as f:file_data = f.read()file_name = f.namemsg.add_attachment(file_data,maintype="application",subtype="octet-stream",filename=file_name)print("正在连接并发送带附件的邮件...")
context = ssl.create_default_context()
with smtplib.SMTP_SSL(SMTP_SERVER, 465, context=context) as smtp:smtp.login(SENDER_EMAIL, SENDER_PASS)smtp.send_message(msg)print("带附件的邮件发送成功!")smtp.quit()

3.2. 运行效果

image-20251024151817028

3.3. 代码讲解

跟第一版的代码相比,其实就新增了附件的那块内容。

attachment_path = "绩效通知-张三-销售A组.docx"指定了附件文件。

通过with open(…, “rb”) as f:打开文件,这里的"rb"是Read Binary的缩写,意思是读取的时候是二进制模式。

因为所有非纯文本文件(图片、Word、Excel、PDF)都必须用rb模式读取。

file_data通过read拿到文件数据。

file_name就是文件名。

add_attachment是邮件内容对象的方法,要把文件数据给他,还要指定主类型、子类型及文件名(这个文件名是邮件中附件显示的文件名)。

其实是EmailMessage模块帮我做了很多的事情,然后暴露出一个非常简单的方法出来给我们调用。

我们只要把文件塞给add_attachment就行了。

其中主类型、子类型指的是文件的MIME类型。

举几个常见的例子:

文件类型maintypesubtype说明
普通二进制文件(不知道具体类型时用)applicationoctet-stream万能类型,表示这是一个二进制文件,但我不知道具体是啥
PDF文件applicationpdf表示这是个PDF文档
图片JPGimagejpeg表示这是个JPG格式的图片
图片PNGimagepng表示这是个PNG格式的图片
文本文件textplain表示这是个纯文本文件
HTML文件texthtml表示这是个HTML格式的文本
ZIP压缩包applicationzip表示这是个ZIP文件
Word文档applicationmsword 或 docx(更准确的是 vnd.openxmlformats…)早期是 msword,新版Office有专门的 subtype

这些类型也不是Python定的,是互联网标准组织定义的。

指定这个类型就是为了告诉对方我这个文件是什么文件。假如对方有对应文件类型的显示图标,就能直观的显示出来。

又或者对方能够直接调用相应的编辑器直接打开对应类型的文件。

邮件发送阶段的代码跟上一版是一致的。

四、终极版

现在,我们要把Day10(Excel)、Day11(Word)和今天 (Email)串联起来,做一个全自动的绩效通知群发器。

我们的绩效数据.xlsx文件里应该有姓名,部门,业绩,评级,邮箱这几列。

image-20251024155630843

模版还是使用Day11中的。

4.1. 敲代码

import smtplib
import ssl
import pandas as pd
from docxtpl import DocxTemplate
from email.message import EmailMessage
import timeSMTP_SERVER = "smtp.163.com"
SENDER_EMAIL = "123456@163.com"
SENDER_PASS = "你的16位授权码"TEMPLATE_PATH = "绩效模版.docx"
DATA_PATH = "绩效数据.xlsx"df = pd.read_excel(DATA_PATH)
print(f"--- 加载数据成功,共 {len(df)} 条记录 ---")context = ssl.create_default_context()
with smtplib.SMTP_SSL(SMTP_SERVER, 465, context=context) as smtp:smtp.login(SENDER_EMAIL, SENDER_PASS)print("--- 邮箱登录成功,准备开始群发 ---")for index, row in df.iterrows():doc = DocxTemplate(TEMPLATE_PATH)context = row.to_dict()doc.render(context)report_filename = f"report-{row['姓名']}.docx"doc.save(report_filename)msg = EmailMessage()msg["Subject"] = f"【绩效通知】{row['姓名']} - 5月绩效"msg["From"] = SENDER_EMAILmsg["To"] = row['邮箱']msg.set_content(f"您好,{row['姓名']}:\n\n附件为您的5月绩效通知书,请查收。\n\nHR部门")with open(report_filename, "rb") as f:msg.add_attachment(f.read(),maintype="application",subtype="octet-stream",filename=f"5月绩效通知-{row['姓名']}.docx")smtp.send_message(msg)print(f"已成功发送给: {row['姓名']} ({row['邮箱']})")time.sleep(1)
print("\n--- 全部任务完成!---")

运行代码之前记得先把用到的模块安装一下:

pip install pandas
pip install docxtpl
pip install openpyxl

4.2. 运行结果

image-20251024163107580

4.3. 代码讲解

整合代码中没有引入新的知识点。

唯一一点就是使用time.sleep(1)让程序暂停了一会, 主要是避免服务器那边把我们当成攻击他的。

其他的都只是调整了逻辑结构。

在发送QQ邮箱时可能会出现smtplib.SMTPResponseException: (-1, b’\x00\x00\x00’),如果出现这种错误就换成163的邮箱进行测试。163的邮箱授权码跟QQ邮箱的获取流程差不多。

五、小结

Day10、Day11加上今天的文章,我们已经从Excel汇总,到Word 生成,再到Email群发。

也算是把工作场景中关于文件、邮件的操作理了一遍。

当然这些都只是点到为止,工作场景的情况肯定更加复杂,这就需要我们慢慢去细化需求,才能写出更加完善的脚本工具。

http://www.dtcms.com/a/536269.html

相关文章:

  • 点亮LED
  • 家乡ppt模板免费下载网站地图 添加到网站
  • JMeter直连数据库的使用案例1
  • 网站备案ip查询系统上海十大营销策划公司排名
  • STM32H743-ARM例程31-CAN
  • Claude Code + 国产模型GLM-4.6 安装指南 (for Windows/Mac)
  • Docker 镜像导出与导入教程(Windows - Linux)
  • ARM《4》_在开发板上裸机编程实现GPIO编程控制LED灯闪烁
  • 手机商城 手机网站建设郴州今天几例
  • 从 Electron 转向 Tauri:用 Rust 打造更轻、更快的桌面应用
  • webrtc代码走读(九)-QOS-SVC(可分级视频编码)
  • 个人项目开发(3) 实现基于角色的权限控制及自动刷新token
  • 在柬埔寨做网络销售推网站校园网站建设教程
  • 具备高度自主学习能力、互联网交互能力、智能家居控制能力和多模态交互能力的通用智能体原型系统
  • 爬虫前奏--基于macos的ip代理池构建
  • 网站开发专员的面试题微信导航wordpress
  • 给传销做网站网站设计模板psd
  • Kingbase 与 ETL:如何实现金融级数据库的安全数据同步
  • cocos 用widget将ui组件固定在屏 随着分辨率自适应 编辑器界面canvas作为手机屏参考 将ui组件放进去 deepseek解答
  • 《微信小程序》第六章:参数定义与管理
  • ElasticSearch架构和写入、更新、删除、查询的底层逻辑
  • 做市场调研的网站网站建设费可以计入管理费用吗
  • SQL 性能优化:出现 sql 比较慢怎么办?
  • Access-Control-Allow-Origin 详解
  • __金仓数据库平替MongoDB:银行存款系统国产化实践__
  • 14天极限复习软考day4-法律、设计模式
  • 深度剖析数字化转型的三驾马车:信息化、数字化、数智化
  • 晋中网站公司长沙找人做企业网站文案
  • Qt——界面优化
  • 基于python的化妆品销售分析系统