Python使用email模块对邮件进行编码和解码的实例教程

yipeiwu_com5年前Python基础

解码邮件
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. 生成最终邮件。

相关文章

Python的几种主动结束程序方式

Python的几种主动结束程序方式

对于如何结束一个Python程序或者用Python操作去结束一个进程等,Python本身给出了好几种方法,而这些方式也存在着一些区别,对相关的几种方法看了并实践了下,同时也记录下。 参考...

Python 数据结构之队列的实现

Python 队列 Queue 队列是一种先进先出(FIFO)的数据类型, 新的元素通过 入队 的方式添加进 Queue 的末尾, 出队 就是从 Queue 的头部删除元素. 用列表来做...

遗传算法python版

遗传算法python版

本文实例为大家分享了python遗传算法的具体代码,供大家参考,具体内容如下 1、基本概念 遗传算法(GA)是最早由美国Holland教授提出的一种基于自然界的“适者生存,优胜劣汰”基...

使用IDLE的Python shell窗口实例详解

使用IDLE的Python shell窗口实例详解

启动IDLE后会打开Python shell窗口。当键入代码 时,它会基于Python语法提供自动缩进和代码着色功能。 使用IDLE中的Python shell。代码在输入时会自动着...

详解django2中关于时间处理策略

详解django2中关于时间处理策略

一、django中数据模型关于时间字段的认识 1、 DateField :可以记录年月日,映射到数据库是 date 类型 2、 DateTimeField :可以记录年月日时分秒,映射到...