首先了解SMTP(簡(jiǎn)單郵件傳輸協(xié)議),郵件傳送代理程序使用SMTP協(xié)議來(lái)發(fā)送電郵到接收者的郵件服務(wù)器。SMTP協(xié)議只能用來(lái)發(fā)送郵件,不能用來(lái)接收郵件,而大多數(shù)的郵件發(fā)送服務(wù)器都是使用SMTP協(xié)議。SMTP協(xié)議的默認(rèn)TCP端口號(hào)是25。
創(chuàng)新互聯(lián)技術(shù)團(tuán)隊(duì)十多年來(lái)致力于為客戶提供成都網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)、成都品牌網(wǎng)站建設(shè)、網(wǎng)絡(luò)營(yíng)銷推廣、搜索引擎SEO優(yōu)化等服務(wù)。經(jīng)過多年發(fā)展,公司擁有經(jīng)驗(yàn)豐富的技術(shù)團(tuán)隊(duì),先后服務(wù)、推廣了上千家網(wǎng)站,包括各類中小企業(yè)、企事單位、高校等機(jī)構(gòu)單位。
本文主要介紹利用'smtplib','email'兩個(gè)模塊來(lái)實(shí)現(xiàn)郵件的發(fā)送,可以如下查看兩個(gè)模塊的函數(shù)和方法:
smtplib模塊簡(jiǎn)介:
smtplib.SMTP([host[, port[, local_hostname[, timeout]]]])
此為SMTP類構(gòu)造函數(shù),表示與SMTP服務(wù)器之間的連接,并根據(jù)這個(gè)連接向smtp服務(wù)器發(fā)送指令,執(zhí)行相關(guān)操作(如:登陸、發(fā)送郵件),且每個(gè)參數(shù)都是可選的。
其中最重要的參數(shù):
host:smtp服務(wù)器主機(jī)名
port:smtp服務(wù)的端口,默認(rèn)是25;
如果在創(chuàng)建SMTP對(duì)象的時(shí)候提供了這兩個(gè)參數(shù),在初始化的時(shí)候會(huì)自動(dòng)調(diào)用connect方法去連接服務(wù)器。
smtplib.SMTP還提供了如下方法:
SMTP.set_debuglevel(level):設(shè)置是否為調(diào)試模式
SMTP.connect([host[, port]]):連接到指定的smtp服務(wù)器。參數(shù)分別表示 smpt主機(jī)和端口。
SMTP.docmd(cmd[, argstring]):向smtp服務(wù)器發(fā)送指令。
SMTP.helo([hostname]) :使用"helo"指令向服務(wù)器確認(rèn)身份。
SMTP.login(user, password):登陸到smtp服務(wù)器?,F(xiàn)在幾乎所有smtp服務(wù)器,都必須在驗(yàn)證用戶信息合法之后才允許發(fā)送郵件。(重要?。?/p>
SMTP.sendmail(from_addr,to_addrs,msg[,mail_options,rcpt_options]):發(fā)送郵件。這里要注意一下第三個(gè)參數(shù),msg是字符串,表示郵件。我們知道郵件一般由標(biāo)題,發(fā)信人,收件人,郵件內(nèi)容,附件等構(gòu)成,發(fā)送郵件的時(shí)候,要注意msg的格式。這個(gè)格式就是smtp協(xié)議中定義的格式。SMTP.quit() :斷開與smtp服務(wù)器的連接,相當(dāng)于發(fā)送"quit"指令。(重要!)
常用的函數(shù)方法:
email模塊
1.class email.message.Message
__getitem__,__setitem__實(shí)現(xiàn)obj[key]形式的訪問。
Msg.attach(playload): 向當(dāng)前Msg添加playload。
Msg.set_playload(playload):
Msg.add_header(_name, _value, **_params): 添加郵件頭字段。
2.class email.mime.base.MIMEBase(_maintype, _subtype, **_params)
所有MIME類的基類,是email.message.Message類的子類。
3.class email.mime.multipart.MIMEMultipart()
在3.0版本的email模塊 (Python 2.3-Python 2.5) 中,這個(gè)類位于email.MIMEMultipart.MIMEMult ipart。這個(gè)類是MIMEBase的直接子類,用來(lái)生成包含多個(gè)部分的郵件體的MIME對(duì)象。
4.class email.mime.text.MIMEText(_text)
使用字符串_text來(lái)生成MIME對(duì)象的主體文本。
獲得所需要使用的郵箱的host地址和port端口號(hào),(本文使用的是163郵箱,對(duì)應(yīng)的smtp服務(wù)器地址:mail.163.com,端口號(hào)25)
常用郵箱的smtp服務(wù)器地址和端口號(hào)如圖:
編寫程序如下:
#! /usr/bin/env python
import smtpli
from email.mime.text import MIMEText
mailto_list=['xxxx@xxx.com'] #收件人(列表)
mail_host="smtp.163.com" #使用的郵箱的smtp服務(wù)器地址
mail_user="name" #用戶名
mail_pass="pwd" #密碼
mail_postfix="postfix" #郵箱的后綴
def send_mail(to_list,sub,content):
me="hello"+""+mail_user+"@"+mail_postfix+""
msg = MIMEText(content,_subtype='plain')
msg['Subject'] = sub
msg['From'] = me
msg['To'] = ";".join(to_list) #將收件人列表以‘;’分隔
try:
server = smtplib.SMTP()
server.connect(mail_host) #連接服務(wù)器
server.login(mail_user,mail_pass) #登錄操作
server.sendmail(me, to_list, msg.as_string())
server.close()
return True
except Exception, e:
print str(e)
return False
for i in range(5): #發(fā)送五封,不過會(huì)被攔截的。。。
if send_mail(mailto_list,"hello","haha!"): #郵件主題和郵件內(nèi)容
print "done!"
else:
print "failed!"
最后,可以運(yùn)行編寫的py文件,可以得到如圖所是的結(jié)果,代表郵件發(fā)送成功。
這樣,就能成功實(shí)現(xiàn)用Python發(fā)送郵件啦!
'''''?
函數(shù)說明:Send_email_text()?函數(shù)實(shí)現(xiàn)發(fā)送帶有附件的郵件,可以群發(fā),附件格式包括:xlsx,pdf,txt,jpg,mp3等?
參數(shù)說明:?
1.?subject:郵件主題?
2.?content:郵件正文?
3.?filepath:附件的地址,?輸入格式為["","",...]?
4.?receive_email:收件人地址,?輸入格式為["","",...]?
'''??
def?Send_email_text(subject,content,filepath,receive_email):??
import?smtplib??
from?email.mime.multipart?import?MIMEMultipart???
from?email.mime.text?import?MIMEText???
from?email.mime.application?import?MIMEApplication??
sender?=?"發(fā)送方郵箱"??
passwd?=?"填入發(fā)送方密碼"??
receivers?=?receive_email???#收件人郵箱??
msgRoot?=?MIMEMultipart()???
msgRoot['Subject']?=?subject??
msgRoot['From']?=?sender??
if?len(receivers)1:??
msgRoot['To']?=?','.join(receivers)?#群發(fā)郵件??
else:??
msgRoot['To']?=?receivers[0]??
part?=?MIMEText(content)???
msgRoot.attach(part)??
##添加附件部分??
for?path?in?filepath:??
if?".jpg"?in?path:??
#jpg類型附件??
jpg_name?=?path.split("\\")[-1]??
part?=?MIMEApplication(open(path,'rb').read())???
part.add_header('Content-Disposition',?'attachment',?filename=jpg_name)??
msgRoot.attach(part)??
if?".pdf"?in?path:??
#pdf類型附件??
pdf_name?=?path.split("\\")[-1]??
part?=?MIMEApplication(open(path,'rb').read())???
part.add_header('Content-Disposition',?'attachment',?filename=pdf_name)???
msgRoot.attach(part)??
if?".xlsx"?in?path:??
#xlsx類型附件??
xlsx_name?=?path.split("\\")[-1]??
part?=?MIMEApplication(open(path,'rb').read())???
part.add_header('Content-Disposition',?'attachment',?filename=xlsx_name)??
msgRoot.attach(part)??
if?".txt"?in?path:??
#txt類型附件??
txt_name?=?path.split("\\")[-1]??
part?=?MIMEApplication(open(path,'rb').read())??
part.add_header('Content-Disposition',?'attachment',?filename=txt_name)??
msgRoot.attach(part)??
if?".mp3"?in?path:??
#mp3類型附件??
mp3_name?=?path.split("\\")[-1]??
part?=?MIMEApplication(open(path,'rb').read())???
part.add_header('Content-Disposition',?'attachment',?filename=mp3_name)???
msgRoot.attach(part)??
try:??
s?=?smtplib.SMTP()??
s.connect("smtp.mail.aliyun.com")?#這里我使用的是阿里云郵箱,也可以使用163郵箱:smtp.163.com??
s.login(sender,?passwd)??
s.sendmail(sender,?receivers,?msgRoot.as_string())??
print?("郵件發(fā)送成功")??
except?smtplib.SMTPException?as?e:??
print("Error,?發(fā)送失敗")??
finally:??
s.quit()
由上篇文章我們已經(jīng)得知郵件從發(fā)送到接收的過程:
發(fā)件人-MUA-MTA-若干MTA-MDA-MUA-收件人
本節(jié)接收郵件主要就是編寫一個(gè) MUA 客戶端,從 MDA 將郵件取回本地。
收取郵件最常用的是 POP協(xié)議 ,目前版本是第三版,也稱 POP3 。python內(nèi)置了 poplib 模塊,支持POP3協(xié)議。
回想上一節(jié) SMTP ,我們對(duì)要發(fā)送的郵件內(nèi)容進(jìn)行了各種編碼,包括添加MIME header,編碼之后再進(jìn)行發(fā)送。
因此,我們通過POP3協(xié)議接收的也不是原內(nèi)容,而是經(jīng)過一系列編碼等處理的文本。
所以,要想把POP3收取的文本變?yōu)榭砷喿x的郵件對(duì)象,就需要利用 email 模塊對(duì)原始郵件進(jìn)行解析。
所以,郵件收取的流程就是:
由上一篇 文章 最后總結(jié)部分可知。郵件由字符到發(fā)送到網(wǎng)絡(luò)經(jīng)歷了如下的格式轉(zhuǎn)化:
純文本:
str-bytes-base64-str-bytes
二進(jìn)制文件:
binary code-base64-str-bytes
我們解析郵件也是按這個(gè)思路,逆序解析出內(nèi)容。
這里的 decode('utf-8') 先把字節(jié)流轉(zhuǎn)化為字符串,再將字符串轉(zhuǎn)化為 message 結(jié)構(gòu)的對(duì)象。這步與發(fā)送郵件的 as_string 函數(shù)相反。
先從上一節(jié)結(jié)構(gòu)化的 msg 中取出信件頭,打印出來(lái)。
如果是 multipart 結(jié)構(gòu), get_payload 函數(shù)會(huì)返回一個(gè)包含不同part的list,然后對(duì)每一part遞歸調(diào)用 print_info ,打印子信件頭和子信件內(nèi)容。
不是 multipart 時(shí),之后再依據(jù) Content-Type 作不同處理:
如果是 text :
利用 get_payload(decode = Ture) 取出子信件的內(nèi)容, decode 為True,則按照 Content-Transfer-Type 將 base64 或 QP 解碼為 bytes 。
再 guess_charset 猜出編碼方式,之后將其解碼為字符顯示。
如果不是 Text 對(duì)象,則為附件:
打印出附件的 Content-Type 。