使用Python定时发送邮件

需求说明

每日定时发送邮件,邮件中包含excel数据

发送邮件代码

# -*- coding:utf-8 -*-

import email, smtplib, ssl

from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import time


smtp_server = "smtp.exmail.qq.com"
sender_email = "analysis@test.com"  # Enter your address
password = "1234"

port = 25

receiver_email = ["test@gmail.com"]  # Enter receiver address


subject = "测试邮件主题"
body = """测试邮件内容"""

# 发送方法,file_name是发送附件的路径与名称
def send_email(file_name):
    # Create a multipart message and set headers
    message = MIMEMultipart()
    message["From"] = sender_email
    # mime message must be strings
    message["To"] = ', '.join(receiver_email)
    subjectTxt = subject.format(time.strftime('%Y%m%d%H%M',time.localtime(time.time())))
    message["Subject"] = subjectTxt
    # mime message must be strings
    message["Bcc"] = ', '.join(receiver_email)  # Recommended for mass emails

    # Add body to email
    message.attach(MIMEText(body.format(subjectTxt), "plain"))

    # Open PDF file in binary mode
    with open(file_name, "rb") as attachment:
        # Add file as application/octet-stream
        # Email client can usually download this automatically as attachment
        part = MIMEBase("application", "octet-stream")
        part.set_payload(attachment.read())

    # Encode file in ASCII characters to send by email    
    encoders.encode_base64(part)

    # Add header as key/value pair to attachment part
    part.add_header(
        "Content-Disposition",
        f"attachment; filename= {file_name}",
    )

    # Add attachment to message and convert message to string
    message.attach(part)
    text = message.as_string()
    

    # Log in to server using secure context and send email
    context = ssl.create_default_context()
    with smtplib.SMTP(smtp_server, port) as server:
        server.ehlo()  # Can be omitted
        server.starttls(context=context)
        server.ehlo()  # Can be omitted
        server.login(sender_email, password)
        server.sendmail(sender_email, receiver_email, text)

这里是关键的发邮件代码,执行send_email方法,相对路径+文件名称作为参数,即可发送邮件。本地测试成功后即可部署到服务器

部署到阿里云服务器

前置准备:请自行安装python3

本地测试成功,部署到阿里云服务器,出现异常:

  File "/data/scripts/cps-email/SendEmail.py", line 64, in send_email
    with smtplib.SMTP(smtp_server, port) as server:
  File "/usr/local/python-3.6.7/lib/python3.6/smtplib.py", line 251, in __init__
    (code, msg) = self.connect(host, port)
  File "/usr/local/python-3.6.7/lib/python3.6/smtplib.py", line 338, in connect
    (code, msg) = self.getreply()
  File "/usr/local/python-3.6.7/lib/python3.6/smtplib.py", line 394, in getreply
    raise SMTPServerDisconnected("Connection unexpectedly closed")
smtplib.SMTPServerDisconnected: Connection unexpectedly closed

异常原因:

代码中使用发送邮件服务的端口是25,在阿里云上是不安全端口

解决方法:

  • 端口换成:456
  • 修改代码

原代码:

 # Log in to server using secure context and send email
    context = ssl.create_default_context()
    with smtplib.SMTP(smtp_server, port) as server:
        server.ehlo()  # Can be omitted
        server.starttls(context=context)
        server.ehlo()  # Can be omitted
        server.login(sender_email, password)
        server.sendmail(sender_email, receiver_email, text)

改成如下:

    # Log in to server using secure context and send email
    context = ssl.create_default_context()
    with smtplib.SMTP_SSL(smtp_server, port) as server:
        server.login(sender_email, password)
        server.sendmail(sender_email, receiver_email, text)

再次执行脚本成功发送

使用crontab定时执行Python脚本

如果是直接在crontab中执行python脚本:

*/1 * * * * python3 /data/scripts/cps-email/Report.py /data/scripts/cps-email/run.log 2>&1

会出现脚本中文件路径错误问题,必须使用绝对路径。因为crontab执行脚本时根目录是python3的安装路径

解决办法是把执行语句放入shell脚本中执行:

#发送cps邮件数据
#!/bin/bash
#切换到脚本目录
cd /data/scripts/cps-email/
python3 /data/scripts/cps-email/Report.py 

再把shell脚本部署到crontab中:

*/1 * * * * sh /data/scripts/cps-email/sendCpsEmail.sh >> /data/scripts/cps-email/run.log 2>&1

测试成功,大功告成!

上一篇:回复 "Timer008" 的一个类型转换的问题 - 不是很典型, 对其他人参考价值不大


下一篇:HTML基础-04