用Python处理邮件,全文完

[原创] 用Python处理邮件,全文完.


http://www.chinaunix.net 作者:jasonnbfan  发表于:2006-08-29 16:27:48
 

总体来说python处理邮件还是比较方便的,库提供了很多工具.下面我把心得写出来,给新手一个启迪,也请高手给些更好的方法.
先说接受邮件.  poplib 方法.
1.poplib.POP3('这里填入你pop邮件服务器地址') 登陆服务器.
2.poplib.user('用户名 ') poplib.pass_('密码')
3.poplib.stat()方法返回一个元组:(邮件数,邮件尺寸)
   mailCount,size=poplib.stat()
   这样mailCount就是邮件的数量,size,就是所有邮件的大小.

3.poplib.rert('邮件号码')方法返回一个元组:(状态信息,邮件,邮件尺寸) 
  hdr,message,octet=server.retr(1) 读去第一个邮件信息.
   hdr的内容就是响应信息和邮件大小比如'+OK 12498 octets'
   message 是包含邮件所有行的列表.
   octet 是这个邮件的内容.

 得到的message是邮件的原始内容,也就是没有解码过的,里面的内容和标题基本上都是base64编码的,下面说说如何处理原始邮件.
python 邮件处理,盲人邮件收发器 的email库里提供了很多处理邮件的方法,我们先把原始邮件转成email实例,这样就可以用库方法处理邮件.
email.message_from_string() 这个方法能把String的邮件转换成email.message实例.
比如我们上面的message,向下面这样调用.
mail=email.message_from_string(string.join(message,'/n'))
这样我们就生成了一个email.Message实例

现在我们来提取邮件内容,和标题,mail支持字典操作.比如下面的操作.
mail['subject'] ,mail.get('subject')
mail['To'],mail.get('to')'
mail.keys() ,mail.items() 等等.

中文邮件的标题和内容都是base64编码的.解码可以使用email.Header 里的decode_header()方法.
比如 print mail['subject']   显示的都未处理的编码.
'=?GB2312?B?UmU6IFtweXRob24tY2hpbmVzZV0g?=/n/t=?GB2312?B?y63E3LDvztLV0tbQzsS1xFBZVEhPTrP10afRp8+wtcTXysHP?='

email.Header.decode_header(mail['subject']) 下面是解码后的信息.
[('Re: [python-chinese] /xcb/xad/xc4/xdc/xb0/xef/xce/xd2/xd5/xd2/xd6/xd0/xce/xc4/xb5/xc4PYTHON/xb3/xf5/xd1/xa7/xd1/xa7/xcf/xb0/xb5/xc4/xd7/xca/xc1/xcf', 'gb2312')]
返回的是一个列表,里面的内容保存在一个元组里,(解码后的字串,字符编码)

显示解码后的标题就象下面这样
print email.Header.decode_header(mail['subject'])[0][0]
Re: [python-chinese] 谁能帮我找中文的PYTHON初学学习的资料

上面的mail标题编码是'gb2312'的,在我的winxp机器上可以直接显示,如果编码是别的比如'utf-8'编码,那么显示出来的就是乱码了.所以我们需要使用unicode()方法,unicode('这里是string','这里是编码,比如UTF-8'),比如
subject=email.Header.decode_header(mail['subject'])[0][0]
subcode=email.Header.decode_header(mail['subject'])[0][1])

print unicode(subject,subcode)
Re: [python-chinese] 谁能帮我找中文的PYTHON初学学习的资料

下面看如何处理邮件内容.
mail里有很多方法,熟悉这些方法处理邮件就很容易了。
get_payload() 这个方法可以把邮件的内容解码并且显示出来.第一个可选择参数是mail实例,第二个参数是decode='编码' ,一般都是,'base64'编码
is_multipart(),这个方法返回boolean值,如果实例包括多段,就返回True,
print mail.is_multipart()
true  ,这说明这个mail邮件包含多个字段。我下面的函数就可以处理,显示邮件的全部内容。

def  showmessage(mail):
    
if  mail.is_multipart():
        
for  part  in  mail.get_payload():
            showmessage(part)
    
else :
        type
= mail.get_content_charset()
        
if  type == None:
            
print  mail.get_payload()
        
else :
            
try :
                
print  unicode(mail.get_payload( ' base64 ' ),type)
            
except  UnicodeDecodeError:
                
print  mail



最后,有点要说明,如果邮件里的中文用mail.Header.decode_header()方法,和unicode()方法都不能正常显示,那么说明这个中文无法处理了,显示出来就是乱码.比如:看看看见,最终处理完成后,还是乱麻。

