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


Posted in Python onJuly 01, 2016

解码邮件
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中的with语句与上下文管理器学习总结
Jun 28 Python
对python pandas 画移动平均线的方法详解
Nov 28 Python
python小程序实现刷票功能详解
Jul 17 Python
Django CSRF跨站请求伪造防护过程解析
Jul 31 Python
Python绘制三角函数图(sin\cos\tan)并标注特定范围的例子
Dec 04 Python
Django 再谈一谈json序列化
Mar 16 Python
基于Python共轭梯度法与最速下降法之间的对比
Apr 02 Python
Django中文件上传和文件访问微项目的方法
Apr 27 Python
PythonPC客户端自动化实现原理(pywinauto)
May 28 Python
简单了解Django项目应用创建过程
Jul 06 Python
编写python代码实现简单抽奖器
Oct 20 Python
Python实现老照片修复之上色小技巧
Oct 16 Python
详解Python中使用base64模块来处理base64编码的方法
Jul 01 #Python
Python画图学习入门教程
Jul 01 #Python
Python中json格式数据的编码与解码方法详解
Jul 01 #Python
python判断字符串编码的简单实现方法(使用chardet)
Jul 01 #Python
Python字符编码判断方法分析
Jul 01 #Python
Android模拟器无法启动,报错:Cannot set up guest memory ‘android_arm’ Invalid argument的解决方法
Jul 01 #Python
python在Windows下安装setuptools(easy_install工具)步骤详解
Jul 01 #Python
You might like
PHP冒泡算法详解(递归实现)
2014/11/10 PHP
php中switch语句用法详解
2015/08/17 PHP
PHP模板引擎Smarty之配置文件在模板变量中的使用方法示例
2016/04/11 PHP
Yii 2.0自带的验证码使用经验分享
2017/06/19 PHP
PHP编程实现计算抽奖概率算法完整实例
2017/08/09 PHP
JS 自动安装exe程序
2008/11/30 Javascript
javascript 关于# 和 void的区别分析
2009/10/26 Javascript
extjs中grid中嵌入动态combobox的应用
2011/01/01 Javascript
JavaScript控制Session操作方法
2013/01/17 Javascript
jquery 实现input输入什么div图层显示什么
2014/06/15 Javascript
JavaScript闭包函数访问外部变量的方法
2014/08/27 Javascript
js判断一个字符串是否包含一个子串的方法
2015/01/26 Javascript
jQuery mobile类库使用时加载导航历史的方法简介
2015/12/04 Javascript
javascript中apply、call和bind的使用区别
2016/04/05 Javascript
JS使用eval()动态创建变量的方法
2016/06/03 Javascript
JS中的hasOwnProperty()和isPrototypeOf()属性实例详解
2016/08/11 Javascript
webpack学习教程之publicPath路径问题详解
2017/06/17 Javascript
CSS3结合jQuery实现动画效果及回调函数的实例
2017/12/27 jQuery
基于layui轮播图满屏是高度自适应的解决方法
2019/09/16 Javascript
Python语言编写电脑时间自动同步小工具
2013/03/08 Python
python用opencv批量截取图像指定区域的方法
2019/01/24 Python
pycharm new project变成灰色的解决方法
2019/06/27 Python
浅谈pytorch池化maxpool2D注意事项
2020/02/18 Python
Python中的__init__作用是什么
2020/06/09 Python
Elasticsearch py客户端库安装及使用方法解析
2020/09/14 Python
CSS3不透明度实例讲解
2016/04/26 HTML / CSS
详解Canvas 实现炫丽的粒子运动效果(粒子生成文字)
2018/02/01 HTML / CSS
HTML5新特性之用SVG绘制微信logo
2016/02/03 HTML / CSS
巴西最大的在线约会网站:ParPerfeito
2018/07/11 全球购物
机电专业个人求职信范文
2013/12/30 职场文书
绩效考核实施方案
2014/03/18 职场文书
《记金华的双龙洞》教学反思
2014/04/19 职场文书
2014公安机关纪律作风整顿思想汇报
2014/09/13 职场文书
金陵十三钗观后感
2015/06/04 职场文书
PHP RabbitMQ消息列队
2022/05/11 PHP
MySQL数据库实验之 触发器和存储过程
2022/06/21 MySQL