處理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()