python發送郵件并帶有附件,如果安裝相關的包,先用pip安裝這些包:smtplib,email
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
# msg,郵件的正文
def send_mail(msg):
# 設置郵件服務器,請按實際情況修改
mail_host="smtp.xxx.com.cn"
# 登錄郵件的帳號,請按實際情況修改
mail_user="test"
# 登錄郵件的密碼,請按實際情況修改
mail_pass="test123456"
# 發送人,請按實際情況修改
sender='test@xxx.com.cn'
# 接收人,,請按實際情況修改,多個接收人以逗號隔開
receivers=['test1@xxx.com.cn', 'test2@xxx.com.cn']
# msg=MIMEMultipart('mixed')
msg=MIMEMultipart('related')
# 郵件標題,請按實際修改
msg['Subject']=u"這里是郵件的標題 "
msg['From']=sender
# 收件人為多個收件人,通過join將列表轉換為以;為間隔的字符串
msg['To']=";".join(receivers)
# 設置郵件正文
html_msg=msg
text_plain=MIMEText(html_msg, 'html', 'utf-8')
msg.attach(text_plain)
# 設置附件,file_path是附件的路徑,請按實際情況修改
file_path="d:\\test.html"
sendfile=MIMEApplication(open(file_path, 'rb').read())
sendfile.add_header('Content-Disposition', 'attachment', filename='fail-report.html')
msg.attach(sendfile)
try:
smtpObj=smtplib.SMTP()
smtpObj.connect(mail_host)
smtpObj.login(mail_user, mail_pass)
smtpObj.sendmail(sender, receivers, msg.as_string())
print ("郵件發送成功")
except smtplib.SMTPException:
print ("郵件發送失敗")
近使用了不少通訊工具的接口, 比如企業微信機器人,釘釘,微信公眾號的接口(未認證的訂閱公眾號),相對于郵件來說,它們的表現形式太弱。比如沒有更豐富的版本方式。當然了,并不是說表現形式越棒就是越好的通知手段,這個依個人情況而定,而我恰恰需要比較豐富的表現形式,最終還是回到了郵件,郵件真香!
而個人微信號的接口我沒有合適的微信號可以登錄,如果網頁版微信沒有被封的話,我想這個是表現形式與消息時效性結合的最好的方式。
雖說就發郵件這么個小事,很容易兼容Python2, Python3, 但是大家還是擁抱Python3吧, 我這里沒有做python2的兼容寫法,所以需要python3以上。
郵件的格式主要就兩種: plain和html
plain就像一個普通的文本, 沒有格式。
html就如其名, 是html的格式,相當于一個郵件就是一個靜態的網頁,這樣的話可玩性就很高了,你可以通過css控制表現形式.
注意: 這里的css雖然語法一樣,但,是否與瀏覽器渲染結果完全一致, 是不一定的。
那么可能有人要問了,我要發一個動態的網頁怎么辦? 發個鏈接呀
無論是QQ郵箱抑或網易郵箱都是沒有問題的,重要的是有一個可以通過smtp服務器發送郵件的賬戶名及密碼,這里大家百度吧。
因為發送郵件的代碼在下面每個步驟都是一樣的所以先貼出來
def send_email(msg, mail_to, smtp_host, smtp_username, smtp_password, subject, from_):
msg["Subject"]=Header(subject, "utf-8")
msg["From"]=Header(from_, "utf-8")
if not isinstance(mail_to, list):
mail_to=[mail_to]
msg["To"]=COMMASPACE.join(mail_to)
try:
print("準備連接smtp郵件服務器: %s" % smtp_host)
client=smtplib.SMTP(smtp_host)
print("連接成功")
# client=smtplib.SMTP("localhost")
# client.set_debuglevel(1)
# print(self.mail_user, self.mail_pass)
client.login(smtp_username, smtp_password)
print("登錄成功")
# print("=====>", self.mail_from, mail_to)
print("通過郵箱[%s]發送郵件給 %s" % (smtp_username, COMMASPACE.join(mail_to)))
client.sendmail(smtp_username, mail_to, msg.as_string())
print("發送成功...")
return True
except Exception:
print("發送郵件失敗")
finally:
client.quit()
如果遇到郵件發送的問題可以將client.set_debuglevel(1)的注釋取消,這樣會顯示足夠多的debug信息用于排查問題。
這里發送圖片的意思是指, 圖片內嵌在郵件中而不是以附件的形式出現。
效果如下:
代碼如下:
EMAIL_IMAGE_TEMPLATE="""<html>
<head>
<title>Page Title</title>
</head>
<body>
<h3>這是一張圖片</h3>
<p><img src="cid:{{image_name}}" height="112" width="200" ></p>
</body>
</html>
"""
def create_image_eamil_contant(fp):
tpl=Template(EMAIL_IMAGE_TEMPLATE)
if not path.exists(fp):
sys.exit("要發送的本地圖片不存在")
msg=MIMEMultipart("related")
image_name="demo"
with open(fp, "rb") as rf:
mime_image=MIMEImage(rf.read())
# 注意: 一定需要<>括號
mime_image.add_header("Content-ID", "<%s>" % image_name)
msg.attach(mime_image)
# 渲染郵件文本內容
text=tpl.render(image_name=image_name)
msg_alternative=MIMEMultipart("alternative")
msg_alternative.attach(MIMEText(text, "html", "utf-8"))
msg.attach(msg_alternative)
return msg
如果你使用過python的web框架,你對文本的渲染一定不陌生,因為大多數web框架都支持文本渲染,這里使用的jinja2.
其實這里跟上面沒什么區別的,唯一的區別就是是否保存在本地,既然能發送本地圖片,我就先保存到本地然后再按照上面的方式不就可以了么? 首先這個方法是沒有問題的,不過多了一次IO, 能在內存中解決的事為什么要放到本地呢?
這種情況主要是應對回去圖片的方式是從其他接口獲取到的,或者實時生成的時候。雖然很簡單,但覺得說說也挺有意思的。
這里的模擬方式是假設在網上獲取到了多張base64編碼的圖片,需要將其組合在一起,然后在不保存在本地情況下直接發送這張照片。
這個base64編碼的圖片已經保存在本地了,名字是demo_base64.txt
效果如下:
代碼如下:
EMAIL_ONLINE_IMAGE_TEMPLATE="""<html>
<head>
<title>Page Title</title>
</head>
<body>
<h3>這是一張圖片</h3>
<p><img src="cid:{{image_name}}" ></p>
</body>
</html>
"""
def create_online_image_content():
from PIL import Image
tpl=Template(EMAIL_ONLINE_IMAGE_TEMPLATE)
fp="demo_base64.txt"
if not path.exists(fp):
sys.exit("要發送的base64編碼的圖片不存在")
msg=MIMEMultipart("related")
image_name="demo"
with open(fp, "rb") as rf:
base64_data=rf.read()
img_data=base64.b64decode(base64_data)
# 因為open方法需要一個file-like文件對象,而我們解碼后的對象類型是bytes類型
# bytes類型沒有文件對象的read, close方法,所以我們需要通過BytesIO對象包裝一下,它會返回一個file-like文件對象
img=Image.open(BytesIO(img_data))
img_width, img_height=img.size
repeat_times=5
# compose images
ret_img=Image.new(img.mode, (img_width, img_height * repeat_times))
for index in range(repeat_times):
ret_img.paste(img, box=(0, index * img_height))
# 因為MIMEImage需要一個bytes對象,所以們需要獲取圖片編碼后的二進制數據而不是圖片的array數據
img_bytes=BytesIO()
# 如果不指定圖片格式,會因為沒有文件名而報錯
ret_img.save(img_bytes, "png")
mime_image=MIMEImage(img_bytes.getvalue())
# 注意: 一定需要<>括號
mime_image.add_header("Content-ID", "<%s>" % image_name)
msg.attach(mime_image)
# 渲染郵件文本內容
text=tpl.render(image_name=image_name)
msg_alternative=MIMEMultipart("alternative")
msg_alternative.attach(MIMEText(text, "html", "utf-8"))
msg.attach(msg_alternative)
return msg
這里很有意思一點是用BytesIO模擬file-like對象。這里需要安裝PIL哦
前面的代碼已經足夠說明圖片怎么發了,這里通過一個寫了css樣式的表格進行演示
效果如下:
代碼如下:
EMAIL_TEMPLATE="""<html>
<head>
<style type="text/css">
table
{
border-collapse: collapse;
margin: 0 auto;
text-align: center;
}
table td, table th
{
border: 1px solid #cad9ea;
color: #666;
height: 30px;
}
table thead th
{
background-color: #CCE8EB;
width: 100px;
}
table tr:nth-child(odd)
{
background: #fff;
}
table tr:nth-child(even)
{
background: #F5FAFA;
}
</style>
</head>
<body>
<p>一共有以下{{record_size}}條數據</p>
<table width="90%" class="table">
<thead>
<tr>
{% for label in labels %}
<th>{{label}}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for item in items %}
<tr>
{% for value in item %}
<td>{{value}}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</html>"""
def create_html_content():
tpl=Template(EMAIL_TEMPLATE)
record_size=10
label_size=5
labels=["label-%s" % i for i in range(label_size)]
items=[]
for _ in range(record_size):
item=["item-%s" % value_index for value_index in range(label_size)]
items.append(item)
text=tpl.render(record_size=record_size, items=items, labels=labels)
msg=MIMEText(text, "html", "utf-8")
return msg
https://github.com/youerning/blog/tree/master/sendmail
如果期待后續文章可以關注我的微信公眾號(又耳筆記),頭條號(又耳筆記),github.
其實發送一個附件也是不錯的方式,比如發送一個生成的PDF, PDF是一個很棒的文件格式。但是PDF暫時沒用到,以后有機會再說吧。最后要注意的是,手機端的顯示效果跟電腦網頁版的顯示效果是不一樣的。
https://www.runoob.com/python/python-email.html
錄
假設我們想設計一個定時任務,比如每天定時的用python來測試服務是否在正常運行,但是又不希望每天登錄到系統后臺去查看服務狀態。這里我們就可以采取python的smtp模塊進行任務結果廣播,申請一個公共郵箱,每次python執行完定時的測試任務后,調用smtp的接口將測試結果廣播給需要接收的人的郵箱中。這就使得,我們可以在移動端就能按照我們的意愿實時監測系統的狀態。
這里我們直接展示成果代碼,其中一些隱私信息做了處理:
# smtp_test.py
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import smtplib
from email.mime.text import MIMEText
from email.header import Header
# 第三方 SMTP 服務
mail_host="smtp.qq.com" #設置服務器
mail_user="your_name@qq.com" #用戶名
mail_pass="passpasspasspass" #口令
sender='your_name@qq.com'
receivers=['your_target@qq.com'] # 接收郵件,可設置為你的QQ郵箱或者其他郵箱
message=MIMEText('Python SMTP 郵件發送測試...', 'plain', 'utf-8')
message['From']=Header("SMTP EMAIL", 'utf-8')
message['To']=Header("TEST MESSAGE", 'utf-8')
subject='Python SMTP 郵件測試'
message['Subject']=Header(subject, 'utf-8')
try:
smtpObj=smtplib.SMTP()
smtpObj.connect(mail_host, 25) # 25 為 SMTP 端口號
smtpObj.login(mail_user,mail_pass)
smtpObj.sendmail(sender, receivers, message.as_string())
print ("郵件發送成功")
except smtplib.SMTPException:
import traceback
traceback.print_exc()
print ("無法發送郵件")
這里的服務器配置的SMTP的服務器smtp.qq.com,對應端口號配置為25,這里的口令和帳號應替換為讀者自己的授權口令和帳號。該程序的正常結果如下:
[dechin@dechin-manjaro smtp]$ python3 smtp_test.py
郵件發送成功
另外由于這里采用了tracback做錯誤日志采集,因此即使有報錯程序也能繼續執行,但是會廣播錯誤日志。
最后通過查詢郵箱里面的郵件(有時候可能會被放到垃圾箱里面),正常情況下可以看到一份這樣的郵件:
crontab是Linux系統下自帶的定時任務配置服務,基本使用方法就是通過crontab -l來查看定時任務,以及通過crontab -e來編輯定時任務。但是由于自帶的編輯器為nano,使用起來非常的不順手,所以我們可以將其編輯器配置為vim再進行使用,相關指令為:
[dechin@dechin-manjaro smtp]$ export EDITOR="/usr/bin/vim" ; crontab -e
當然,在當前用戶登錄界面下,只需要臨時配置一次即可一直直接使用crontab -e進行配置,持久生效需要修改配置文件,這里不展開介紹。crontab的任務配置可以參考如下介紹(圖片來自于參考鏈接2):
這里我們首先創建一個簡單的打印隨機數的任務,這樣如果我們在crontab中添加一個執行該程序的定時任務,就可以每次產生一個不同的隨機數并且將其輸出到一個指定的文件中,再通過另外一個smtp的定時任務進行讀取和廣播。以下是打印隨機數的任務內容:
[dechin@dechin-manjaro smtp]$ cat random_job.py
import random
print (random.random())
我們將前面用到的smtp的任務稍作修改,將隨機數讀取到郵件標題中:
# smtp_test.py
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import smtplib
from email.mime.text import MIMEText
from email.header import Header
# 第三方 SMTP 服務
mail_host="smtp.qq.com" #設置服務器
mail_user="your_email@qq.com" #用戶名
mail_pass="passpasspasspass" #口令
sender='your_email@qq.com'
receivers=['your_target@qq.com'] # 接收郵件,可設置為你的QQ郵箱或者其他郵箱
message=MIMEText('Python SMTP 郵件發送測試...', 'plain', 'utf-8')
message['From']=Header("SMTP EMAIL", 'utf-8')
message['To']=Header("TEST MESSAGE", 'utf-8')
random_number=1
with open('/home/dechin/projects/2021-python/smtp/random_number.txt', 'r') as file:
random_number=float(file.readlines()[0])
subject='The random number generated is: ' + str(random_number)
message['Subject']=Header(subject, 'utf-8')
try:
smtpObj=smtplib.SMTP()
smtpObj.connect(mail_host, 25) # 25 為 SMTP 端口號
smtpObj.login(mail_user,mail_pass)
smtpObj.sendmail(sender, receivers, message.as_string())
print ("郵件發送成功")
except smtplib.SMTPException:
import traceback
traceback.print_exc()
print ("無法發送郵件")
最后,再配置好crontab定時任務如下:
[dechin@dechin-manjaro smtp]$ crontab -l
* * * * * python3 /home/dechin/projects/2021-python/smtp/random_job.py > /home/dechin/projects/2021-python/smtp/random_number.txt
* * * * * python3 /home/dechin/projects/2021-python/smtp/smtp_test.py
上面由于為了盡快地展示定時任務效果因此我們設置為每分鐘都執行一次任務,實際場景中不需要這么高頻率的定時任務測試。
最后查看郵箱收件箱,我們發現了一系列的定時任務的內容反饋如下:
到這里我們的定時監測任務+smtp廣播的示例就演示完畢了,如果使用完該定時任務不想再啟動,可以關閉crontab服務或者刪除相關的crontab條目。
本文首發鏈接為:https://www.cnblogs.com/dechinphy/p/smtp.html
作者ID:DechinPhy
更多原著文章請參考:https://www.cnblogs.com/dechinphy/
*請認真填寫需求信息,我們會在24小時內與您取得聯系。