Python基于smtplib實現(xiàn)異步發(fā)送郵件服務
基于smtplib包制作而成,但在實踐中發(fā)現(xiàn)一個不知道算不算是smtplib留的一個坑,在網(wǎng)絡斷開的情況下發(fā)送郵件時會拋出一個socket.gaierror的異常,但是smtplib中并沒有捕獲這個異常,導致程序會因這個異常終止,因此代碼中針對這部分的異常進行處理,確保不會異常終止。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'Zoa Chou'
# see http://www.mudoom.com/Article/show/id/29.html for detail
import logging
import smtplib
import mimetypes
import socket
from email import encoders
from email.header import Header
from email.mime.text import MIMEText, MIMENonMultipart
from email.mime.base import MIMEBase
from email.utils import parseaddr, formataddr
class Mailer(object):
def __init__(self):
pass
def send_mail(self, smtp_server, from_address, to_address, subject, body, files=None):
"""
發(fā)送郵件主程序
: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: 發(fā)件人郵箱
:param to_address: 收件人郵箱
:param subject: 郵件標題
:param body: 郵件內(nèi)容
:param files: 附件
:raise: NetworkError/MailerException
"""
# 格式化郵件內(nèi)容
body = self._encode_utf8(body)
# 郵件類型
content_type = 'html' if body.startswith('<html>') else 'plain'
msg = MIMENonMultipart() if files else MIMEText(body, content_type, 'utf-8')
# 格式化郵件數(shù)據(jù)
msg['From'] = self._format_address(from_address)
msg['To'] = ', '.join(self._format_list(to_address))
msg['subject'] = self._encode_utf8(subject)
# 構(gòu)造附件數(shù)據(jù)
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)
# 開啟調(diào)試模式
# server.set_debuglevel(1)
# 如果存在用戶名密碼則嘗試登錄
if user and passwd:
server.login(user, passwd)
# 發(fā)送郵件
server.sendmail(from_address, to_address, msg.as_string())
logging.debug('Mail sent success.')
# 關(guān)閉stmp連接
server.quit()
except socket.gaierror, e:
""" 網(wǎng)絡無法連接 """
logging.exception(e)
raise NetworkError(e)
except smtplib.SMTPServerDisconnected, e:
""" 網(wǎng)絡連接異常 """
logging.exception(e)
raise NetworkError(e)
except smtplib.SMTPException, e:
""" 郵件發(fā)送異常 """
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的頭部數(shù)據(jù)
:param s: string 待格式化數(shù)據(jù)
:return: 格式化后的數(shù)據(jù)
"""
return Header(s, 'utf-8').encode()
def _encode_utf8(self, s):
"""
格式化成utf-8編碼
:param s: string 待格式化數(shù)據(jù)
:return: string 格式化后的數(shù)據(jù)
"""
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):
""" 郵件發(fā)送異常類 """
pass
class NetworkError(MailerException):
""" 網(wǎng)絡異常類 """
pass
# test for @qq.com
if __name__ == '__main__':
import sys
def prompt(prompt):
"""
接收終端輸入的數(shù)據(jù)
"""
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)
以上所述就是本文的全部內(nèi)容了,希望大家能夠喜歡。
相關(guān)文章
使用Python中tkinter庫簡單gui界面制作及打包成exe的操作方法(二)
這篇文章主要介紹了使用Python中tkinter庫簡單gui界面制作及打包成exe的操作方法(二),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10
python中main函數(shù)(主函數(shù))相關(guān)應用例子
這篇文章主要介紹了python中main函數(shù)(主函數(shù))相關(guān)應用,本文通過示例代碼給大家介紹的非常詳細,需要的朋友可以參考下2023-05-05
Python Numpy中數(shù)據(jù)的常用保存與讀取方法
這篇文章主要介紹了Python Numpy中數(shù)據(jù)的常用保存與讀取方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04
使用python對多個txt文件中的數(shù)據(jù)進行篩選的方法
今天小編就為大家分享一篇使用python對多個txt文件中的數(shù)據(jù)進行篩選的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-07-07
python?pygame英雄循環(huán)飛行及作業(yè)示例
這篇文章主要為大家介紹了python?pygame英雄循環(huán)飛行及作業(yè)實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-08-08

