wxpython制作eml文件阅读器

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 处理eml文件,一般windows下会启用默认的outlook来阅读,实际上python的email模块可以很简单的实现对eml文件的阅读,闲来木事,利用wxpython制作了一个eml文件阅读器,实现了对eml文件正文的阅读和附件的阅读,但由于制作时,在信头读取部分使用了label,在格式处理时未处理好,当信件有多个收件人时,会出现格式混乱。

      处理eml文件,一般windows下会启用默认的outlook来阅读,实际上python的email模块可以很简单的实现对eml文件的阅读,闲来木事,利用wxpython制作了一个eml文件阅读器,实现了对eml文件正文的阅读和附件的阅读,但由于制作时,在信头读取部分使用了label,在格式处理时未处理好,当信件有多个收件人时,会出现格式混乱。另外,附件如果有多个,只会显示最后一个。哪位感兴趣或者有时间可以自己改一下。
    一、email模块对eml文件的读取
    首先先来看一个邮件的源文件:
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: user2 <YYYY@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--
    从第一行到第一个空行之间的为信件头,后面的是信件体。通过outlook解码后,正文显示为:
我已开始以为是一个星期换一次,原来是一个月调一次
我们现在应该是半点才能热
真麻烦

    下面,具体来看看email模块的使用。
(1)打开eml文件,利用open语句,具体与打开其它文件一样,如fileopen=open('AAA.eml','r')。
(2)email.message_from_file()创建message对象,此时会对fileopen内容进行初步解码。如msg = email.message_from_file(fileopen)
(3)获取信件主题subject = msg.get("subject"),此时主题中含有 =?gbk?B?u+nJtA==?=这样的编码,以下代码用来解码

h = email.Header.Header(subject)
dh = email.Header.decode_header(h)
subject = dh[0][0]

解码后,正常显示为“婚纱”

(4)解析发件人和收件人,发件人往往只有一个,所以可以直接用efrom=email.utils.parseaddr(msg.get("from"))[1]进行解析,收件人有时会有多个,可得用以下代码:

for tolines in msg.get("to").splitlines():
findst=tolines.find('<') #从to中找<位置
if findst==-1:#判断是否只有一个收件人,当tolines中不含有'<'时,只存在一个收件人
    eto=email.utils.parseaddr(msg.get("to"))[1]
else:
    eto=eto+tolines[findst:]+'\n' 

(5)解析时间,etime=msg.get("date"),显示时间格式为Thu, 12 Apr 2007 12:07:48 +0800 (CST),这里可以再根据需要转换为2007年4月12日星期四 12:07。

(6)解析正文和附件,代码如下:

for bodycheck in msg.walk():
if not bodycheck.is_multipart():
psname = bodycheck.get_param("name")

if psname:
psh = email.Header.Header(psname)
psdh = email.Header.decode_header(psh)
psfname = psdh[0][0]

data = bodycheck.get_payload(decode=True)

try:
f = open(psfname, 'wb')
except:
#
f = open('tempps', 'wb')

f.write(data)
f.close()
else:
data=bodycheck.get_payload(decode=True)
p=str(data)



  


这里,邮件正文保存为p,str类型,附件文件名为psfname,并将附件保存在当前文件+下面。如果有多个附件都会保存在这里。
(7)关闭文件fp.close()


    二、wxpython制作GUI
(1)self.staticText1、self.textCtrl1、self.button1三个控件完成对eml文件的选取。点击self.button1后,利用wxfiledialog调取文件选择框,限定wildcard = "note source (*.eml)|*.eml",由于路径有时会出现中文错误,所以使用了encode('utf-8')进行编码。

def OnButton1Button(self, event): #浏览
      dialog = wx.FileDialog(None, "Choose a file", os.getcwd(),"", wildcard, wx.OPEN)
      if dialog.ShowModal() == wx.ID_OK:
           aa=dialog.GetPath().encode('utf-8')
           self.textCtrl1.SetValue(aa.decode('utf-8'))
           self.reademail()
      dialog.Destroy()
      event.Skip()

  


