python自带的email模块是个很有意思的东西,它可以对邮件编码解码,用来处理邮件非常好用。
处理邮件是一个很细致的工作,尤其是解码邮件,因为它的格式变化太多了,下面先看看一个邮件的源文件:

Received: from 192.168.208.56 ( 192.168.208.56 [192.168.208.56] ) by
  ajax-webmail-wmsvr37 (Coremail) ; Thu, 12 Apr 2007 12:07:48 +0800 (CST)
 Date: Thu, 12 Apr 2007 12: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 -*- 
 
 
 import 
  email

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 -*- 
 
  import 
  email

fp   = 
  open( 
 " 
 xxx.eml 
 " 
 ,  
 " 
 r 
 " 
 )
msg   = 
  email.message_from_file(fp)

  # 
  循环信件中的每一个mime的数据块 
 
  for 
  par  
 in 
  msg.walk():
      if 
   
 not 
  par.is_multipart():  
 # 
  这里要判断是否是multipart,是的话,里面的数据是无用的,至于为什么可以了解mime相关知识。 
 
          name  
 = 
  par.get_param( 
 " 
 name 
 " 
 )  
 # 
 如果是附件,这里就会取出附件的文件名 
 
           
 if 
  name:
              # 
 有附件 
 
               
 # 
  下面的三行代码只是为了解码象=?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 
 :
              # 
 不是附件,是文本内容 
 
               
 print 
  par.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 -*-   
   import    email
   import    string, sys, os, email
   import    time

   class    MailCreator:
        def       __init__ 
  (self):
            #    创建邮件的message对象   
           self.msg    =    email.Message.Message()
         self.mail    =       "" 
      
         
        def    create(self, mailheader, maildata, mailattachlist   = 
  []):
            #    mailheader 是dict类型,maildata是list, 且里面第一项为纯文本类型,第二项为html.   
              #    mailattachlist 是list, 里面为附件文件名 
  
              if     
  not 
   mailheader  
  or 
    
  not 
   maildata:
                return   
         
            for    k    in 
   mailheader.keys():
                #    对subject要作特殊处理,中文要转换一下。   
                  #    比如 "我的一个测试邮件" 就要转换为 =?gb2312?b?ztK1xNK7uPay4srU08q8/g==?= 
  
                  if    k  
  == 
    
  ' 
  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部分,这个是可选的   
              if    maildata[ 
  1 
  ]:
             body_html    =    email.MIMEText.MIMEText(maildata[   1 
  ], _subtype 
  = 
  ' 
  html 
  ' 
  , _charset 
  = 
  ' 
  gb2312 
  ' 
  )
         
         
            #    创建一个multipart, 然后把前面的文本部分和html部分都附加到上面,至于为什么,可以看看mime相关内容   
           attach   =   email.MIMEMultipart.MIMEMultipart()
         attach.attach(body_plain)
            if    body_html:
             attach.attach(body_html)
            #    处理每一个附件   
              for    fname  
  in 
   mailattachlist:
             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()
         
            return    self.mail

   if       __name__ 
    
  == 
    
  ' 
  __main__ 
  ' 
  :
     mc    =    MailCreator()
     header    =    {   ' 
  from 
  ' 
  :  
  ' 
  zhaowei@163.com 
  ' 
  ,  
  ' 
  to 
  ' 
  : 
  ' 
  weizhao@163.com 
  ' 
  ,  
  ' 
  subject 
  ' 
  : 
  ' 
  我的一个测试邮件 
  ' 
  }
     data    =    [   ' 
  plain text information 
  ' 
  ,  
  ' 
  <font color="red">html text information</font> 
  ' 
  ]
        if    sys.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. 生成最终邮件。