> ; > ; > ;mail.get( ' subject ' )
' Re: [python-chinese] =?UTF-8?B?wrnDmMOTw5p4bWzCscOgw4LDq8K1w4TDjg==?= =?UTF-8?B?w4rDjMOi?= '
> ; > ; > ;decode_header( mail.get( ' subject ' ))
[(
' Re: [python-chinese] ' , None), ( ' ¹ØÓÚxml±àÂëµÄÎÊÌâ ' ' utf-8 ' )]
> ; > ; > ; print  decode_header( mail.get( ' subject ' ))[ 1 ][0]
鹿脴脫脷xml卤脿脗毛碌脛脦脢脤芒
> ; > ; > ; print  unicode(decode_header( mail.get( ' subject ' ))[ 1 ][0], ' utf-8 ' )
1 ?óúxml±à??μ??êìa





 jasonnbfan 回复于:2005-07-11 20:22:35

下面说说发送邮件,其实我感觉发送比接收邮件要容易。还是使用mail.Message里的方法。我们一步一步来。
1:发送一个普通的文本邮件。
msg = mail.Message.Message()     # 一个实例 
msg[ ' to ' ] = ' love@python.com '        # 发送到哪里 
msg[ ' from ' ] = ' my@email.com '         # 自己的邮件地址 
msg[ ' date ' ] = time.ctime()              # 时间日期 
msg[ ' subject ' ] = email.Header.Header( ' 邮件主题 ' , ' gb2312 '
# 这里用Header方法处理subject. 
完成后的样子. 
> ; > ; > ; print  msg.as_string() 
to: love@python.com 
from : my@email.com 
date: Mon Jul 
11   20 : 18 : 13   2005  
subject: 
= ?gb2312?b?08q8 / tb3zOI = ? =   


下面开始写内容。
body = email.MIMEText.MIMEText( ' 这里是邮件内容 ' ,_subtype = ' plain ' ,_charset = ' gb2312 ' )

MIMEText()方法包括3个参数,内容,_subtype类型,_charset字符编码,完成后的样子: 
> ; > ; > ; print  body.as_string() 
Content
- Type: text / plain; charset = " gb2312 "  
MIME
- Version:  1.0  
Content
- Transfer - Encoding: base64 

1eLA78rHxNrI3Q
==  


Content-Type,说明内容类型,这里是txt/plain,纯文本类型。如果添加附件
那么就是Application/octet-stream
Content-Transfer-Encoding这个就是编码类型,这里是base64,现在的email都是base64编码

写完以后如何组合起来?mail有一个as_string()方法,顾名思义。显示成一个字符串.我上面也用了。smtplib里的sendmail()方法里需要的是字符串类型。所以我们这里可以这样: 完整的内容加起来就行了。
> ; > ; > ; print  msg.as_string() + body.as_string() 
to: love@python.com 
from : my@email.com 
date: Mon Jul 
11   20 : 18 : 13   2005  
subject: 
= ?gb2312?b?08q8 / tb3zOI = ? =  

Content
- Type: text / plain; charset = " gb2312 "  
MIME
- Version:  1.0  
Content
- Transfer - Encoding: base64 

1eLA78rHxNrI3Q
==  

 

如何发送.

server = smtplib.SMTP( ' smtp.mail.yahoo.com ' )   # 你发送服务器的地址 
server.login( ' username ' , ' password ' )        # 用户名和密码 
server.sendmail( ' from ' , ' to ' , ' msg.as_string()[:-1]+body.as_string() '
# 这样就完成了邮件的发送. 


有一点要注意.只有内容前面有一个空行,其他的地方都不能有空行.前面我们直接print msg.as_string()+body.as_string()可以看见在subject: 和Content-type:这里有一个空行,所以我用'msg.as_string()[:-1]把多余的空行去掉.让他和body保持格式.

下面看看如何发带附件的邮件,和上面差不多.


attachment.replace_header('Content-type','Application/octet-stream;name="myself.py"')
#前面讲过Content-type:他的值可以是text/plain text/heml 等等,如果是附件,就是Application/octet-stream,后面的;name="myself.py"是附件的文件名.默认的MIMEText()后这里的内容是text/plain的,所以需要替换

attachment.add_header('Content-Disposition','attachment;filename="myself.py")
#这里添加一个标题,Content-Disposition,attachment说明是一个附件,filename说明文件名.mail里有一个get_filename()的方法可以得到附件里的文件名.

attach.attach(attachment)  #现在我们把编码好的附件也加进去

完成后的邮件像下面这样


to: asdf@tom.com
from: asdf@tom.com
subject: =?gb2312?b?1vfM4g==?=
Content-Type: multipart/mixed; boundary="===============0572491976=="
MIME-Version: 1.0

--===============0572491976==
Content-Type: text/plain; charset="gb2312"
MIME-Version: 1.0
Content-Transfer-Encoding: base64

1eLA78rHxNrI3Q==

--===============0572491976==
Content-Type: Application/octet-stream;name="myself.py"
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: attachment;filename="myself.py"

ZnJvbSBlbWFpbC5NSU1FVGV4dCBpbXBvcnQgTUlNRVRleHQKZnJvbSBlbWFpbC5NSU1FTXVsdGlw
YXJ0IGltcG9ydCBNSU1FTXVsdGlwYXJ0CmZyb20gZW1haWwuSGVhZGVyIGltcG9ydCBIZWFkZXIK
ZnJvbSBlbWFpbC5IZWFkZXIgaW1wb3J0IGRlY29kZV9oZWFkZXIKZnJvbSB0eXBlcyBpbXBvcnQg
--===============0572491976==--


好了,可以发送了.
server=smtplib.SMTP('smtp.mail.yahoo.com')  
server.login('username','password')       
server.sendmail('from','to','msg.as_string()[:-1]+attach.as_string()')

刚才说了,附件也可以不用MIMEText()方法创建像下面这样也可以.
att=base64.encodestring(open('file','r').read())
att=MIMEText(att)
然后就和前面一样,换标题Content-type, 加Content-Disposition标题,等等.显然比较麻烦.

当然更简单的方法就是创建上面的attach以后,直接在attach里添加 主题等标题.
attach['to']='asdf@tom.com'
attach['from']='asdfd@tom.com'
attach['date']=time.ctime()
attach['subject']=Header('直接发送的标题','gb2312')
这样添加完以后直接attach.as_string()发送就可以了,包括了主题,内容,附件.

全文完,菜鸟学习经过,仅供新手参考.

希望高手能多多指点.


 jasonnbfan 回复于:2005-07-11 21:40:33

发现自己的表达能力太差,看来要多练练,大家将就着看好了。
这个星期研究Tk学会了写个GUI界面的邮件程序.学好了再上来写心得.


 jasonnbfan 回复于:2005-07-11 21:44:34

最后把我写的一个简陋的,幼稚的一个字符平台的email程序贴上来.希望高手能给指点指点.
程序的菜单截面根据Programming Python ed2,里面的程序改的,否则我肯定是想不出来这样的菜单截面.(想象力差,还是经验不足,晕)

保存邮件方法没有做出来.发送邮件也没写.


           
           
from  email.MIMEText  import  MIMEText
from  email.MIMEMultipart  import  MIMEMultipart
from  email.Header  import  Header
from  email.Header  import  decode_header
from  types  import   *
import  smtplib,poplib,string,sys,os,email


helptext 
=   """
Available commands:
i     - index display
l n?  - list all messages (or just message n)
d n?  - mark all messages for deletion (or just message n)
s n?  - save input num messages to a file (or just message n)
m     - compose and send a new mail message
q     - quit pymail
?     - display this help text
"""
# 简单的菜单处理,无返回值,要求一个处理过的mail列表
def  interact(processmail):
    
# showindex(processmail)
     while   1 :
        
try :
            command
= raw_input( ' [Pymail] Action? (i, l, d, s, m, q, ?)  ' )
        
except  EOFError:
            command
= ' q '

        
if  command == ' q '   or   not  command:
            
break

        
elif  command[0] == ' i ' :
            showindex(processmail)

        
elif  command[0] == ' l ' :
            
if  len(command) == 1 :
                
for  mail  in  processmail:
                    showmessage(mail)
                    
print  string.join(message)
            
else :
                
if  0 < msgnum(command) <= len(processmail):
                    num
= msgnum(command)
                    showsubject(processmail[num
- 1 ])
                    showmessage(processmail[num
- 1 ])
        
elif  command[0] == ' s ' :
            
if  len(command) == 1 :
                
print   ' 请输入要保存的邮件号码 '
                
continue
            
else :
                
if  0 < msgnum(command) <= len(processmail):
                    num
= msgnum(command)
                    savemail(processmail[num
- 1 ])

        
elif  command[0] == ' ? ' :
            
print  helptext

        
else :
            
print   ' What? -- type "?" for commands help '

 #保存email未完成           
def  savemail(mail):
    filename
= raw_input( ' Enter a file name: ' )
    file
= open( ' filename ' , ' w ' )
    
print   > ; > ; file,showsubject(mail),showmessage(mail)
    
print   ' saving mail to %s ok. '   % (filename)

# 处理输入的数字   
def  msgnum(command):
    
try :
        
return  string.atoi(string.split(command)[ 1 ])
    
except :
        
return   - 1

# 用于接收 邮件的相关处理,返回一个server实例   
def  POPconnect():
    sname,user,passwd
= popconfig()
    server
= poplib.POP3(sname)
    server.user(user)
    server.pass_(passwd)
    
print  server.getwelcome()
    
return  server

# 用于发送 邮件的相关处理,返回一个server实例 
def  SMTPconnect():
    server
= smtplib.SMTP(sname)
    server.login(user,passwd)
    
return  server

# 从服务器读取邮件到maillist.列表,位处理的原始字符串
def  loadmail():
    server
= POPconnect()
    
try :
        
print  server.list()
        (mailCount,mailByte)
= server.stat()
        
print   ' There are ' ,mailCount, ' mail messages in ' ,mailByte, ' bytes '
        
print   ' Retrieving: '
        mailList
= []
        
for  i  in  range(mailCount):
            
print  i + 1 ,
            (hdr,message,octet)
= server.retr(i + 1 )
            mailList.append(string.join(message,
' ' ))
        
print
        
assert  len(mailList) == mailCount
        
return  mailList

    
finally :
        server.quit()

# 处理loadmain返回的原始mail列表,返回处理过的processmail列表
def  processmail(mailList):
    processmaillist
= []
    
for  i  in  range(len(mailList)):
        processmaillist.append(email.message_from_string(mailList))
    
return  processmaillist

# 显示邮件主题,要求一个处理过的mail做参数
def  showsubject(mail):
    header
= []
    
for  head  in  decode_header(mail.get( ' subject ' )):
        
if  head[ 1 ] == ' utf-8 ' :
            header.append(unicode(head[0],
' utf-8 ' ))
        
else :
            header.append(head[0])
            
    
for  sub  in  ( ' From ' , ' Date ' , ' Subject ' ):
        
if  sub == ' Subject ' :
            
print   ' Subject: ' ,
            
for  subject  in  header:
                
try :
                    
print  subject,
                
except  UnicodeEncodeError:
                    
print   ' 注意:这个邮件标题无法正常显示... '
        
else :
            
print   ' %s:%s '   % (sub,mail[sub])
    
print       
        
# 显示邮件内容,要求一个处理过的mail做参数
def  showmessage(mail):
    
if  mail.is_multipart():
        
for  part  in  mail.get_payload():
            showmessage(part)
    
else :
        type
= mail.get_content_charset()
        
if  type == None:
            
print  mail.get_payload()
        
else :
            
try :
                
print  unicode(mail.get_payload(decode = ' base64 ' ),type)
            
except  UnicodeDecodeError:
                
print  mail

# 显示全部邮件主题要求整个处理过的邮件列表
def  showindex(processmaillist):
    count
= 1
    
for  mail  in  processmaillist:
        
print  count,
        showsubject(mail)
        
print  
        
if  count % 5 == 0:
            raw_input(
" [Press Enter key] " )
        count
+= 1

# 输入发送时需要的服务器名等相关信息,返回一个元组
def  sendconfig():
    SMTPname
= raw_input( ' SMTPserverName? ' )
    SMTPuser
= raw_input( ' SMTPusername? ' )
    SMTPpass
= raw_input( ' SMTPServerPassword? ' )
    To
= raw_input( ' To? ' )
    From
= raw_input( ' From? ' )
    
return  SMTPname,SMTPuser,SMTPpass,to,From

# 输入接收邮件时需要的相关输入,返回一个元组
def  popconfig():
   POPname
= raw_input( ' POPServerName? ' )
   POPuser
= raw_input( ' POPusername? ' )
   POPpass
= raw_input( ' POPpassword? ' )
   
return  POPname,POPuser,POPpass

if   __name__ == ' __main__ ' :
    list
= loadmail()
    maillist
= processmail(list)
    interact(maillist)
    







 nfqx 回复于:2005-07-12 09:09:12

鼓励一下


 bleem1998 回复于:2005-07-12 09:20:57

very good


 xichen 回复于:2005-07-12 09:23:46

对于汉字编码的转换,可以这样做,我试过效果不错。
a='中国'
a.decode('gb2312').encode("utf-8")


 guotie 回复于:2006-06-27 11:17:03

very good !

thanks!


 kai0200 回复于:2006-06-27 22:13:30

鼓励一下
帮你顶一下,向你学习!

[ 本帖最后由 kai0200 于 2006-7-4 22:22 编辑 ]


 guotie 回复于:2006-08-28 14:58:22

请教一个问题,如何判断邮件的附件?

btw,楼主的showmessage()用walk()来实现似乎更好一点。


 guotie 回复于:2006-08-29 14:56:54

判断某个part的编码应该使用Content-Transfer-Encoding。


 guotie 回复于:2006-08-29 16:27:48

处理邮件内容时,这样做更好一些:
参数mail是通过message_from_string()得到的instance
def  mail_content(mail): 
    content 
=      ''  
    
for  part  in  mail.walk(): 
        
if  part.is_multipart(): 
            
continue  

        ch 
=     part.get_content_charset() 
        
if  ch: 
            content 
+=     unicode(part.get_payload(decode  =  True),ch).encode( ' utf-8 '
        
else
            content 
+=     part.get_payload(decode  =  True).decode( ' gb2312 ' ).encode( ' utf-8 '

    
return  content

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值