(2)利用self.htmlWindow1显示邮件正文。邮件正文读取后一般都是html代码,通过htmlWindow1进行正常显示。
(3)实现邮件的另存为txt。邮件另存为txt时,遇到的主要问题是如何将html代码转换为带格式的txt文档,于是利用了正则和字符串的替换,替换为\n换行,然后又去掉了多余的换行,具体代码如下:

def savetotxt(self,filena):#邮件另存为txt文件
resultfile=''
read=file('temps').read()
readf_con=read.replace('&nbsp;','\n')
readf_con=readf_con.replace('&#187;','\n')
readf_con=re.sub("<!--.+?-->",'\n',readf_con)
readf_con=re.sub("<.*?>",'\n',readf_con)
readf_con=re.sub('\n+','\n',readf_con)
for tt in readf_con.splitlines():
tt=tt.rstrip()+'\n'
resultfile=resultfile+tt #去除temps文件中多余的空行

w=open(filena,'w')

w.write(resultfile)
w.close()



目前该代码存在的主要问题是:
1、收件人很多时,窗口格式会发生错乱
2、附件有多个时,只能显示一个
3、eml另存为时,附件只能保存最后一个
有兴趣的朋友可以自行修改一下。

 

完整代码如下:

#-*- encoding: gb2312 -*-
'''
eml阅读器 V1.0
小五义:http://www.cnblogs.com/xiaowuyi
仍然存在问题:
1、收件人很多时,窗口格式会发生错乱
2、附件有多个时,只能显示一个
3、eml另存为时,附件只能保存一个
'''


import wx
import wx.html
import os
import email
import re
import shutil

wildcard = "note source (*.eml)|*.eml"
psfname='' #附件名

def create(parent):
return Frame1(parent)

[wxID_FRAME1, wxID_FRAME1BUTTON1, wxID_FRAME1PANEL1, wxID_FRAME1STATICTEXT1, wxID_FRAME1STATICLINE1,
wxID_FRAME1STATICTEXT2, wxID_FRAME1STATICTEXT3,wxID_FRAME1TEXTCTRL1, wxID_FRAME1HTMLWINDOW1,wxID_FRAME1BUTTON2
] = [wx.NewId() for _init_ctrls in range(10)]

class Frame1(wx.Frame):
def _init_ctrls(self, prnt):

wx.Frame.__init__(self, id=wxID_FRAME1, name='', parent=prnt,
pos=wx.Point(269, 142), size=wx.Size(899, 599),
style=wx.DEFAULT_FRAME_STYLE, title='Eml文件阅读器 V1.0')
self.SetClientSize(wx.Size(891, 565))
self.Enable(True)
self.SetIcon(wx.Icon(u'mb_4.ico', wx.BITMAP_TYPE_ICO)) #当前目录下放一个名为mb_4.ico的文件做图标

self.panel1 = wx.Panel(id=wxID_FRAME1PANEL1, name='panel1', parent=self,
pos=wx.Point(0, 0), size=wx.Size(891, 565),
style=wx.TAB_TRAVERSAL)

self.textCtrl1 = wx.TextCtrl(id=wxID_FRAME1TEXTCTRL1, name='textCtrl1',
parent=self.panel1, pos=wx.Point(120, 16), size=wx.Size(632, 23),
style=0, value=u'')
self.textCtrl1.SetEditable(False)


self.button1 = wx.Button(id=wxID_FRAME1BUTTON1, label=u'浏览',
name='button1', parent=self.panel1, pos=wx.Point(776, 16),
size=wx.Size(75, 23), style=0)
self.button1.SetFont(wx.Font(11, wx.SWISS, wx.NORMAL, wx.BOLD, False,
u'Tahoma'))
self.button1.Bind(wx.EVT_BUTTON, self.OnButton1Button,
id=wxID_FRAME1BUTTON1)

self.staticText1 = wx.StaticText(id=wxID_FRAME1STATICTEXT1,
label=u'文件名:', name='staticText1', parent=self.panel1,
pos=wx.Point(45, 16), size=wx.Size(56, 23), style=0)
self.staticText1.SetFont(wx.Font(11, wx.SWISS, wx.NORMAL, wx.BOLD, False,
u'Tahoma'))

