用python定时发送邮件

2020/12/24 Hongmin
Python 3.6.3 |Anaconda custom (64-bit)|

关键词:python邮件发送;定时

一、 用python实现邮件发送

用程序实现邮件收发的三个角色:
MUA (mail user agent, 邮件用户代理)
MTA (mail transfer agent, 邮件传输代理)
MDA (mail delivery agent, 邮件投递代理)

发邮件:MUA → MTA【使用SMTP协议】
收邮件:MUA ← MDA 【使用POP3/IMAP4协议】

Python对SMTP的支持有smtplib和email两个模块,email负责构造邮件,smtplib负责发送邮件

电子邮件消息由headers(标题)和payloads(有效负载, 也称为content)组成。headers是RFC 5322或RFC 532风格的字段名和值,字段名和值用冒号分隔。冒号既不是字段名的一部分也不是字段值的一部分。payloads可以是一个简单的文本消息,或者一个二进制对象,或者一个结构化的子消息序列,每个子消息都有自己的头部集和有效负载。后一种类型的payloads由MIME类型的消息来指示,例如multipart/**或message/rfc822*。
——19.1.1. email.message: Representing an email message

(一)用到的模块

【1. email

(1) email.mime: Python文档中对mime的介绍

email.mime.text.MIMEText(_text, _subtype=‘plain’, _charset=None, *, policy=compat32)

MIMEText用来生成一个主要类型为text的MIME对象。

(2) email.utils.parseaddr(address)email.utils.formataddr((name_encoded_by_Header, address))

写邮件的时候,Email的地址可以有两种写法。一种写法是:xxx@qq.com,另一种写法是:name<xxx@qq.com>。用前者写法,在邮箱中就会显示为xxx<xxx@qq.com>;用后者,就会显示为name<xxx@qq.com>,更为清晰、专业。 【有的时候,用前者写法也会显示name,是因为对方邮箱中保存了这个地址,类似于对方手机中保存了手机号和姓名。】而parseaddr函数就是将str “name<xxx@qq.com>”转换成一个tuple。

In[48]: a = parseaddr('name<xxx@126.com>')
In[49]: a
Out[49]: ('name', 'xxx@126.com')
In[50]: type(a)
Out[50]: tupleIn[53]: name, addr = parseaddr('boy<xxx@126.com>')
In[54]: name
Out[54]: 'boy'
In[55]: addr
Out[55]: 'xxx@126.com'

解析出name和address后,需要用formataddr()函数将其变为标准的email地址。

标准的email地址格式为’name <address>’,因此formataddr()与parseaddr()相反,会将tuple转换成str。

注意:formataddr()接收的name需要经Header()进行编码处理后才行。【注1】

from email.header import HeaderIn[67]: name, addr = parseaddr('boy<xxx@126.com>')
In[68]: a = formataddr((name, addr)) # 将name直接传入
In[69]: b = formataddr((Header(name, 'utf-8').encode(), addr)) # 将name用Header()编码处理后传入
In[70]: a
Out[70]: 'boy <xxx@126.com>'
In[71]: b
Out[71]: '=?utf-8?q?boy?= <xxx@126.com>' #下文msg[’From‘]和msg['To']只识别这种格式

构建邮件正文:

from email.mime.text import MIMEText
from email.header import Header
from email.utils import parseaddr, formataddr# 编写一个进行地址解析、生成标准地址格式的函数
def _format_addr(s):name, addr = parseaddr(s)return formataddr((Header(name, 'utf-8').encode(), addr))# 设置邮件内容
msg = MIMEText('This is an email for testing.', 'plain', 'utf-8') # 设置邮件收发件人名称、主题等
'''
收发件人名称、主题,不是通过SMTP发送给MTA的,而是包含在发送给MTA的文本中(msg)。
'''
msg['From'] = _format_addr('发件人名 <%s>' % '发件人邮箱')
msg['To'] = _format_addr('收件人名 <%s>' % '收件人邮箱') #msg['From']和msg['To']直接使用formataddr后的字符串,如果有多个这样的字符串地址,用逗号(,)分开。
msg['Subject'] = Header('TEST', 'utf-8').encode() # 此处,主题也经过了Header的编码处理

纯文本邮件:MIMEText(‘文本’, ‘plain’, ‘utf-8’)
HTML邮件:MIMEText(‘HTML字符串’, ‘html’, ‘utf-8’)

【2. smtplib】

在使用email构建好邮件正文后,用smtplib来发送邮件。

smtplib模块定义了一个SMTP客户端会话对象,可以使用该对象向任何具有SMTP或ESMTP侦听程序守护进程的Internet机器发送邮件。有关SMTP和ESMTP操作的详细信息,请参考RFC 821 (Simple Mail Transfer Protocol)和RFC 1869 (SMTP服务扩展)。
——21.17. smtplib — SMTP protocol client

smtplib.SMTP()函数,设置服务器地址和端口号

smtplib.SMTP(host='', port=0, local_hostname=None, [timeout, ]source_address=None)

常用邮箱的服务器(SMTP/POP3)地址和端口总结
163.com:
POP3服务器地址:pop.163.com(端口:110)
SMTP服务器地址:smtp.163.com(端口:25)
126邮箱:
POP3服务器地址:pop.126.com(端口:110)
SMTP服务器地址:smtp.126.com(端口:25)
139邮箱:
POP3服务器地址:POP.139.com(端口:110)
SMTP服务器地址:SMTP.139.com(端口:25)
QQ邮箱:
POP3服务器地址:pop.qq.com(端口:110)
SMTP服务器地址:smtp.qq.com (端口:25)
QQ企业邮箱 :
POP3服务器地址:pop.exmail.qq.com (SSL启用 端口:995)
SMTP服务器地址:smtp.exmail.qq.com(SSL启用 端口:587/465)
gmail(google.com) :
POP3服务器地址:pop.gmail.com(SSL启用 端口:995)
SMTP服务器地址:smtp.gmail.com(SSL启用 端口:587)
Foxmail:
POP3服务器地址:POP.foxmail.com(端口:110)
SMTP服务器地址:SMTP.foxmail.com(端口:25)
sina.com:
POP3服务器地址:pop3.sina.com.cn(端口:110)
SMTP服务器地址:smtp.sina.com.cn(端口:25)
——常用邮箱的服务器(SMTP/POP3)地址和端口总结

设置发送参数:

from_addr = 'xxx@126.com'
password = 'GJZDZMUF' # 密码或邮箱授权码
to_addr = 'xxx@foxmail.com'
smtp_server = 'smtp.126.com'server = smtplib.SMTP(smtp_server, 25) # 设置服务器和端口号
server.set_debuglevel(1) # 打印出和SMTP服务器交互的所有信息,如果没有这一语句,将不打印任何信息
server.login(from_addr, password) # 进行登录
server.sendmail(from_addr, [to_addr], msg.as_string())
# 1. msg.as_string()把MIMEText对象变成str(包含utf-8编码信息和Base64编码)
# 2. [to_addr]表示可以有多个收件人。多个收件人,传入list。当然,也可以在前面to_addr处直接用list进行赋值,那么此处就不是传入[to_addr],而是传入to_addr了
server.quit()

二、定时

【方法一】利用datetime包

datetime.datetime.now()可以返回现在的本地时间(默认本地时区)

In[7]: import datetime
In[8]: now = datetime.datetime.now() # 返回datetime.datetime数据类型
In[9]: print(now)
2020-12-30 17:03:09.681370
In[10]: now.replace(microsecond = 0) # 通过replace()方法去掉微秒
Out[10]: datetime.datetime(2020, 12, 30, 17, 3, 9)
In[11]: print(now) # replace()方法不改变now本身
2020-12-30 17:03:09.681370
In[12]: print(now.replace(microsecond = 0))
2020-12-30 17:03:09

设定定时变量scheduled_time,并判断是否达到该时间:

scheduled_time = datetime.datetime(2021, 1, 1, 00, 00, 00) # 将时间定为2021年1月1月0时0分0秒
while True:now = datetime.datetime.now().replace(microsecond=0)if now == scheduled_time:print('时间到啦!新年快乐!')break # 不写break的话,也可以增加scheduled_time的值。如果都不写,会遇到重复运行的问题

三、写一个完整的程序

from email import encoders
from email.header import Header
from email.mime.text import MIMEText
from email.utils import parseaddr, formataddr
import smtplibdef _format_addr(s):name, addr = parseaddr(s)return formataddr((Header(name, 'utf-8').encode(), addr))from_addr = 'xxx@126.com'
password = 'GJZDZMUF' # 密码或邮箱授权码
to_addr = 'xxx@foxmail.com'
smtp_server = 'smtp.126.com'def send_mail():server = smtplib.SMTP(smtp_server, 25)
#    server.set_debuglevel(1)server.login(from_addr, password)server.sendmail(from_addr, [to_addr], msg.as_string())server.quit()import datetime
i = 1
limit = 3
scheduled_time = datetime.datetime(2020, 12, 30, 18, 0, 0)
print('首次发送邮件的时间是:', scheduled_time)
while True:now = datetime.datetime.now().replace(microsecond=0)if now == scheduled_time:msg = MIMEText('This is a test' , 'plain', 'utf-8')msg['From'] = _format_addr('发件人 <%s>' % from_addr)msg['To'] = _format_addr('收件人 <%s>' % to_addr)msg['Subject'] = Header('TEST%s...' % i, 'utf-8').encode()send_mail()scheduled_time = scheduled_time + datetime.timedelta(seconds = 10)print('第%s次邮件发送成功!现在时间是' % i, datetime.datetime.now().replace(microsecond=0))i = i + 1if i < limit:print('下一次发送邮件的时间是:', scheduled_time.replace(microsecond=0))else: passif i >= limit:print('所有邮件发送完毕,现在的时间是:', datetime.datetime.now().replace(microsecond=0))break

四、遇到的错误

【注1】实际操作时,如果传入未被Header编码处理的name,发送邮件时暂未发现报错(英文与中文皆是)。因此此处待进一步验证。

参考资料:
SMTP发送邮件
从Python email模块理解邮件生命周期及MIME
python:利用smtplib模块发送邮件详解

用python定时发送邮件相关推荐

  1. 技巧 | python定时发送邮件(自动添加附件)

    文章目录 1. 邮箱设置 1.1 设置 1.2 获取POP3/SMTP服务授权码 2. 发送邮件 2.1 导入email/smtplib相关模块 2.2 email模块 2.2.1 邮箱设置 2.2. ...

  2. python定时发送邮件的条件_python实现定时发送邮件

    本文实例为大家分享了python实现定时发送邮件的具体代码,供大家参考,具体内容如下 一.发送邮件 import smtplib from email.mime.text import MIMETex ...

  3. python定时发送邮件_Python3实现带附件的定时发送邮件功能

    本文实例为大家分享了Python3定时发送邮件功能的具体代码,供大家参考,具体内容如下 1. 导入模块 import os import datetime #定时发送,以及日期 import shut ...

  4. python定时发送邮件

    E:\reptile\day10\邮件发送\具体故事.py #-*-coding:utf-8-*- #-*-coding:utf-8-*- import requests,json from fake ...

  5. 定时运行python脚本并发送邮件_python实现定时发送邮件到指定邮箱

    本文实例为大家分享了python实现定时发送邮件到指定邮箱的具体代码,供大家参考,具体内容如下 整个链路:传感器采集端采集数据,边缘端上传数据库,从数据库拿到数据. 产品端有个自动出报告的需求,并且希 ...

  6. 用python实现自动化办公------定时发送邮件

    用python实现自动化办公------定时发送邮件 摘要 一.注册"和风天气" 二.用python获取和风天气响应的json数据 三.发送邮件 四.写入日志 程序源码 摘要 本文 ...

  7. 【python热搜爬虫+定时发送邮件操作①】不会吧不会吧!不会2020了还有人需要用软件看微博热搜吧?

    以下内容为本人原创,欢迎大家观看学习,禁止用于商业用途,转载请说明出处,谢谢合作! ·作者:@Yhen ·原文网站:CSDN ·原文链接:https://blog.csdn.net/Yhen1/art ...

  8. 用python自动化定时发送邮件(普通文本,html,图片,附件等)_亲测有效

    这周有需求将Bi报表每天定时,自动的群发给team成员,今天搜集资料完成了这个需求,可以发送普通文本,图片,附件已经html形式将其展现出来,整套代码如下:已亲测可行,相关信息已脱敏~ 写完脚本后登陆 ...

  9. 【python微博爬虫+定时发送邮件操作②】不会吧不会吧!不会2020了还有人需要用软件看微博热搜吧?

    以下内容为本人原创,欢迎大家观看学习,禁止用于商业用途, ·作者:@Yhen ·原文网站:CSDN ·原文链接:https://blog.csdn.net/Yhen1/article/details/ ...

  10. odoo定时发送邮件

    采购订单延迟或者存在部分到货的情况,定时发送邮件给相关人员 包含,采购订单明细,订单数量,已到货数量,未到货数量 <?xml version="1.0" encoding=& ...

最新文章

  1. Java多线程编程笔记4:Java内存模型
  2. java之通过FileChannel实现文件复制
  3. 2021 CSP-S 游记
  4. 1040 有几个PAT (25 分
  5. 常用WebService一览表(一)
  6. 2-Eighteenth Scrum Meeting-20151218
  7. Java基础学习总结(69)——匿名内部类与Lambda表达式
  8. hamcrest的jar包_重新设计Hamcrest
  9. K8s中nodePort、port、targetPort、hostPort介绍
  10. Python 防止反编译
  11. C#winform中弹出提示框,点击确认或者取消(是或否)
  12. 打印机服务器纸张属性不显示,为什么我的打印机能在打印机服务器属性里设置自定义纸张大小,却无法? 爱问知识人...
  13. python金融实战 源代码_Python金融股票爬虫实战源码大全
  14. Go语言中的条件变量Cond
  15. 使用jsoup入门java爬虫 案例
  16. bom成本分析模型_BOM成本估算表
  17. 互联网大厂造AI芯片,前景如何?
  18. 深度学习作业L1W4:深层神经网络
  19. FRP内网穿透映射本地内网80端口到云服务器
  20. 安卓Service组件使用系列2:使用Service下载网络图片并存储于sdCard卡上

热门文章

  1. 南充十中高考2021成绩查询,2021年南充十中招办电话是多少?
  2. Amazon Silk 你所不知道的在Kindle背后的大数据
  3. 美团外卖前端可视化界面组装平台 —— 乐高
  4. rem 针对设计稿宽度,设计rem调试比例
  5. AcWing237. 程序自动分析
  6. 配置flashgot+axel
  7. 印刷厂ERP系统源码
  8. 人,要活得明白。活到极致,就是素与简。
  9. pycharm如何下载库?
  10. Cortex-A7 MPCore 架构