Python 优雅的群发邮件

用 python 不会自动化办公可不行, 学习一下一键发邮件的方法吧(๑•̀ㅂ•́)و✧

0%

前置条件

邮箱需要开启SMTP功能

简单了解

  • SMTP(Simple Mail Transfer Protocol)
    简单邮件传输协议, 是电子邮件发送的行业标准协议.(发送用)

  • IMAP(Internet Access Message Protocol)
    处理从接收服务器管理和检索电子邮件消息的电子邮件协议.(接受用)
    可以跨所有设备同步消息.

  • POP3
    POP(邮局协议), 3(版本号), 也是接受用的协议.
    IMAP主要区别在, POP3会保存文件到本地, 服务器删了也没事, 便于脱机.

开启SMTP

outlook 邮箱为例:

  • 登录网页版邮箱 -> https://outlook.com

  • 右上角打开设置-搜索IMAP-打开POP和IMAP设置

  • 默认是开启SMTP 服务的

  • 记录下 SMTP 配置

    服务器名称: smtp.office365.com
    端口: 587
    加密方法: STARTTLS

注: 其它类型的邮箱大多需要获取授权码才能登录邮箱, 而outlook用账号密码就可以.

简单实现

先介绍要用到的库

  • smtplib
    定义了SMTP客户端的使用
  • email
    邮件的编写和简单发送
import smtplib
from email.message import EmailMessage

msg = EmailMessage()
msg['From'] = 'me@outlook.com' # 发送邮箱
msg['To'] = ['example@outlook.com', 'example@qq.com'] # 接受邮箱
password = '*****'

msg['Subject'] = 'This is a test email' # 邮件标题
msg.set_content('Hello !') # 邮件内容

with smtplib.SMTP('smtp.office365.com', port=587) as smtp:
smtp.starttls() # 设置连接模式为TLS
smtp.login(msg['From'], password) # 登录客户端
smtp.send_message(msg)

# 另一种发送方式
# smtp.sendmail(from_addr=f"owq<{msg['From']}>", to_addrs=msg['To'], msg=msg.as_string())

当出现更复杂的需求出现时, 上面的代码很显然不够便捷, 这时要不就自己封装一个库, 要不就找找有没有现成的, 而redmail就是一个不错的选择.

redmail 库

一个高级邮件发送库, 可以快速简易的进行邮件发送.

官方文档 -> 🚪

安装

pip install redmail

例子

from redmail import EmailSender

email = EmailSender(host='smtp.office365.com', port=587,
username='me@outlook.com', password='*****')

email.send(
subject='This is a test email',
sender='me@outlook.com',
receivers=['example@outlook.com', 'example@qq.com'],
text='Hello!'
)

一键群发

大致流程

现在有这么一个需求:

截图到剪贴板->粘贴到邮件客户端->填写收件人->点击发送->关闭客户端

我们把它优化成这样:

截图到剪贴板->启动脚本完成群发

构建配置

先定义一下配置文件config/email_archive.yml, 有不同需求时可以通过切换配置文件来快速应用.

# SMTP 服务配置
smtp:
username: me@outlook.com
password:
host: smtp.office365.com
port: 587

# 邮件发送配置
send:
# 发送
sender: owq<me@outlook.com>
# 接收
receive:
- example01@outlook.com
# 抄送
cc:
- example02@outlook.com
- example03@outlook.com
# 密送
bcc:
- example04@outlook.com
- example05@outlook.com

加载yml文件用的函数

import yaml

def load_yml(conf_path:str) -> dict:
"""Load .yml file """
with open(file=conf_path, mode='r', encoding="utf-8") as f:
return yaml.load(stream=f, Loader=yaml.FullLoader)

图片提取

使用PIL库从剪贴板提取图片保存

from PIL import Image, ImageGrab

def save_paste_pic(filename: str) -> bool:
""" Save the clipboard picture """
im = ImageGrab.grabclipboard()
if isinstance(im, Image.Image):
im.save(filename)
return True
return False

注: Mac 桌面环境暂不支持

密文密码

对于不想把密码保存在配置里的情况, 这里可以定义一个模拟密码输入的函数.

import msvcrt

def input_password(info: str = 'Password: ') -> tuple:
"""shell密码输入

按键绑定: 回车确认, 退格删除, Esc/Ctrl+C 退出(信号为False)

:param info: 输入提示信息, defaults to 'Password: '
:return: 信号和输入密码
:rtype: tuple
"""
print(info, end='', flush=True)
pawd_arr, sing = [], False
while True:

keypad = msvcrt.getch()
# Enter
if keypad == b'\r':
msvcrt.putch(b'\n')
sing = True
break
# Esc or Ctrl+C
elif keypad in [b'\x1b', b'\x03']:
msvcrt.putch(b'\n')
break
# Backspace
elif keypad == b'\x08':
if pawd_arr:
pawd_arr.pop()
msvcrt.putch(b'\b')
msvcrt.putch(b' ')
msvcrt.putch(b'\b')
else:
pawd_arr.append(keypad)
msvcrt.putch(b'*')

return sing, b''.join(pawd_arr).decode()
↓ 效果是这样子 ↓

注: 仅适用 Windows 桌面环境

封装函数

创建templates\archive_pic.html编写内容模板, 便于邮件内容修改.

<h1>Archive</h1>
{{ pic }}

定义下发送邮件的函数

def send_archive_email(conf: dict, img_path: str):
"""加载配置发送附加图片的归档邮件

:param conf: 发送配置
:param img_path: 发送图片路径
"""
email = EmailSender(**conf['smtp'])
email.set_template_paths(html='templates')
try:
email.send(
**conf['send'],
subject=f"Recorded in {time.strftime('%b %d, %Y', time.localtime())}",
html_template='archive_pic.html',
body_images={'pic': img_path},
)
print('Email send done.')
except smtplib.SMTPAuthenticationError:
print('Authentication unsuccessful.')
except smtplib.SMTPDataError:
print('SMTP service exception, login authentication is required.')

接下来编写一下流程, 就基本完成了~

def main():
""" 获取剪贴板图片添加到邮件发送 """

img_path = r'paste_pic.png'
if save_paste_pic(img_path):

conf = load_yml(r'config/email_archive.yml')
if not conf['smtp']['password']:
sign, password = input_password()
if sign:
conf['smtp']['password'] = password
else:
return

send_archive_email(conf, img_path)
else:
print('Clipboard has no pictures!')

快速启动

这里使用的是PowerShell, 打开它的配置文件(没有需要创建).

code $profile

创建运行函数和别名

function sendArchiveEmail {
$Pwd = Get-Location
Write-Output 'Start send_archive_email.py.'
cd D:\Project\auto_email && python send_archive_email.py
cd $Pwd
}

New-Alias send-email sendArchiveEmail

刷新终端后使用send-email命令就可以启动脚本了

------------ 已触及底线了 感谢您的阅读 ------------
  • 本文作者: OWQ
  • 本文链接: https://www.owq.world/9f9b4f64/
  • 版权声明: 本站所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处( ̄︶ ̄)↗