self.htmlWindow1 = wx.html.HtmlWindow(id=wxID_FRAME1HTMLWINDOW1,
name='htmlWindow1', parent=self.panel1, pos=wx.Point(72, 186),
size=wx.Size(747, 300), style=wx.html.HW_SCROLLBAR_AUTO|wx.DOUBLE_BORDER)

self.staticText2 = wx.StaticText(id=wxID_FRAME1STATICTEXT2,
label=u' 发件人:\n 日 期:\n 收件人:\n 主 题:\n', name='staticText2', parent=self.panel1,
pos=wx.Point(72, 80), size=wx.Size(747, 90), style=wx.html.HW_SCROLLBAR_AUTO)
self.staticText2.SetFont(wx.Font(11, wx.SWISS, wx.NORMAL, wx.BOLD, False,
u'Tahoma'))

self.staticText3 = wx.StaticText(id=wxID_FRAME1STATICTEXT3,
label=u' 附 件:', name='staticText3', parent=self.panel1,
pos=wx.Point(72, 496), size=wx.Size(680, 14), style=0)
self.staticText3.SetFont(wx.Font(11, wx.SWISS, wx.NORMAL, wx.BOLD, False,
u'Tahoma'))

self.staticLine1 = wx.StaticLine(id=wxID_FRAME1STATICLINE1,
name='staticLine1', parent=self.panel1, pos=wx.Point(0, 55),
size=wx.Size(891, 2), style=0)

self.button2 = wx.Button(id=wxID_FRAME1BUTTON2, label=u'导出',
name='button2', parent=self.panel1, pos=wx.Point(776, 496),
size=wx.Size(75, 23), style=0)
self.button2.SetFont(wx.Font(11, wx.SWISS, wx.NORMAL, wx.BOLD, False,
u'Tahoma'))
self.button2.Bind(wx.EVT_BUTTON, self.OnButton2Button,
id=wxID_FRAME1BUTTON2)


def __init__(self, parent):
self._init_ctrls(parent)



def reademail(self):#read email
global psfname
checkfile=True
p='' #记录邮件html正文
txtemail=''#记录邮件txt正文
eto=''

filename=self.textCtrl1.GetValue()
try:
emailfile=open(filename,'rb')
msg=email.message_from_file(emailfile)
subject=msg.get('subject')
head=email.Header.Header(subject)
dhead=email.Header.decode_header(head)
subject=dhead[0][0]
efrom=email.utils.parseaddr(msg.get("from"))[1]
etime=msg.get("date")
#判断收件人个数

print msg.get("to")
for tolines in msg.get("to").splitlines():
findst=tolines.find('<') #从to中找<位置
if findst==-1:
eto=email.utils.parseaddr(msg.get("to"))[1]
else:
eto=eto+tolines[findst:]+'\n'

#eto=email.utils.parseaddr(msg.get("to"))[1]
ehead=' 发件人:'+efrom+'\n'+' 日 期:'+etime+'\n'+' 收件人:'+eto+'\n'+' 主 题:'+subject+'\n'

for bodycheck in msg.walk():
if not bodycheck.is_multipart():
psname = bodycheck.get_param("name")

if psname:
psh = email.Header.Header(psname)
psdh = email.Header.decode_header(psh)
psfname = psdh[0][0]

data = bodycheck.get_payload(decode=True)

try:
f = open(psfname, 'wb')
except:
#
f = open('tempps', 'wb')

f.write(data)
f.close()
else:
data=bodycheck.get_payload(decode=True)
p=str(data)

emailend=ehead
self.staticText2.SetLabel(emailend)
self.staticText3.SetLabel(' 附 件:'+psfname)

self.htmlWindow1.SetPage(p)
txtemail=emailend+'正文:'+p
checkfile=False
except:

tishi='文件'+'格式错误!'
wx.MessageBox(tishi,'注意',wx.OK)

