在寫腳本時,放到后台運行,想知道執行情況,會通過郵件、SMS(短信)、飛信、微信等方式通知管理員,用的最多的是郵件。在linux下,Shell腳本發送郵件告警是件很簡單的事,有現成的郵件服務軟件或者調用運營商郵箱服務器。
對於Python來說,需要編寫腳本調用郵件服務器來發送郵件,使用的協議是SMTP。接收郵件,使用的協議是POP3和IMAP。我想有必要說明下 ,POP3和IMAP的區別:POP3在客戶端郵箱中所做的操作不會反饋到郵箱服務器,比如刪除一封郵件,郵箱服務器並不會刪除。IMAP則會反饋到郵箱服務器,會做相應的操作。
Python分別提供了收發郵件的庫,smtplib、poplib和imaplib。
本章主要講解如果使用smtplib庫實現發送各種形式的郵件內容。在smtplib庫中,主要主要用smtplib.SMTP()類,用於連接SMTP服務器,發送郵件。
這個類有幾個常用的方法:
方法 |
描述 |
SMTP.set_debuglevel(level) | 設置輸出debug調試信息,默認不輸出 |
SMTP.docmd(cmd[, argstring]) | 發送一個命令到SMTP服務器 |
SMTP.connect([host[, port]]) | 連接到指定的SMTP服務器 |
SMTP.helo([hostname]) | 使用helo指令向SMTP服務器確認你的身份 |
SMTP.ehlo(hostname) | 使用ehlo指令像ESMTP(SMTP擴展)確認你的身份 |
SMTP.ehlo_or_helo_if_needed() | 如果在以前的會話連接中沒有提供ehlo或者helo指令,這個方法會調用ehlo()或helo() |
SMTP.has_extn(name) | 判斷指定名稱是否在SMTP服務器上 |
SMTP.verify(address) | 判斷郵件地址是否在SMTP服務器上 |
SMTP.starttls([keyfile[, certfile]]) | 使SMTP連接運行在TLS模式,所有的SMTP指令都會被加密 |
SMTP.login(user, password) | 登錄SMTP服務器 |
SMTP.sendmail(from_addr, to_addrs, msg, mail_options=[], rcpt_options=[]) | 發送郵件 from_addr:郵件發件人 to_addrs:郵件收件人 msg:發送消息 |
SMTP.quit() | 關閉SMTP會話 |
SMTP.close() | 關閉SMTP服務器連接 |
看下官方給的示例:
>>> import smtplib >>> s=smtplib.SMTP("localhost") >>> tolist=["one@one.org","two@two.org","three@three.org","four@four.org"] >>> msg = '''\ ... From: Me@my.org ... Subject: testin'... ... ... This is a test ''' >>> s.sendmail("me@my.org",tolist,msg) { "three@three.org" : ( 550 ,"User unknown" ) } >>> s.quit()
我們根據示例給自己發一個郵件測試下:
我這里測試使用本地的SMTP服務器,也就是要裝一個支持SMTP協議的服務,比如sendmail、postfix等。
CentOS安裝sendmail:yum install sendmail
>>> import smtplib >>> s = smtplib.SMTP("localhost") >>> tolist = ["xxx@qq.com", "xxx@163.com"] >>> msg = '''\ ... From: Me@my.org ... Subject: test ... This is a test ''' >>> s.sendmail("me@my.org", tolist, msg) {}
進入騰訊和網易收件人郵箱,就能看到剛發的測試郵件,一般都被郵箱服務器過濾成垃圾郵件,所以收件箱沒有,你要去垃圾箱看看。
可以看到,多個收件人可以放到一個列表中進行群發。msg對象里From表示發件人,Subject是郵件標題,換行后輸入的是郵件內容。
上面是使用本地SMTP服務器發送的郵件,測試下用163服務器發送郵件看看效果:
>>> import smtplib >>> s = smtplib.SMTP("smtp.163.com") >>> s.login("baojingtongzhi@163.com", "xxx") (235, 'Authentication successful') >>> tolist = ["xxx@qq.com", "xxx@163.com"] >>> msg = '''\ ... From: baojingtongzhi@163.com ... Subject: test ... This is a test ''' >>> s.sendmail("baojingtongzhi@163.com", tolist, msg) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib64/python2.6/smtplib.py", line 725, in sendmail raise SMTPDataError(code, resp) smtplib.SMTPDataError: (554, 'DT:SPM 163 smtp10,DsCowAAXIdDIJAtYkZiTAA--.65425S2 1477125592,please see http://mail.163.com/help/help_spam_16.htm?ip=119.57.73.67&hostid=smtp10&time=1477125592')
訪問給出的163網址,SMTP554錯誤是: "554 DT:SUM 信封發件人和信頭發件人不匹配;"
大概已經明白啥意思,看上面再使用本地SMTP服務器時候,收件人位置是“undisclosed-recipients”,看這樣163的SMTP服務器不給我們服務的原因就是這里收件人沒指定。
重新修改下msg對象,添加上收件人:
>>> msg = '''\ ... From: baojingtongzhi@163.com ... To: 962510244@qq.com ,xxx@163.com ... Subject: test ... ... This is a test ''' >>> s.sendmail("baojingtongzhi@163.com", tolist, msg) {}
好了,可以正常發送郵件了。msg這個格式是SMTP規定的,一定要遵守。
1.1 Python發送郵件並抄送
#!/usr/bin/python # -*- coding: utf-8 -*- import smtplib def sendMail(body): smtp_server = 'smtp.163.com' from_mail = 'baojingtongzhi@163.com' mail_pass = 'xxx' to_mail = ['xxx@qq.com', 'xxx@163.com'] cc_mail = ['lizhenliang@xxx.com'] from_name = 'monitor' subject = u'監控'.encode('gbk') # 以gbk編碼發送,一般郵件客戶端都能識別 # msg = '''\ # From: %s <%s> # To: %s # Subject: %s # %s''' %(from_name, from_mail, to_mail_str, subject, body) # 這種方式必須將郵件頭信息靠左,也就是每行開頭不能用空格,否則報SMTP 554 mail = [ "From: %s <%s>" % (from_name, from_mail), "To: %s" % ','.join(to_mail), # 轉成字符串,以逗號分隔元素 "Subject: %s" % subject, "Cc: %s" % ','.join(cc_mail), "", body ] msg = '\n'.join(mail) # 這種方式先將頭信息放到列表中,然后用join拼接,並以換行符分隔元素,結果就是和上面注釋一樣了 try: s = smtplib.SMTP() s.connect(smtp_server, '25') s.login(from_mail, mail_pass) s.sendmail(from_mail, to_mail+cc_mail, msg) s.quit() except smtplib.SMTPException as e: print "Error: %s" %e if __name__ == "__main__": sendMail("This is a test!")
s.sendmail(from_mail, to_mail+cc_mail, msg) 在這里注意下,收件人和抄送人為什么放一起發送呢?其實無論是收件人還是抄送人,它們收到的郵件都是一樣的,SMTP都是認為收件人這樣一封一封的發出。所以實際上並沒有抄送這個概念,只是在郵件頭加了抄送人的信息罷了!另外,如果不需要抄送人,直接把上面cc的信息去掉即可。
1.2 Python發送郵件帶附件
由於SMTP.sendmail()方法不支持添加附件,所以可以使用email模塊來滿足需求。email模塊是一個構造郵件和解析郵件的模塊。
先看下如何用email庫構造一個簡單的郵件:
message = Message() message['Subject'] = '郵件主題' message['From'] = from_mail message['To'] = to_mail message['Cc'] = cc_mail message.set_payload('郵件內容')
基本的格式就是這樣的!
繼續回到主題,發送郵件帶附件:
#!/usr/bin/python # -*- coding: utf-8 -*- import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.header import Header from email import encoders from email.mime.base import MIMEBase from email.utils import parseaddr, formataddr # 格式化郵件地址 def formatAddr(s): name, addr = parseaddr(s) return formataddr((Header(name, 'utf-8').encode(), addr)) def sendMail(body, attachment): smtp_server = 'smtp.163.com' from_mail = 'baojingtongzhi@163.com' mail_pass = 'xxx' to_mail = ['xxx@qq.com', 'xxx@163.com'] # 構造一個MIMEMultipart對象代表郵件本身 msg = MIMEMultipart() # Header對中文進行轉碼 msg['From'] = formatAddr('管理員 <%s>' % from_mail).encode() msg['To'] = ','.join(to_mail) msg['Subject'] = Header('監控', 'utf-8').encode() # plain代表純文本 msg.attach(MIMEText(body, 'plain', 'utf-8')) # 二進制方式模式文件 with open(attachment, 'rb') as f: # MIMEBase表示附件的對象 mime = MIMEBase('text', 'txt', filename=attachment) # filename是顯示附件名字 mime.add_header('Content-Disposition', 'attachment', filename=attachment) # 獲取附件內容 mime.set_payload(f.read()) encoders.encode_base64(mime) # 作為附件添加到郵件 msg.attach(mime) try: s = smtplib.SMTP() s.connect(smtp_server, "25") s.login(from_mail, mail_pass) s.sendmail(from_mail, to_mail, msg.as_string()) # as_string()把MIMEText對象變成str s.quit() except smtplib.SMTPException as e: print "Error: %s" % e if __name__ == "__main__": sendMail('附件是測試數據, 請查收!', 'test.txt')
博客地址:http://lizhenliang.blog.51cto.com
QQ群:323779636(Shell/Python運維開發群)
1.3 Python發送HTML郵件
#!/usr/bin/python # -*- coding: utf-8 -*- import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.header import Header from email.utils import parseaddr, formataddr # 格式化郵件地址 def formatAddr(s): name, addr = parseaddr(s) return formataddr((Header(name, 'utf-8').encode(), addr)) def sendMail(body): smtp_server = 'smtp.163.com' from_mail = 'baojingtongzhi@163.com' mail_pass = 'xxx' to_mail = ['xxx@qq.com', 'xxx@163.com'] # 構造一個MIMEMultipart對象代表郵件本身 msg = MIMEMultipart() # Header對中文進行轉碼 msg['From'] = formatAddr('管理員 <%s>' % from_mail).encode() msg['To'] = ','.join(to_mail) msg['Subject'] = Header('監控', 'utf-8').encode() msg.attach(MIMEText(body, 'html', 'utf-8')) try: s = smtplib.SMTP() s.connect(smtp_server, "25") s.login(from_mail, mail_pass) s.sendmail(from_mail, to_mail, msg.as_string()) # as_string()把MIMEText對象變成str s.quit() except smtplib.SMTPException as e: print "Error: %s" % e if __name__ == "__main__": body = """ <h1>測試郵件</h1> <h2 style="color:red">This is a test</h1> """ sendMail(body)
1.4 Python發送圖片郵件
#!/usr/bin/python # -*- coding: utf-8 -*- import smtplib from email.mime.text import MIMEText from email.mime.image import MIMEImage from email.mime.multipart import MIMEMultipart from email.header import Header from email.utils import parseaddr, formataddr # 格式化郵件地址 def formatAddr(s): name, addr = parseaddr(s) return formataddr((Header(name, 'utf-8').encode(), addr)) def sendMail(body, image): smtp_server = 'smtp.163.com' from_mail = 'baojingtongzhi@163.com' mail_pass = 'xxx' to_mail = ['xxx@qq.com', 'xxx@163.com'] # 構造一個MIMEMultipart對象代表郵件本身 msg = MIMEMultipart() # Header對中文進行轉碼 msg['From'] = formatAddr('管理員 <%s>' % from_mail).encode() msg['To'] = ','.join(to_mail) msg['Subject'] = Header('監控', 'utf-8').encode() msg.attach(MIMEText(body, 'html', 'utf-8')) # 二進制模式讀取圖片 with open(image, 'rb') as f: msgImage = MIMEImage(f.read()) # 定義圖片ID msgImage.add_header('Content-ID', '<image1>') msg.attach(msgImage) try: s = smtplib.SMTP() s.connect(smtp_server, "25") s.login(from_mail, mail_pass) s.sendmail(from_mail, to_mail, msg.as_string()) # as_string()把MIMEText對象變成str s.quit() except smtplib.SMTPException as e: print "Error: %s" % e if __name__ == "__main__": body = """ <h1>測試圖片</h1> <img src="cid:image1"/> # 引用圖片 """ sendMail(body, 'test.png')
上面發郵件的幾種常見的發郵件方法基本滿足日常需求了。
原文地址:http://lizhenliang.blog.51cto.com/7876557/1875330
PS.以上均轉載未測試,以下是親測無誤的代碼(Python3.6版本測)
包括發送郵件,抄送郵件多人功能
import smtplib from email.mime.text import MIMEText from email.header import Header mail = Mail(app) sender = 'kakarrot2009@163.com' subject = '我是測試的' smtpserver = 'smtp.163.com' username = 'kakarrot2009@163.com' password = '************' tolist = ["xxxxxxxx@qq.com", "yyyyyyy@qq.com"]#接收者 tocc=["mmmmmm@qq.com", "nnnnnn@qq.com"]#抄送者 msg = MIMEText('你好', 'plain', 'utf-8') # 中文需參數‘utf-8',單字節字符不需要 msg['Subject'] = Header(subject, 'utf-8')#郵件中顯示內容 msg['From']='卡卡羅特<kakarrot2009@163.com>' #郵件中顯示內容 msg['To']='zoro<xxxxxxxx@qq.com>;nami<yyyyyyy@qq.com>'#郵件中顯示內容 msg['CC']='sanji<mmmmmm@qq.com>;luffy<nnnnnn@qq.com>'#郵件中顯示內容 smtp = smtplib.SMTP() smtp.connect('smtp.163.com') smtp.login(username, password) smtp.sendmail(sender,tolist+tocc, msg.as_string().encode()) smtp.quit()