邮件由三部分组成:信封,邮件头,邮件体
信封显然可以不含有二进制信息,而其它两部分则可能包含任意二进制序列,因此需要加以改进。MIME正是抓住了这两个地方来对他们加以改进。
1) 新增了一些邮件头信息,用来协商MIME的一些参数。
2) 定义了许多邮件内容的格式,对多媒体电子邮件的表示方法进行了标准化。
3) 定义了传送编码,从而可以传送任意二进制文件。
邮件头:
包含了发件人、收件人、主题、时间、MIME版本、邮件内容的类型等重要信息。每条信息称为一个域,由域名后加“: ”和信息内容构成,可以是一行,较长的也可以占用多行。域的首行必须“顶头”写,即左边不能有空白字符(空格和制表符);续行则必须以空白字符打头,且第一个空白字符不是信息本身固有的,解码时要过滤掉。
常见的域有:
邮件体:
邮件体又有许多段组成,每一段有自己的段头和段体
常见的头信息有:
类型 | 描述 |
---|---|
Content-Type: | 它定义了数据的类型,以便数据能被适当的处理。有效的类型有:text,image,audio,video, applications,multipart和message。注意任何一个二进制附件都应该被叫做application/octet- stream。这个头的一些用例为:image/jpg, application/mswork,multipart/mixed,这只是很少的一部分。 |
Content-Transfer-Encoding: | 这是所有头中最重要的一个,因为它说明了对数据所执行的编码方式,客户/MUA 将用它对附件进行解码。对于每个附件,可以使用7bit,8bit,binary ,quoted-printable,base64和custom中的一种编码方式。7bit编码是用在US ASCII字符集上的常用的一种编码方式,也就是,保持它的原样。8bit和binary编码一般不用。对人类可读的标准文本,如果传输要经过对格式有影响的网关时对其进行保护,可以使用quoted printable 。Base64是一种通用方法,在需要决定使用哪一种编码方法时,它提供了一个不用费脑子的选择;它通常用在二进制,非文本数据上。注意,任何非7bit 数据必须用一种模式编码,这样它就可以通过Internet邮件网关! |
Content-ID: | 如果Content-Type是message/external-body或multipart/alternative时,这个头就有用了。它超出了本文的范围。 |
Content-Description: | 这是一个可选的头。它是任何信息段内容的自由文本描述。描述必须使用us-ascii码。 |
Content-Disposition: | 一个试验性的头,它用于给客户程序/MUA提供提示,来决定是否在行内显示附件或作为单独的附件。MIME段头(出现在实际的MIME附件部分的头),除了MIME-Version头,可以拥有以上任何头字段。如果一个MIME头是信息块的一部分,它将作用于整个信息体。例如,如果Content-Transfer-Encoding显示在信息(指整个信息)头中,它应用于整个信息体,但是如果它显示在一个MIME段里,它”只能”用于那个段中. |
注意:其可以对自动对收到的邮件进行解密
Content-Type的格式:
header(“Content-type:text/html;charset=utf-8”);
Content-Dispostion的格式:
格式说明: content-disposition = “Content-Disposition” “:”
disposition-type *( “;” disposition-parm ) 字段说明:
Content-Disposition为属性名 disposition-type是以什么方式下载,如attachment为以附件方式下载
disposition-parm为默认保存时的文件名
服务端向客户端游览器发送文件时,如果是浏览器支持的文件类型,一般会默认使用浏览器打开,比如txt、jpg等,会直接在浏览器中显示,如果需要提示用户保存,就要利用Content-Disposition进行一下处理,关键在于一定要加上attachment:
还有常见的MIME类型,请参考上一篇博客。
下面关于SMTP和POP3的代码参考自:廖雪峰的官方网站
写的很好,帮助很大。
import smtplib,poplib
from time import sleep
from email.mime.text import MIMEText
from email import encoders
from email.header import Header
from email import parser
import email
from email.header import decode_header
from DecodeEmail import print_info
from email.utils import parseaddr,formataddr
def format_addr(s):
name,addr=parseaddr(s)
return formataddr((Header(name,'utf-8').encode(),addr))
smtpmailserver='smtp.qq.com'
pop3mailserver='pop.qq.com'
sender='xxxxxx@qq.com'
receiver='xxxxxx@qq.com'
username='xxxxxx@qq.com'
password='xxxxxx'
msg=MIMEText('''
I am a programmer!
what's your name?
My name is tanying.
enheng
You are my baby!
hahahahahah
''','plain','utf-8')
msg['Subject']=Header('SMTP发邮件','utf-8').encode()
msg['From']=format_addr('Python爱好者<%s>' % sender)
msg['To']=format_addr('管理员<%s>' % receiver)
smtp=smtplib.SMTP(smtpmailserver)
smtp.set_debuglevel(1)
smtp.login(username,password)
smtp.sendmail(sender,receiver,msg.as_string())
smtp.quit()
pop=poplib.POP3(pop3mailserver)
pop.user(username)
pop.pass_(password)
#retr()返回一个tuple,其中包含了结果代码和邮件。但是邮件并不是字符串格式,而是一个字符串的列表。每一个元素表示该邮件的一行。我们可以使用”\n”.join(lines)来把它们转换成标准的字符串。
resp,mails,size=pop.retr(pop.stat()[0])
msg_content=b'\r\n'.join(mails)
#parser.Parser().parsestr将字符串解析成Message,参数只能是字符串
msg=parser.Parser().parsestr(bytes.decode(msg_content))
pop.quit()
print_info(msg,0)
__author__ = 'qingjin'
import email
from email.parser import Parser
from email.header import decode_header
from email.utils import parseaddr
def decode_header_str(s):
#decode_header解析头部字符串,返回一个list,包含字符串的值和编码方式
value,charset=decode_header(s)[0]
if charset:
value=value.decode(charset)
return value
def guess_charset(msg):
charset=msg.get_charset()
if charset is None:
#如果没有那么从Content-type中获得charset的值
#Content-type格式是这样的header("Content-type:text/html;charset=utf-8");
content_type=msg.get('Content-Type','').lower()
pos=content_type.find('charset=')
if(pos>=0):
charset=content_type[pos+8:].strip()
return charset
def print_info(msg,index=0):
if not index:
for x in ['Subject','From','To']:
value=msg[x]
if value:
if x=='Subject':
value=decode_header_str(x)
else :
#邮件的地址可以是这种形式:name<addr@163.com>
#parseaddr返回name和addr
name,addr=parseaddr(value)
name=decode_header_str(name)
value=u'%s<%s>' % (name,addr)
print('%s%s: %s' % (' '*index,x,value))
if msg.is_multipart():
#get_payload return a list of Message
#get_payload返回这个段中的Message列表
parts=msg.get_payload()
for n,part in enumerate(parts):
print('%spart %d' % (' '*index,n))
print('%s-------------------------' % (' '*index))
print_info(part,index+1)
else:
#get_content_type返回Message的MIME类型
content_type=msg.get_content_type()
if content_type in ['text/plain','text/html']:
content=msg.get_payload(decode=True)
charset=guess_charset(msg)
if charset:
content=content.decode(charset)
print('%sText: %s' % (' '*index,content+'...'))
else:
print('%sAttachment: %s'%(' '*index,content_type))
还有几个地址也解决了我几个问题
python字符串str和字节数组相互转化
header中Content-Disposition的作用与使用方法