做了一个html E-Letter项目. 邮件模板采用jinja2, html 邮件内容生成简直太爽了. 整个项目开发只用了2个小时, 调试却花了大半天时间, 生成的邮件总是发不出去. 于是, 打开 smtp 的debuglevel, 发现邮件已经mail queue了, 但就是收不到邮件. mail server是exchange. 之前用java写过类似的程序也没有问题(也是走smtp协议发送html邮件). 为什么这次用python实现却有问题? 怀疑过python smtp模块用法, 怀疑过python smtp的html写法, 怀疑过smtp subject和content不支持unicode. 最终都被排除. 最后终于定位到元凶: 邮件的subject只要包含" Please check."这13个字符, 邮件就会石沉大海. 难道是exchange server设了某种规则?
#------------------ 资料: #------------------ cnblogs 小五义的文章不错, <<python发送各类邮件的主要方法>>, http://www.cnblogs.com/xiaowuyi/archive/2012/03/17/2404015.html
#------------------ 代码结构 #------------------ 下面简单描述一下项目的结构, 在我的脚手架项目基础上, 做了少量的裁剪, 并增加两个目录, scripts和templates, scripts目录存放E-Letter生成和发送脚本, templates目录存放E-Letter的模板. 并附上 mail_service.py, 修改了网上找的代码, 用来发送 html 邮件. py_package |--scripts |--check1.py |--check2.py |--templates |--check1.html |--check2.html
# -*- coding: utf-8 -*- # check1.py ''' Created on 2014-5-23 ''' from __future__ import absolute_import import jinja2 from jinja2.loaders import FileSystemLoader def get_template(file_name): ''' get template html with jinja2 format in templates folder ''' template_path=os.path.join(os.path.dirname(os.path.dirname( __file__)), ' templates ') template_env = jinja2.Environment(loader=FileSystemLoader(template_path)) return template_env.get_template(file_name) def check(): template=get_template( ' check1.html ') something= ' something here ' mail_body=template.render(something=something) mail_sender=HtmlMailSender() mail_subject= ' Some subject here ' # mail_subject='Please check.' #cannot send out, why? mail_to=[ ' a@corp.com ', ' b.corp.com '] mail_cc=[] mail_sender.send_html_mail(mail_to,mail_cc,mail_subject,mail_body)
# -*- coding: utf-8 -*- # conf.py ''' Created on 2014-6-23 ''' from __future__ import absolute_import import logging # #logging log_level=logging.INFO # email setting smtp_host= " 10.10.10.10 " smtp_port=25 smtp_over_ssl=False mail_user= " a@corp.com " mail_pwd= "" # if no auth required, set pwd as empty
# -*- coding: utf-8 -*- # mail_service.py ''' Created on 2014-6-23 ''' from __future__ import absolute_import import logging from . import conf class HtmlMailSender(object): logger=logging.getLogger( __name__) def __init__(self): # read mail settings from configure file self.smtp_host=conf.smtp_host self.smtp_port=conf.smtp_port self.smtp_over_ssl=conf.smtp_over_ssl self.mail_user=conf.mail_user self.mail_pwd=conf.mail_pwd def send_html_mail(self, to_list, cc_list, subject, body): self.logger.info( ' send_html_mail() called. ') import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart # Construct email msgRoot = MIMEMultipart( ' related ') msgRoot[ ' Subject '] = subject msgRoot[ ' From '] = self.mail_user msgRoot[ ' To '] = " , ".join(to_list) msgRoot[ ' CC '] = " , ".join(cc_list) # msgRoot['BCC'] =",".join(cc_list) msgRoot.preamble = ' This is a multi-part message in MIME format. ' # Encapsulate the plain and HTML versions of the message body in an # 'alternative' part, so message agents can decide which they want to display. msgAlternative = MIMEMultipart( ' alternative ') msgRoot.attach(msgAlternative) # Add plain content msgText = MIMEText( ' This is HTML mail. If you see this message, which means you will not see the real mail content. ', ' plain ') msgAlternative.attach(msgText) # add html content msgText = MIMEText(body, ' html ') msgAlternative.attach(msgText) try: if not self.smtp_over_ssl: if self.smtp_port== '': s = smtplib.SMTP(self.smtp_host) else: s = smtplib.SMTP(self.smtp_host, self.smtp_port) else: if self.smtp_port== '': s = smtplib.SMTP_SSL(self.smtp_host) else: s = smtplib.SMTP_SSL(self.smtp_host, self.smtp_port) s.set_debuglevel(True) # print stmp actions to stdout if self.mail_pwd : s.login(self.mail_user,self.mail_pwd) to_addrs=to_list+cc_list s.sendmail(self.mail_user ,to_addrs, msgRoot.as_string()) # s.sendmail(from_addr ,to_addrs, 'test message') s.quit() self.logger.info( """ Mail sent. Find details below, to_list: %s cc_list: %s subject: %s body: %s """%(to_list, cc_list, subject, body)) return True except Exception, ex: self.logger.exception(ex) return False