1. 设置QQ邮箱
QQ邮箱是SSL认证的邮箱系统,要用QQ邮箱发送邮件,需要开启POP3/ SMTP服务,并获取授权码。
2. 代码
前面已经提到过,QQ邮箱是SSL认证的邮箱系统,因此用QQ邮箱发送邮件,需要创建一个SMTP_SSL对象,而不是SMTP对象,然后用发送邮箱及之前获取到的授权码login,最后调用sendmail()发送邮件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import smtplib smtpserver = "smtp.qq.com" smtpport = 465 password = "****************" # 16位授权码 try: smtp = smtplib.SMTP_SSL(smtpserver,smtpport) smtp.login(from_mail,password) smtp.sendmail(from_mail,to_mail,msg.as_string()) except(smtplib.SMTPException) as e: print(e.message) finally: smtp.quit() |
用try-except-finally括起来,是为了捕捉可能的错误信息。
这很简单,但细心的读者应该注意到,上面代码中所有的方法的参数都有定义,唯独sendmail(from_addr, to_addrs, msg)的第3个参数msg,即邮件的发送内容没有定义,而发送邮件的关键就在于此。发送内容的格式是SMTP规定的,如果不符合规定,即会导致邮件发送失败。
邮件主要包括三个部分:一是我称之为属性的部分,二是正文,三是附件。包含各部分内容的是一个MIMEMultipart对象,其实邮件的任何部分都是可以为空的,甚至都为空,还是可以成功发送的。下面主要讨论的就是这三部分内容的组织。
2.1 组织邮件属性
邮件的属性,我指的是诸如发件人、主题、收件人、抄送等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
from email.mime.multipart import MIMEMultipart from email.header import Header subject = "test report" from_name = "水云之外" msg = MIMEMultipart() msg["Subject"] = Header(subject, "utf-8") msg["From"] = Header(from_name + " <" + from_mail + ">", "utf-8") msg["To"] = Header(",".join(to_mail), "utf-8") msg["Cc"] = Header(",".join(cc_mail), "utf-8") |
2.2 组织邮件正文
2.2.1 文本格式正文
用正文字符串生成一个plain类型的MIMEText对象,可指定编码,然后黏贴到代表邮件全部内容的MIMEMultipart对象中。
1 2 3 4 5 6 7 8 |
from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText body = "hi, the attachment is the test report of this test, please check it in time." msg = MIMEMultipart() msgtext = MIMEText(body, "plain", "utf-8") msg.attach(msgtext) |
2.2.2 HTML格式正文
HTML格式的正文和文本格式的正文,唯一区别就是MIMEText对象的类型不同,是html,当然正文字符串应该是一个符合html格式的字符串。
1 2 3 4 5 6 7 8 |
from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText body = "<h3>hi, the attachment is the test report of this test, please check it in time.</h3>" msg = MIMEMultipart() msgtext = MIMEText(body, "html", "utf-8") msg.attach(msgtext) |
2.2.3 包含图片的正文
包含图片的正文,其实是一种特殊的html格式正文,正文字符串应符合html格式,且包含img标签,指定src,用读入图片文件创建一个MIMEImage对象,指定其属性,应和src中相匹配,最后将MIMEImage对象也黏贴到MIMEMultipart对象中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.image import MIMEImage imgbody = ''' <h3>hi, the attachment is the test report of this test, please check it in time.</h3> <img src="cid:image1"/> ''' imgfile = r"..\test\result.png" msg = MIMEMultipart() msgtext = MIMEText(imgbody, "html", "utf-8") msg.attach(msgtext) file = open(imgfile, "rb") img = MIMEImage(file.read()) img.add_header("Content-ID", "<image1>") msg.attach(img) |
2.3 组织邮件附件
经验证,文本文件、HTML文件和图片文件(png),都可以用下面的代码组装到邮件内容中。生成一个MIMEBase对象,读入附件文件加载到该对象中,编码、添加头后同样黏贴到MIMEMultipart对象中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
from email.mime.multipart import MIMEMultipart from email.mime.base import MIMEBase attfile = r"..\test\result.html" msg = MIMEMultipart() att = MIMEBase("application", "octet-stream") file = open(file, "rb") att.set_payload(file.read()) encoders.encode_base64(att) list = file.split("\\") filename = list[len(list) - 1] att.add_header("Content-Disposition", "attachment; filename='%s'" %filename) msg.attach(att) |
3. 最终代码
3.1 MailAssembler类
包含三个方法:attachAttributes()、attachBody()和attachAttachment(),分别用来组装属性、正文和附件。
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 |
from email.header import Header from email.mime.base import MIMEBase from email.mime.text import MIMEText from email.mime.image import MIMEImage from email import encoders class MailAssembler: def attachAttributes(self,msg,subject,from_name,from_mail,to_mail,cc_mail=None): msg["Subject"] = Header(subject, "utf-8") msg["From"] = Header(from_name + " <" + from_mail + ">", "utf-8") msg["To"] = Header(",".join(to_mail), "utf-8") msg["Cc"] = Header(",".join(cc_mail), "utf-8") def attachBody(self,msg,body,type,imgfile=None): msgtext = MIMEText(body, type, "utf-8") msg.attach(msgtext) if imgfile != None: try: file = open(imgfile, "rb") img = MIMEImage(file.read()) img.add_header("Content-ID", "<image1>") msg.attach(img) except(Exception) as err: print(str(err)) finally: if file in locals(): file.close() def attachAttachment(self,msg,attfile): att = MIMEBase("application", "octet-stream") try: file = open(attfile, "rb") att.set_payload(file.read()) encoders.encode_base64(att) except(Exception) as err: print(str(err)) finally: if file in locals(): file.close() if "\\" in attfile: list = attfile.split("\\") filename = list[len(list) - 1] else: filename = attfile att.add_header("Content-Disposition", "attachment; filename='%s'" %filename) msg.attach(att) |
3.2 MailSender类
只有一个sendMail()方法,初始化的时候保存了发送的相关参数,之后就可以用该方法发送其参数msg了。
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 |
import smtplib class MailSender: def __init__(self,smtpserver,smtpport,password,from_mail,to_mail,cc_mail=None): self.smtpserver = smtpserver self.smtpport = smtpport self.password = password self.from_mail = from_mail self.to_mail = to_mail self.cc_mail = cc_mail def sendMail(self,msg): try: smtp = smtplib.SMTP_SSL(self.smtpserver, self.smtpport) smtp.login(self.from_mail, self.password) if self.cc_mail == None: smtp.sendmail(self.from_mail, self.to_mail, msg.as_string()) else: smtp.sendmail(self.from_mail, self.to_mail+self.cc_mail, msg.as_string()) print("successful") except(smtplib.SMTPRecipientsRefused): print("Recipient refused") except(smtplib.SMTPAuthenticationError): print("Auth error") except(smtplib.SMTPSenderRefused): print("Sender refused") except(smtplib.SMTPException) as e: print(e.message) finally: smtp.quit() |
3.3 主程序
在主程序中,调用了MailAssembler和MailSender的方法,完成邮件内容的组装和发送。邮件正文包含文字和图片,并有两个附件。
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 |
from email.mime.multipart import MIMEMultipart from src.MailContent import MailAssembler from src.MailSender import MailSender subject = "test report" from_name = "水云之外" imgbody = ''' <h3>hi, the attachment is the test report of this test, please check it in time.</h3> <img src="cid:image1"/> ''' file1 = r"..\test\result.html" file2 = r"..\test\result.txt" imgfile = r"..\test\result.png" smtpserver = "smtp.qq.com" smtpport = 465 password = "****************" # 授权码 msg = MIMEMultipart() assembler = MailAssembler() sender = MailSender(smtpserver,smtpport,password,from_mail,to_mail,cc_mail) assembler.attachAttributes(msg,subject,from_name,from_mail,to_mail,cc_mail) assembler.attachBody(msg,imgbody,"html",imgfile) assembler.attachAttachment(msg,file1) assembler.attachAttachment(msg,file2) sender.sendMail(msg) |
4. 参考文献
[1] 使用python发送QQ邮件 https://www.cnblogs.com/lovealways/p/6701662.html
[2] Python发送邮件(常见四种邮件内容)https://blog.csdn.net/xiaosongbk/article/details/60142996
[3] Selenium3+python3-发送添加附件的邮件 https://www.cnblogs.com/liyanqi/p/7885014.html