tem=open('temps','w') # 临时文件存放


lamp='<DIV>'+txtemail+'</DIV><DIV>'+' 附 件:'+psfname
lamp=lamp.replace('\n','</DIV><DIV>')
tem.write(lamp)

tem.close()

def OnButton1Button(self, event): #浏览
dialog = wx.FileDialog(None, "Choose a file", os.getcwd(),"", wildcard, wx.OPEN)
if dialog.ShowModal() == wx.ID_OK:
aa=dialog.GetPath().encode('utf-8')
self.textCtrl1.SetValue(aa.decode('utf-8'))
self.reademail()
dialog.Destroy()
event.Skip()

def savetotxt(self,filena):#邮件另存为txt文件
resultfile=''
read=file('temps').read()
readf_con=read.replace('&nbsp;','\n')
readf_con=readf_con.replace('&#187;','\n')
readf_con=re.sub("<!--.+?-->",'\n',readf_con)
readf_con=re.sub("<.*?>",'\n',readf_con)
readf_con=re.sub('\n+','\n',readf_con)
for tt in readf_con.splitlines():
tt=tt.rstrip()+'\n'
resultfile=resultfile+tt #去除temps文件中多余的空行

w=open(filena,'w')

w.write(resultfile)
w.close()


def OnButton2Button(self, event):#eml另存为
wid = "text file (*.txt)|*.txt"
savedialog = wx.FileDialog(None, "Choose a file", os.getcwd(),"", wid, wx.SAVE)
if savedialog.ShowModal() == wx.ID_OK:
fil=savedialog.GetPath().encode('utf-8')
savef=fil.decode('utf-8')
savedialog.Destroy()
savep=os.path.dirname(savef)

#将附件另存到该目录下
checkdot=psfname.find('.')
if checkdot!= -1:#判断是否存在附件
kzhname='ps'+psfname[checkdot:]
psfile=os.path.join(savep,kzhname)
shutil.copy(psfname, psfile)
self.savetotxt(savef)#存eml正文

event.Skip()




class App(wx.App):
def OnInit(self):
self.main=create(None)
self.main.Show()
self.SetTopWindow(self.main)
return True
def main():
application=App(0)
application.MainLoop()

if __name__=='__main__':
main()



目录
相关文章
|
7月前
|
存储 数据处理 开发工具
用python制作好的软件放手机里
【4月更文挑战第7天】
124 4
|
数据可视化 Android开发
XMind 2021 v11.1.2破解版使用方法
XMind 2021 v11.1.2破解版使用方法
245 0
|
4月前
|
Python
Python模块制作及安装
Python模块制作及安装
|
Web App开发 JavaScript 算法
快速制作一个chrome插件
在我日常开发以及娱乐生活中,浏览器是我使用频率较高的一个应用,当我大学拥有第一部电脑开始,之后不论电脑换成什么,以及使用的是什么系统,我的首选浏览器都是Chrome,不仅仅是因为其速度快,更多是它丰富的扩展在吸引我,那么大家有没有想过如何自己来开发一个Chrome浏览器插件呢?是不是有的同学会觉得Chrome浏览器插件的制作难度会很大呢?今天就让我来带你们看看一个简单的Chrome浏览器插件的编写过程,并给大家制作一个简单的插件模板,大家可以通过模板来进行快速开发。
353 0
快速制作一个chrome插件
|
Web App开发 数据采集 JavaScript
用 pyppeteer 制作 PDF文件
之前介绍过一些将html转换为PDF文件的库,比如 wkhtmltopdf、WeasyPrint,今天再介绍另一个神器Pyppeteer可将html页面转换为PDF。
204 0
Dreamweaver如何导入psd文件?
Dreamweaver如何导入psd文件?
306 0
|
Ubuntu 安全 数据可视化
干货|教你使用Doxygen制作出漂亮程序文档
不知道大家有没有把自己的代码整理成文档的习惯,有没有给自己的代码一个非常漂亮的注释,就像下图这样。
2009 0
干货|教你使用Doxygen制作出漂亮程序文档