Python基于smtplib实现异步发送邮件服务
基于smtplib包制作而成,但在实践中发现一个不知道算不算是smtplib留的一个坑,在网络断开的情况下发送邮件时会抛出一个socket.gaierror的异常,但是smtplib中并没有捕获这个异常,导致程序会因这个异常终止,因此代码中针对这部分的异常进行处理,确保不会异常终止。
#!/usr/bin/envpython
#-*-coding:utf-8-*-
__author__='ZoaChou'
#seehttp://www.mudoom.com/Article/show/id/29.htmlfordetail
importlogging
importsmtplib
importmimetypes
importsocket
fromemailimportencoders
fromemail.headerimportHeader
fromemail.mime.textimportMIMEText,MIMENonMultipart
fromemail.mime.baseimportMIMEBase
fromemail.utilsimportparseaddr,formataddr
classMailer(object):
def__init__(self):
pass
defsend_mail(self,smtp_server,from_address,to_address,subject,body,files=None):
"""
发送邮件主程序
:paramsmtp_server:dict邮件服务器设置
:keywordhost:stringsmtp服务器地址
:keywordport:intsmtp服务器端口号
:keyworduser:string用户名
:keywordpasswd:string密码
:keywordssl:bool是否启用ssl,默认False
:keywordtimeout:int超时时间,默认10s
:paramfrom_address:发件人邮箱
:paramto_address:收件人邮箱
:paramsubject:邮件标题
:parambody:邮件内容
:paramfiles:附件
:raise:NetworkError/MailerException
"""
#格式化邮件内容
body=self._encode_utf8(body)
#邮件类型
content_type='html'ifbody.startswith('<html>')else'plain'
msg=MIMENonMultipart()iffileselseMIMEText(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)
#构造附件数据
iffiles:
msg.attach(MIMEText(body,content_type,'utf-8'))
cid=0
forfile_name,payloadinfiles:
file_name=self._encode_utf8(file_name)
main_type,sub_type=self._get_file_type(file_name)
ifhasattr(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)
#没有输入端口则使用默认端口
ifportisNoneorport==0:
ifssl:
port=465
else:
port=25
logging.debug('Sendmailform%sto%s'%(msg['From'],msg['To']))
try:
ifssl:
#开启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)
#如果存在用户名密码则尝试登录
ifuserandpasswd:
server.login(user,passwd)
#发送邮件
server.sendmail(from_address,to_address,msg.as_string())
logging.debug('Mailsentsuccess.')
#关闭stmp连接
server.quit()
exceptsocket.gaierror,e:
"""网络无法连接"""
logging.exception(e)
raiseNetworkError(e)
exceptsmtplib.SMTPServerDisconnected,e:
"""网络连接异常"""
logging.exception(e)
raiseNetworkError(e)
exceptsmtplib.SMTPException,e:
"""邮件发送异常"""
logging.exception(e)
raiseMailerException(e)
def_format_address(self,s):
"""
格式化邮件地址
:params:string邮件地址
:return:string格式化后的邮件地址
"""
name,address=parseaddr(s)
returnformataddr((self._encode_header(name),self._encode_utf8(address)))
def_encode_header(self,s):
"""
格式化符合MIME的头部数据
:params:string待格式化数据
:return:格式化后的数据
"""
returnHeader(s,'utf-8').encode()
def_encode_utf8(self,s):
"""
格式化成utf-8编码
:params:string待格式化数据
:return:string格式化后的数据
"""
ifisinstance(s,unicode):
returns.encode('utf-8')
else:
returns
def_get_file_type(self,file_name):
"""
获取附件类型
:paramfile_name:附件文件名
:return:dict附件MIME
"""
s=file_name.lower()
pos=s.rfind('.')
ifpos==-1:
return'application','octet-stream'
ext=s[pos:]
mime=mimetypes.types_map.get(ext,'application/octet-stream')
pos=mime.find('/')
ifpos==(-1):
returnmime,''
returnmime[:pos],mime[pos+1:]
def_format_list(self,address):
"""
将收件人地址格式化成list
:paramaddress:string/list收件人邮箱
:return:list收件人邮箱list
"""
l=address
ifisinstance(l,basestring):
l=[l]
return[self._format_address(s)forsinl]
classMailerException(Exception):
"""邮件发送异常类"""
pass
classNetworkError(MailerException):
"""网络异常类"""
pass
#testfor@qq.com
if__name__=='__main__':
importsys
defprompt(prompt):
"""
接收终端输入的数据
"""
sys.stdout.write(prompt+":")
returnsys.stdin.readline().strip()
from_address=prompt("From(Only@qq.com)")
passwd=prompt("Password")
to_address=prompt("To").split(',')
subject=prompt("Subject")
print"Entermessage,endwith^D:"
msg=''
while1:
line=sys.stdin.readline()
ifnotline:
break
msg=msg+line
print"Messagelengthis%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)
exceptMailerException,e:
print(e)
以上所述就是本文的全部内容了,希望大家能够喜欢。