十七:电子邮件:
一封电子邮件的旅程就是:发件人-->MUA-->MTA-->MTA-->若干个MTA-->MDA<--MUA<--收件人
MTA:Mail Transfer Agent 邮件传输代理 MUA:Mail User Agent 邮件用户代理 MDA:Mail Delivery Agent 邮件投递代理
要编写程序来发送和接受邮件 本质上是:
1:编写MUA把邮件发送到MTA
2:编写MUA从MDA上收邮件
1:SMTP发送邮件
SMTP是发送邮件的协议 Python内置对SMTP的支持 可以发送纯文本邮件 HTML文件以及带附件的邮件
Python对SMTP支持有smtplib 和email 两个模块 email负责构造邮件 smtplib 负责发送邮件
首先 我们来构造一个最简单的纯文本邮件:
from email.mime.text import MIMEText
msg=-MIMEText('Hello ,send by Python...','plain','utf-8')
注意到构造MIMEText对象时,第一个参数是邮件正文,第二个参数是MIME的subtype传入‘plain’最终的MIME就是‘text/plain’最后一定要用'urf-8'
然后通过SMTP发送出去
#输入Email的地址和口令
from_addr=raw_input('From:')
password=raw_input('Password:')
#输入SMTP服务器的地址
smtp_server=raw_input('SMTP server:')
#输入收件人地址
to_addr=raw_input('To:.....')
import smtplib
server=smtplib.SMTP(smtp_server,25)
server.set_debuglevel(1)
server.login(from_addr,password)
server.sendmail(from_addr,[to_addr],msg.as_tostring())
server.quit()
我们用set_debuglevel(1)就可以打印出和SMTP服务器交互的所有信息 SMTP协议就是简单的文本命令和响应 login()方法用来登录SMTP服务器 sendmail()方法就是发邮件由于一次可以给多个人发 所以传入 的是一个list 邮件正文是一个str as_tostring 把MIMETest 对象转化为一个str
如果一切顺利 就可以在收件人信箱里面收到我们刚发送的信息
但是邮件1:没有主题 2:邮件人的名字没有显示为友好的名字 3:明明收到了邮件 却提示不在收件人中
我们需要把 from to subject 添加到MIMETest中
#-*-coding:utf-8-*-
from email import encoders
from email.header import Header
from email.mime.text import MIMEText
from emial.utils import parseaddr,formataddr
import smtplib
def _format_addr(s):
name,addr=parseaddr(s)
return formataddr((\
Header(name,'utf-8').encoding(),\
addr.encoding('utf-8') if isinstance(addr,unicode) else addr))
from_addr=raw_input('From')
password=raw_input('Password: ')
to_addr=raw_input('To:')
smtp_server=raw_input('smtp server:')
msg=MIMEText('hello ,send by Python ....','plain','utf-8')
msg['From']=_format_addr(u'Python 爱好者 <%s>' % from_addr)
msg['To'] = _format_addr(u'管理员 <%s>' % to_addr)
msg['Subject'] =Header(u'来自SMTP的问候','utf-8').encoding()
server=smtplib.SMTP(smtp_server,25)
server.set_debuglevel(1)
server.login(from_addr,password)
server.sendmail(from_addr,[to_addr],msg.as_tostring())
server.quit()
发送HTML邮件
如果要发送HTML邮件 而不是普通的纯文本文件 方法就是 在构造MIMEText对象时 把HTML字符串传进去 再把第二个参数由plain 变为 html 就可以了
msg=MIMEText('
Hello
'+'
send by Pytohn...
'+'','html','utf-8')
发送附件: 如果要发送附件 可以构造一个MIMEMultipart 对象代表邮件本身 然后往里面加上一个MIMEText 作为邮件正文 在继续往里面加上表示附件的MIMEBase对象即可
#邮件对象
msg=MIMEMultipart()
msg['From']=_format_addr(u'Python 爱好者 <%s>' % from_addr)
msg['To'] = _format_addr(u'管理员 <%s>' % to_addr)
msg['Subject'] =Header(u'来自SMTP的问候','utf-8').encoding()
#邮件正文是MIMEText:
msg.attach(
MIMEText('hello ,send by Python ....','plain','utf-8')
)
#添加附件就是加上一个MIMEBase 从本地读取一个图片
with open('a.png','rb') as f:
#设置附件的MIME和文件名 这里是png类型:
mime=MIMEBase('image','png',filename='a.png')
#加上必要的头信息
mime.add_header('Content-Disposition','attachment',filename='a.png')
mime.add_header('Content-ID','<0>')
mime.add_header('X-Attachment-Id','0')
#把附件类容读进来
mime.set_payload(f.read())
#利用base64编码
encoders.encode_base64(mime)
#添加到MIMEMultipart:
msg.attach(mime)
server=smtplib.SMTP(smtp_server,25)
server.set_debuglevel(1)
server.login(from_addr,password)
server.sendmail(from_addr,[to_addr],msg.as_tostring())
server.quit()
发送图片:
如果要把一个图片嵌入到正文中怎么做 ?要把图片嵌入到正文中 我们只需要按照发送邮件的方式 先把邮件作为附件添加进去 然后在HTML中通过引用 src='cid=:0' 就可以把有附件作为图片嵌入了
msg.attach(MIMEText('
Hello

','html','utf-8'))
同时支持HTML和plain格式
如果收件人使用的设备太老看不了HTML邮件 将其自动降级为纯文本文件
利用MIMEMultipart 就可以组合一个HTML和plain 要注意subtype是alternative
msg=MIMEMultipart('alternative')
msg['From']=......
msg['To']=.....
msg['Subject']=.....
msg.attach(MIMEText('Hello','plain','utf-8'))
msg.attach(
MIMEText('Hello
'+'send by Pytohn...
'+'','html','utf-8')
)
........
加密SMTP
smtp_server='smtp.gmail.com'
smtp_port=587
server=smtplib.SMTP(smtp_server,smtp_port)
server.starttls()
......
只要在创建了SMTP对象之后立即调用starttls方法 这样就创建了安全连接
2:接受邮件 POP3
接受邮件分为两步:1:用poplib 把邮件的原始文本下载到本地 2:用email解析原始文本 还原为邮件对象
1:通过POP3 下载邮件
import poplib
#输入邮件地址,口令和POP3服务器地址:
email=raw_input('Email:...')
password=raw_input('Password')
pop3_server=raw_iinput('POP3 server..')
#连接到pop3 服务器
server=poplib.POP3(pop3_server)
#可以打开或者关闭调试信息
server.set_debuglevel(1)
#可选 打印POP3服务器欢迎文字
print(server.getwelcome())
#身份认知
server.user(email)
server.pass_(password)
#stat()返回邮件数量 和占用空间
print('Message:%s Size: %s' % server.stat())
#list() 返回所有邮件的编号
resp,mails.octets=server.list()
#可以查看邮件的列表 类似【1 82923 2 2184】
print mails
#获取最新的一封邮件 注意索引号从1开始
index=len(mails)
resp,lines,octets=server.retr(index)
#lines存储了邮件的原始文本的没一行
#可以获得整个邮件的原始文本
msg_content='\r\n'.join(lines)
#稍后解析邮件
msg=Parser().parsestr(msg_content)
#可以根据邮件索引号直接从服务器删除邮件
server.dele(index)
sever.quit()
解析邮件:
解析邮件的过程与发送邮件的过程正好相反
import email
from email.parser import Parser
from email.header import decode_header
from email.utils import parseaddr
#msg=Parser().parsestr(msg_content) 这个将邮件内容解析问一个Messge对象但是这个对象可能是MIMEMultipart 对象
#indent用于缩进显示
def print_info(msg,indent=0):
if indent=0:
#邮件的from to subject 存于根对象上
for header in ['From','To','Subject']
value=msg.get(header,'')
if value:
if header=="Subject":
#需要解析subject 字符串
value=decode_str(value)
else:
#需要解码Email地址
hdr,addr=parseaddr(value)
name=decode_str(hdr)
value=u'%s <%s>' %(name,addr)
print('%s%s:%s' % (' '*indent,header,value))
if (msg.is_multipart()):
#如果对象是一个MIMEMultipart get_payload()返回list包含所有的子对象
parts=msg.get_payload()
for n,part in enumerate(parts):
print('%spart %s' % (' '*indent,n))
print('%s-------------' % (' '*indent ))
#递归打印每一个子对象
print_info(part,indent+1)
else:
#邮件对象不是一个MIMEMultipart 就根据content_type判断
content_type=msg.get_content_type()
if content_type=='text/plain' or content_type='text/html':
content=msg.get_payload(decode=True)
#要检测文本编码
charset=guess_charset(msg)
if charset:
content=content.decode(charset)
print('%sText:%s' % (' '*indent,content+'......'))
else:
#不是文本 作为附件处理
print('%s Attachment:%s' % (' '*indent,content_type))
def decode_str(s):
value,charset=decode_header(s)[0]
if charset:
value=value.decode(charset)
return value
def guess_charset(msg):
#从msg对象获取编码
charset=msg.get_charset()
if charset is None:
#如果没有 再从content_type 字段获取
content_type=msg.get('Content-Type','').lower()
pos=content_type.find('charset=')
if pos>=0:
charset=content_type[pos+8:].strip()
return charset