Python使用email模块对邮件进行编码和解码的实例教程
解码邮件
python自带的email模块是个很有意思的东西,它可以对邮件编码解码,用来处理邮件非常好用。
处理邮件是一个很细致的工作,尤其是解码邮件,因为它的格式变化太多了,下面先看看一个邮件的源文件:
Received:from192.168.208.56(192.168.208.56[192.168.208.56])by ajax-webmail-wmsvr37(Coremail);Thu,12Apr200712:07:48+0800(CST) Date:Thu,12Apr200712:07:48+0800(CST) From:user1<xxxxxxxx@163.com> To:zhaowei<zhaoweikid@163.com> Message-ID:<31571419.200911176350868321.JavaMail.root@bj163app37.163.com> Subject:=?gbk?B?u+nJtA==?= MIME-Version:1.0 Content-Type:multipart/Alternative; boundary="----=_Part_21696_28113972.1176350868319" ------=_Part_21696_28113972.1176350868319 Content-Type:text/plain;charset=gbk Content-Transfer-Encoding:base64 ztLS0b+qyrzS1M6qysfSu7j20MfG2ru70ru0zqOs1K3AtMrH0ru49tTCtffSu7TOztLDx8/W1NrT prjDysew67XjssXE3MjI1ebC6bezICAg ------=_Part_21696_28113972.1176350868319 Content-Type:text/html;charset=gbk Content-Transfer-Encoding:quoted-printable <DIV>=CE=D2=D2=D1=BF=AA=CA=BC=D2=D4=CE=AA=CA=C7=D2=BB=B8=F6=D0=C7=C6=DA=BB= =BB=D2=BB=B4=CE=A3=AC=D4=AD=C0=B4=CA=C7=D2=BB=B8=F6=D4=C2=B5=F7=D2=BB=B4=CE= </DIV> <DIV>=CE=D2=C3=C7=CF=D6=D4=DA=D3=A6=B8=C3=CA=C7=B0=EB=B5=E3=B2=C5=C4=DC=C8= =C8</DIV> <DIV>=D5=E6=C2=E9=B7=B3</DIV> ------=_Part_21696_28113972.1176350868319--
上面的就是以封邮件的源文件,从第一行到第一个空行之间的为信件头,后面的就是信件体了。把上面的信息复制下来存到一个叫xxx.eml的文件里,用鼠标双击就可以看到内容,当然看到的是解码后的,是outlook帮你解码了。
看看email模块怎么处理这个邮件,假设信件已经存为xxx.eml。
#-*-encoding:gb2312-*- importemail fp=open("xxx.eml","r") msg=email.message_from_file(fp)#直接文件创建message对象,这个时候也会做初步的解码 subject=msg.get("subject")#取信件头里的subject,也就是主题 #下面的三行代码只是为了解码象=?gbk?Q?=CF=E0=C6=AC?=这样的subject h=email.Header.Header(subject) dh=email.Header.decode_header(h) subject=dh[0][0] print"subject:",subject print"from:",email.utils.parseaddr(msg.get("from"))[1]#取from print"to:",email.utils.parseaddr(msg.get("to"))[1]#取to fp.close()
这段代码可以把一封邮件中的主题、发件人、收件人解析出来。email.utils.parseaddr是用来专门解析邮件地址的,原因是邮件地址很多时候在原文里是这样写的:user1<xxxxxxxx@163.com>,email.utils.parseaddr就可以把它解析为一个列表,第一项是user1,第二项是xxxxxxxx@163.com,这里只显示了后面以部分。
前面那段代码只是解析了信件头,接着解析信件体吧。信体里可能有纯文本的plain和html两部分,也可能有附件。这里需要mime的知识,详细介绍可以从网上搜搜。我这里就不说了,下面看看怎么解析的:
#-*-encoding:gb2312-*- importemail fp=open("xxx.eml","r") msg=email.message_from_file(fp) #循环信件中的每一个mime的数据块 forparinmsg.walk(): ifnotpar.is_multipart():#这里要判断是否是multipart,是的话,里面的数据是无用的,至于为什么可以了解mime相关知识。 name=par.get_param("name")#如果是附件,这里就会取出附件的文件名 ifname: #有附件 #下面的三行代码只是为了解码象=?gbk?Q?=CF=E0=C6=AC.rar?=这样的文件名 h=email.Header.Header(name) dh=email.Header.decode_header(h) fname=dh[0][0] print'附件名:',fname data=par.get_payload(decode=True)#解码出附件数据,然后存储到文件中 try: f=open(fname,'wb')#注意一定要用wb来打开文件,因为附件一般都是二进制文件 except: print'附件名有非法字符,自动换一个' f=open('aaaa','wb') f.write(data) f.close() else: #不是附件,是文本内容 printpar.get_payload(decode=True)#解码出文本内容,直接输出来就可以了。 print'+'*60#用来区别各个部分的输出
简单吧,并没有多少代码就可以实现复杂的解析邮件的功能!
编码邮件
用email模块来生成邮件也是很简单的,只是需要一些mime的基础知识。下面看看一点mime基础。
MIME消息由消息头和消息体两大部分组成,在邮件里就是邮件头和邮件体。邮件头与邮件体之间以空行进行分隔。这点可以用文本编辑器(比如记事本)查看一个邮件的源文件就可以清除看到。outlook和foxmail自己就有查看源文件的功能。
邮件头包含了发件人、收件人、主题、时间、MIME版本、邮件内容的类型等重要信息。每条信息称为一个域,由域名后加“:”和信息内容构成,可以是一行,较长的也可以占用多行。域的首行必须“顶头”写,即左边不能有空白字符(空格和制表符);续行则必须以空白字符打头,且第一个空白字符不是信息本身固有的。
邮件体包含邮件的内容,它的类型由邮件头的“Content-Type”域指出。最常见的类型有text/plain(纯文本)和text/html(超文本)。邮件体被分为多个段,每个段又包含段头和段体两部分,这两部分之间也以空行分隔。常见的multipart类型有三种:multipart/mixed,multipart/related和multipart/alternative。从它们的名称,不难推知这些类型各自的含义和用处。
如果在邮件中要添加附件,必须定义multipart/mixed段;如果存在内嵌资源,至少要定义multipart/related段;如果纯文本与超文本共存,至少要定义multipart/alternative段。生成邮件就是要生成这各个MIME部分。email模块对这些处理都是包装好的,看看生成方法:
#-*-encoding:gb2312-*- importemail importstring,sys,os,email importtime classMailCreator: def__init__(self): #创建邮件的message对象 self.msg=email.Message.Message() self.mail="" defcreate(self,mailheader,maildata,mailattachlist=[]): #mailheader是dict类型,maildata是list,且里面第一项为纯文本类型,第二项为html. #mailattachlist是list,里面为附件文件名 ifnotmailheaderornotmaildata: return forkinmailheader.keys(): #对subject要作特殊处理,中文要转换一下。 #比如"我的一个测试邮件"就要转换为=?gb2312?b?ztK1xNK7uPay4srU08q8/g==?= ifk=='subject': self.msg[k]=email.Header.Header(mailheader[k],'gb2312') else: self.msg[k]=mailheader[k] #创建纯文本部分 body_plain=email.MIMEText.MIMEText(maildata[0],_subtype='plain',_charset='gb2312') body_html=None #创建html部分,这个是可选的 ifmaildata[1]: body_html=email.MIMEText.MIMEText(maildata[1],_subtype='html',_charset='gb2312') #创建一个multipart,然后把前面的文本部分和html部分都附加到上面,至于为什么,可以看看mime相关内容 attach=email.MIMEMultipart.MIMEMultipart() attach.attach(body_plain) ifbody_html: attach.attach(body_html) #处理每一个附件 forfnameinmailattachlist: attachment=email.MIMEText.MIMEText(email.Encoders._bencode(open(fname,'rb').read())) #这里设置文件类型,全部都设置为Application.当然也可以是Image,Audio什么的,这里不管那么多 attachment.replace_header('Content-type','Application/octet-stream;name="'+os.path.basename(fname)+'"') #一定要把传输编码设置为base64,因为这里默认就是用的base64 attachment.replace_header('Content-Transfer-Encoding','base64') attachment.add_header('Content-Disposition','attachment;filename="'+os.path.basename(fname)+'"') attach.attach(attachment) #生成最终的邮件 self.mail=self.msg.as_string()[:-1]+attach.as_string() returnself.mail if__name__=='__main__': mc=MailCreator() header={'from':'zhaowei@163.com','to':'weizhao@163.com','subject':'我的一个测试邮件'} data=['plaintextinformation','<fontcolor="red">htmltextinformation</font>'] ifsys.platform=='win32': attach=['c:/windows/clock.avi'] else: attach=['/bin/cp'] mail=mc.create(header,data,attach) f=open("test.eml","wb") f.write(mail) f.close()
这里我自己封装了一个类来做处理,大体的过程就是:
1.先创建message对象:email.Message.Message()
2.创建MIMEMultipart对象:email.MIMEMultipart.MIMEMultipart()
3.创建各个MIMEText对象,并把他们attach到MIMEMultipart里,这里的MIMEText其实不仅仅是text,也包括image,application,audio等等。
4.生成最终邮件。