基於smtplib包製作而成,但在實踐中發現一個不知道算不算是smtplib留的一個坑,在網路斷開的情況下發送郵件時會拋出一個socket.gaierror的異常,但是smtplib中並沒有捕獲這個異常,導致程式會因這個異常終止,因此代碼中針對這部分的異常進行處理,確保不會異常終止。
#!/usr/bin/env python# -*- coding: utf-8 -*-__author__ = 'Zoa Chou'# see http://www.mudoom.com/Article/show/id/29.html for detailimport loggingimport smtplibimport mimetypesimport socketfrom email import encodersfrom email.header import Headerfrom email.mime.text import MIMEText, MIMENonMultipartfrom email.mime.base import MIMEBasefrom email.utils import parseaddr, formataddrclass Mailer(object): def __init__(self): pass def send_mail(self, smtp_server, from_address, to_address, subject, body, files=None): """ 發送郵件主程式 :param smtp_server: dict 郵件伺服器設定 :keyword host: string smtp伺服器位址 :keyword port: int smtp伺服器連接埠號碼 :keyword user: string 使用者名稱 :keyword passwd: string 密碼 :keyword ssl: bool 是否啟用ssl,預設False :keyword timeout: int 逾時時間,預設10s :param from_address: 寄件者郵箱 :param to_address: 收件者郵箱 :param subject: 郵件標題 :param body: 郵件內容 :param files: 附件 :raise: NetworkError/MailerException """ # 格式化郵件內容 body = self._encode_utf8(body) # 郵件類型 content_type = 'html' if body.startswith('') else 'plain' msg = MIMENonMultipart() if files else MIMEText(body, content_type, 'utf-8') # 格式化郵件資料 msg['From'] = self._format_address(from_address) msg['To'] = ', '.join(self._format_list(to_address)) msg['subject'] = self._encode_utf8(subject) # 構造附件資料 if files: msg.attach(MIMEText(body, content_type, 'utf-8')) cid = 0 for file_name, payload in files: file_name = self._encode_utf8(file_name) main_type, sub_type = self._get_file_type(file_name) if hasattr(payload, 'read'): payload = payload.read() f_name = self._encode_header(file_name) mime = MIMEBase(main_type, sub_type, filename=f_name) mime.add_header('Content-Disposition', 'attachment', filename=f_name) mime.add_header('Content-ID', '<%s>' % cid) mime.add_header('X-Attachment-Id', '%s' % cid) mime.set_payload(payload) encoders.encode_base64(mime) msg.attach(mime) cid += 1 host = smtp_server.get('host') port = smtp_server.get('port') user = smtp_server.get('user') passwd = smtp_server.get('passwd') ssl = smtp_server.get('ssl', False) time_out = smtp_server.get('timeout', 10) # 沒有輸入連接埠則使用預設連接埠 if port is None or port == 0: if ssl: port = 465 else: port = 25 logging.debug('Send mail form %s to %s' % (msg['From'], msg['To'])) try: if ssl: # 開啟ssl串連模式 server = smtplib.SMTP_SSL('%s:%d' % (host, port), timeout=time_out) else: server = smtplib.SMTP('%s:%d' % (host, port), timeout=time_out) # 開啟偵錯模式 # server.set_debuglevel(1) # 如果存在使用者名稱密碼則嘗試登入 if user and passwd: server.login(user, passwd) # 發送郵件 server.sendmail(from_address, to_address, msg.as_string()) logging.debug('Mail sent success.') # 關閉stmp串連 server.quit() except socket.gaierror, e: """ 網路無法串連 """ logging.exception(e) raise NetworkError(e) except smtplib.SMTPServerDisconnected, e: """ 網路連接異常 """ logging.exception(e) raise NetworkError(e) except smtplib.SMTPException, e: """ 郵件發送異常 """ logging.exception(e) raise MailerException(e) def _format_address(self, s): """ 格式化郵件地址 :param s:string 郵件地址 :return: string 格式化後的郵件地址 """ name, address = parseaddr(s) return formataddr((self._encode_header(name), self._encode_utf8(address))) def _encode_header(self, s): """ 格式化符合MIME的頭部資料 :param s: string 待格式化資料 :return: 格式化後的資料 """ return Header(s, 'utf-8').encode() def _encode_utf8(self, s): """ 格式化成utf-8編碼 :param s: string 待格式化資料 :return: string 格式化後的資料 """ if isinstance(s, unicode): return s.encode('utf-8') else: return s def _get_file_type(self, file_name): """ 擷取附件類型 :param file_name: 附件檔案名稱 :return: dict 附件MIME """ s = file_name.lower() pos = s.rfind('.') if pos == -1: return 'application', 'octet-stream' ext = s[pos:] mime = mimetypes.types_map.get(ext, 'application/octet-stream') pos = mime.find('/') if pos == (-1): return mime, '' return mime[:pos], mime[pos+1:] def _format_list(self, address): """ 將收件者地址格式化成list :param address: string/list 收件者郵箱 :return: list 收件者郵箱list """ l = address if isinstance(l, basestring): l = [l] return [self._format_address(s) for s in l]class MailerException(Exception): """ 郵件發送異常類 """ passclass NetworkError(MailerException): """ 網路異常類 """ pass# test for @qq.comif __name__ == '__main__': import sys def prompt(prompt): """ 接收終端輸入的資料 """ sys.stdout.write(prompt + ": ") return sys.stdin.readline().strip() from_address = prompt("From(Only @qq.com)") passwd = prompt("Password") to_address = prompt("To").split(',') subject = prompt("Subject") print "Enter message, end with ^D:" msg = '' while 1: line = sys.stdin.readline() if not line: break msg = msg + line print "Message length is %d" % len(msg) # QQ郵箱預設設定 smtp_server = {'host': 'smtp.qq.com', 'port': None, 'user': from_address, 'passwd': passwd, 'ssl': True} mailer = Mailer() try: mailer.send_mail(smtp_server, from_address, to_address, subject, msg) except MailerException, e: print(e)
以上所述就是本文的全部內容了,希望大家能夠喜歡。