由于工作中经常需要收发电子邮件,例如每日(周)的工作报告,测试报告,监控告警,定时提醒等等,大都已电子邮件的形式发送。本文将实现一个 Python 的电子邮件发送类,支持发送多个附件(目录),HTML或纯文本内容,抄送收件人,多个接收者等功能。
代码实现
#!/usr/bin/env python # -*- coding: utf-8 -*- ''' Copyright (C) 2015 By Thomas Hu. All rights reserved. @author : Thomas Hu @version: 1.0 @created: 2015-05-17 ''' import base64 import httplib import re import os import smtplib from xml.etree import ElementTree from email.utils import formatdate from email.MIMEText import MIMEText from email.MIMEBase import MIMEBase from email.MIMEMultipart import MIMEMultipart from email import Encoders class EmailSender(object): def __init__(self, smtp_server, smtp_port=0, verbose=False, debug_level=1, encoding="utf-8"): ''' Initiate the EmailSender. @param smtp_server: the Email SMTP server. @param smtp_port: the Email SMTP server port, if use the default port(25), you can set it to 0 or 25. @param verbose: show the processing information if set to 'True', default is 'False'. @param debug_level: set the smtplib debug level, if it's '0', will enable debug information. @param encoding: the encoding or charset for email body text or attachment file name, default is "utf-8". ''' self.server = smtp_server self.port = int(smtp_port) self.verbose = verbose self.debug_level = int(debug_level) self.encoding = encoding self.attachments = [] #Create smtp instance self.smtp = smtplib.SMTP(self.server, self.port) self.smtp.set_debuglevel(self.debug_level) self.print_verbose("Init SMTP server successfully. server=%s, port=%d."%(self.server, self.port)) def print_verbose(self, message): '''Print the verbose information. @param message: the message to be print if the verbose is "True". ''' if self.verbose: print(message) def login(self, user, password): '''Login to SMTP server. @param user: the user name of the email sender. @param password: the passord of the user for login to SMTP server. ''' self.from_addr = user + "@" + ".".join(self.server.split(".")[1:]) try: self.print_verbose("Start to login into SMTP server.server=%s, port=%d."%(self.server, self.port)) self.smtp.login(user, password) self.print_verbose("Login into SMTP server successfully.") except Exception as ex: print("Login into SMTP server failed! Error info: %s"%(str(ex))) def __add_attachment_file(self, filename, encoding, Filter): '''Add attachment file to the attachment list. @param filename: the file name of attachment, should not be a path. @param encoding: the encode of the attachment file name. @param Filter: the file filter object, must implement 'accept(filename)' interface, and return True or False. ''' # Check if the file is acceptable by the Filter if Filter is not None: try: accept = Filter.accept(filename) except: accept = False if accept == False: return # Add the attachment to the attachment list try: basename = os.path.basename(filename) attach = MIMEBase("application", "octet-stream") attach.set_payload(open(filename, "rb").read()) #attach.add_header("Content-Disposition", "attachment;filename=%s"%(basename)) attach.add_header("Content-Disposition", "attachment", filename=(encoding, "", basename)) Encoders.encode_base64(attach) self.attachments.append(attach) self.print_verbose("Add attachment \"%s\" successfully."%(filename)) except Exception as ex: print("Add attachment file \"%s\" failed. Error info: %s."%(filename, str(ex))) def add_attachment(self, path, encoding=None, Filter=None): '''Add attachment file to the attachment list. @param path: the path of files to be added as attachment files. If is a directory, all of the files in it will be added. @param encoding: the encode of the attachment file name. @param Filter: the file filter object, must implement 'accept(filename)' interface, and return True or False. ''' if not os.path.exists(path): self.print_verbose("Warning: attachment path \"%s\" is not exists."%(path)) return charset = encoding if (encoding is not None) else self.encoding if os.path.isfile(path): return self.__add_attachment_file(path, charset, Filter) for root, dirs, files in os.walk(path): for f in files: fname = os.path.join(root, f) self.__add_attachment_file(fname, charset, Filter) def send_email(self, subject, to_addrs, cc_addrs, content, subtype="plain", charset=None): '''Send the email to the receivers. @param subject: the email's subject(title). @param to_addrs: the receivers' addresses, it's a list looks like ["[email protected]_server.com", "[email protected]_server.com"]. @param cc_addrs: the copy to receivers' addresses, has the same format as to_addrs. @param content: the email message body, it can be plain text or HTML text, which depends on the parameter 'content_type'. @param subtype: the content type, it can be "html" or "plain", default is "plain". @param charset: the charset of message content, default is "None", use the same encoding as the initial function, which default is "utf-8". @return: if send successfully, return 'True', otherwise, return 'False'. ''' charset = charset if charset is not None else self.encoding #Set the root information msg_root = MIMEMultipart("related") msg_root["Subject"] = subject msg_root["From"] = self.from_addr # You can change it to any string msg_root["To"] = ",".join(to_addrs) msg_root["CC"] = ",".join(cc_addrs) msg_root["Date"] = formatdate(localtime=True) msg_root.preamble = "This is a multi-part message in MIME format." #Encapsulate the plain and HTML of the message body into an 'alternative' part, #so message agents can decide which they want to display. msg_alt = MIMEMultipart("alternative") #Set the message content msg_txt = MIMEText(content, subtype.lower(), charset) msg_alt.attach(msg_txt) #Add the alternative part to root part. msg_root.attach(msg_alt) #Add the attachment files for attach in self.attachments: msg_root.attach(attach) #Extend the copy to addresses to to_addrs to_addrs.extend(cc_addrs) #Send the email try: self.smtp.sendmail(self.from_addr, to_addrs, msg_root.as_string()) self.print_verbose("Send email successfully.") except Exception as ex: print("Send email failed. Error info:%s"%(str(ex))) return False return True def close(self): '''Quit from the SMTP. ''' self.smtp.quit() self.print_verbose("Logout SMTP server successfully.") def test(): smtp_server = "smtp.163.com" user = "yyy" password = "yyyxxx" from_addr = "[email protected]" to_addrs = ["[email protected]"] cc_addrs = [] subject = "Email sending test" content ='Dear Friends,<p/><a href="http://blog.csdn.net/thomashtq"> Welcome to my CSDN blog!</a> <p/>Thanks a lot!' attach_files=[r"D:\temp\sendemail\attach"] emailsender = EmailSender(smtp_server, verbose=True) emailsender.login(user, password) for attach in attach_files: emailsender.add_attachment(attach, "utf-8") emailsender.send_email(subject, to_addrs, cc_addrs, content, subtype="html", charset="utf-8") emailsender.close() if __name__ == '__main__': test()
代码中都有详细的解释,请原谅我用英文注释啊,习惯了^_^。 运行时,请将 test() 函数中相关的用户名、密码、服务器、收件人、附件列表等进行修改。也就是 test()函数中开头那段代码(空行之前的),根据自己的具体情况进行修改就 OK 了。
时间: 2024-10-31 17:23:42