处理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(' ','\n')
readf_con=readf_con.replace('»','\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(' ','\n')
readf_con=readf_con.replace('»','\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()