一直觉得发送邮件再简单不过,直到最近深陷几个邮件大坑,费了不少时间才爬出来,当然其中也不乏公司环境给我挖的坑,代码不断,填坑不止呀……
python发邮件主要是借助框架( 如Django),以及pure python,其实底层都一样。
借助Django,具体settings及发送邮件code如下:
1
2
3
4
5
6
7
8
9
10
EMAIL_HOST_USER = '*****'
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
EMAIL_FROM = EMAIL_HOST_USER
EMAIL_HOST = 'smtp.partner.outlook.cn'
EMAIL_HOST_PASSWORD = 'Pul13448'
#看邮箱配置
EMAIL_PORT = 587
#看邮箱配置
EMAIL_USE_TLS = True
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
1
2
3
4
5
6
7
8
9
10
11
12
13
from django.core.mail import EmailMessage
import os
def send_mail_attachment(subject,body,to=[],attach_file=None):
try:
email = EmailMessage(subject,body,from_email=DEFAULT_FROM_EMAIL,to=to)
attach_file = os.path.join(BASE_DIR,attach_file)
email.attach_file(attach_file)
email.send(fail_silently=False)
print('send mail success,mail attachment:{}'.format(attach_file))
return True
except Exception as e:
logger.exception(e)
return False
pure python,这里介绍通过thread发送,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class EmailThreadPure(threading.Thread):
def __init__(self, subject, message, fail_silently=False, html_message=None):
self.smtpserver = EMAIL_HOST
self.user = EMAIL_HOST_USER
self.sender = EMAIL_HOST_USER
self.receiver = [a[1] for a in settings.ADMINS]
self.body = message
self.subject = subject
self.html_message = html_message
threading.Thread.__init__(self)
def attach(self):
"""if attachment,use it"""
ctype, encoding = mimetypes.guess_type(self.attach_file)
if ctype is None or encoding is not None:
ctype = "application/octet-stream"
maintype, subtype = ctype.split("/", 1)
if maintype == "text":
with open(self.attach_file, "rb") as fp:
# Note: we should handle calculating the charset
attachment = MIMEText(fp.read(), _subtype=subtype)
elif maintype == "image":
with open(self.attach_file, "rb") as fp:
attachment = MIMEImage(fp.read(), _subtype=subtype)
elif maintype == "audio":
with open(self.attach_file, "rb") as fp:
attachment = MIMEAudio(fp.read(), _subtype=subtype)
else:
with open(self.attach_file, "rb") as fp:
attachment = MIMEBase(maintype, subtype)
attachment.set_payload(fp.read())
encoders.encode_base64(attachment)
encoders.encode_base64(attachment)
attachment.add_header("Content-Disposition", "attachment", filename=self.attach_file)
self.msg.attach(attachment)
def run(self):
# use html and plain mode,if no html,just self.msg = MIMEMultipart()
self.msg = MIMEMultipart('alternative')
self.msg['Subject'] = Header('%s%s' % (settings.EMAIL_SUBJECT_PREFIX, self.subject), 'utf-8')
self.msg['from'] = EMAIL_HOST_USER
# msg[to] is a string
self.msg['to'] = ', '.join(self.receiver)
self.msg.attach(MIMEText(self.body, 'plain', 'utf-8'))
self.msg.attach(MIMEText(self.html_message, 'html', 'utf-8'))
server = smtplib.SMTP(self.smtpserver, EMAIL_PORT, timeout=30)
#debug mode
# server.set_debuglevel(1)
# USE TLS
server.ehlo()
server.starttls()
# if need login
server.login(EMAIL_HOST_USER,EMAIL_HOST_PASSWORD)
#self.receiver is a list
server.sendmail(self.sender, self.receiver, self.msg.as_string())
server.quit()
列坑
-
DEFAULT_FROM_EMAIL与send mail中的from_email不一致。
error info
:SMTPDataError: (554, ‘5.2.0 STOREDRV.Submission.Exception:SendAsDeniedException.MapiExceptionSendAsDenied; Failed to process message due to a permanent exception with message Cannot submit message.
fix
:统一DEFAULT_FROM_EMAIL与send mail中的from_email应该使用tsl时,未设置tsl。
-
error info
:AUTH extension not supported by serverfix
:设置tsl,settings设置或者1 2
mail.ehlo() mail.starttls()
-
python pure发邮件时,当多个收件人,可能只有第一个收件人能收到。
error info
:无,但会发现只有第一个人收到。fix
:因为self.receiver为str的时候,会自动截取第一个。 self.msg[‘to’] = ‘, ‘.join(self.receiver)
server.sendmail(self.sender, self.receiver, self.msg.as_string())
self.receiver为list
-
如果settings配置错误,很有可能是不会报错并且返回发送邮件数量也为1,但实际上是收不到邮件,检查端口号及tsl和ssl 配置。我碰到的情况时logger中设置的mail setting与Django setting backend不一样,加上公司邮箱设置问题,导致mail setting 里的邮件发不出来。
-
未设置timeout时间,导致一直在connect mail server,无log,会误认为已经发送好了。
error info
: 无,退出时会出现connect timeout,否则会一直连接。fix
:设置timeout时间。 可以使用telnet server port方式看是否连接有问题。 -
避开敏感词汇,否则也可能会报554错误,如test等。