Python學習之旅(三十五)
Python基礎知識(34):電子郵件(Ⅰ)
幾乎所有的程式語言都支援傳送和接收電子郵件
在使用Python收發郵件前,請先準備好至少兩個電子郵件,如[email protected]
,[email protected]
,[email protected]
等,注意兩個郵箱不要用同一家郵件服務商
電子郵件傳送過程:
發件人email->MUA(mail user agent:郵件使用者代理)->MTA(mail transfer agent:郵件傳輸代理)->MDA(mail delivery agent:郵件投遞代理,即伺服器)<-MUA<-收件人
使用SMTP傳送郵件
SMTP是傳送郵件的協議,Python內建對SMTP的支援,可以傳送純文字郵件、HTML郵件以及帶附件的郵件
Python對SMTP支援有smtplib
和email
兩個模組,email
負責構造郵件,smtplib
負責傳送郵件
注:以下所有程式碼本人未進行測試,故無法確定是否能夠正常執行
一、傳送郵件
1、構造郵件
from email.mime.text import MIMEText msg = MIMEText('Hello, world', 'plain', 'utf-8')
到構造MIMEText
物件時,第一個引數就是郵件正文,第二個引數是MIME的subtype,傳入'plain'
'text/plain'
,最後一定要用utf-8
編碼保證多語言相容性
2、通過SMTP傳送郵件
SMTP.sendmail(from_addr, [to_addr], msg.as_string())
sendmail()
方法就是發郵件,由於可以一次發給多個人,所以傳入一個list
,郵件正文是一個str
,as_string()
把MIMEText
物件變成str
必須把From
、To
和Subject
新增到MIMEText
中,才是一封完整的郵件
from email import encoders from email.header importHeader from email.mime.text import MIMEText from email.utils import parseaddr, formataddr import smtplib # 考慮到編碼的原因,這裡統一將name屬性值改成utf-8,地址的話一定是統一的郵箱地址結構,所以不考慮 def __format_addr(s): name, addr = parseaddr(s) return formataddr((Header(name, 'utf-8').encode(), addr)) # 一些基本資訊的定義 from_addr = input('From:') password = input('Password:') to_addr = input('To: ') smtp_server = input('SMTP server: ') # 純文字郵件定義 msg = MIMEText('Hello world!', 'plain', 'utf-8') # 定義傳送人,接收人,以及描述資訊(主題) msg['From'] = __format_addr('發件人 <%s>' % from_addr) msg['To'] = __format_addr('收件人: <%s>' % to_addr) msg['Subject'] = Header('SMTP......', 'utf-8').encode() print(msg)
結果如下
From:[email protected].com Password:1234 To: [email protected] SMTP server: smtp.163.com Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: base64 From: =?utf-8?b?5Y+R5Lu25Lq6?= <[email protected]> To: [email protected] Subject: =?utf-8?b?U01UUC4uLi4uLg==?= SGVsbG8gd29ybGQh
二、傳送HTML郵件
在構造MIMEText
物件時,把HTML字串傳進去,再把第二個引數由plain
變為html
msg = MIMEText('<html><body><h1>Hello</h1>' + '<p>send by <a href="http://www.python.org">Python</a>...</p>' + '</body></html>', 'html', 'utf-8')
三、傳送附件
帶附件的郵件可以看做包含若干部分的郵件:文字和各個附件本身,所以,可以構造一個MIMEMultipart
物件代表郵件本身,然後往裡面加上一個MIMEText
作為郵件正文,再繼續往裡面加上表示附件的MIMEBase
物件即可
# 郵件物件: msg = MIMEMultipart() msg['From'] = _format_addr('Python愛好者 <%s>' % from_addr) msg['To'] = _format_addr('管理員 <%s>' % to_addr) msg['Subject'] = Header('來自SMTP的問候……', 'utf-8').encode() # 郵件正文是MIMEText: msg.attach(MIMEText('send with file...', 'plain', 'utf-8')) # 新增附件就是加上一個MIMEBase,從本地讀取一個圖片: with open('/Users/michael/Downloads/test.png', 'rb') as f: # 設定附件的MIME和檔名,這裡是png型別: mime = MIMEBase('image', 'png', filename='test.png') # 加上必要的頭資訊: mime.add_header('Content-Disposition', 'attachment', filename='test.png') mime.add_header('Content-ID', '<0>') mime.add_header('X-Attachment-Id', '0') # 把附件的內容讀進來: mime.set_payload(f.read()) # 用Base64編碼: encoders.encode_base64(mime) # 新增到MIMEMultipart: msg.attach(mime)
按正常傳送流程把msg
(注意型別已變為MIMEMultipart
)傳送出去
四、傳送圖片
不能直接在HTML郵件中連結圖片地址,因為大部分郵件服務商都會自動遮蔽帶有外鏈的圖片,因為不知道這些連結是否指向惡意網站
要把圖片嵌入到郵件正文中,只需按照發送附件的方式,先把郵件作為附件新增進去,然後,在HTML中通過引用src="cid:0"
就可以把附件作為圖片嵌入了。如果有多個圖片,給它們依次編號,然後引用不同的cid:x
即可
把上面程式碼加入MIMEMultipart
的MIMEText
從plain
改為html
,然後在適當的位置引用圖片
msg.attach(MIMEText('<html><body><h1>Hello</h1>' + '<p><img src="cid:0"></p>' + '</body></html>', 'html', 'utf-8'))
五、同時支援HTML和Plain格式
如果收件人使用的裝置太古老,檢視不了HTML郵件,必須在傳送HTML的同時再附加一個純文字,如果收件人無法檢視HTML格式的郵件,就可以自動降級檢視純文字郵件
利用MIMEMultipart
就可以組合一個HTML和Plain,要注意指定subtype是alternative
msg = MIMEMultipart('alternative') msg['From'] = ... msg['To'] = ... msg['Subject'] = ... msg.attach(MIMEText('hello', 'plain', 'utf-8')) msg.attach(MIMEText('<html><body><h1>Hello</h1></body></html>', 'html', 'utf-8')) # 正常傳送msg物件...
六、加密SMTP
想要更安全地傳送郵件,防止傳送的郵件被竊聽,可以加密SMTP會話,實際上就是先建立SSL安全連線,然後再使用SMTP協議傳送郵件
只需要在建立SMTP
物件後,立刻呼叫starttls()
方法,就建立了安全連線
smtp_server = 'smtp.gmail.com' smtp_port = 587 server = smtplib.SMTP(smtp_server, smtp_port) server.starttls() # 剩下的程式碼和前面的一模一樣: server.set_debuglevel(1)