python与SMTP

SMTP (Simple Mail Transfer Protocol)
  邮件传送代理 (Mail Transfer Agent,MTA) 程序使用SMTP协议来发送电邮到接收者的邮件服务器。SMTP协议只能用来发送邮件,不能用来接收邮件。大多数的邮件发送服务器 (Outgoing Mail Server) 都是使用SMTP协议。SMTP协议的默认TCP端口号是25。

  SMTP协议的一个重要特点是它能够接力传送邮件。它工作在两种情况下:一是电子邮件从客户机传输到服务器;二是从某一个服务器传输到另一个服务器。

POP3 (Post Office Protocol) & IMAP (Internet Message Access Protocol)
  POP协议和IMAP协议是用于邮件接收的最常见的两种协议。几乎所有的邮件客户端和服务器都支持这两种协议。
  POP3协议为用户提供了一种简单、标准的方式来访问邮箱和获取电邮。使用POP3协议的电邮客户端通常的工作过程是:连接服务器、获取所有信息并保存在用户主机、从服务器删除这些消息然后断开连接。POP3协议的默认TCP端口号是110。

  IMAP协议也提供了方便的邮件下载服务,让用户能进行离线阅读。使用IMAP协议的电邮客户端通常把信息保留在服务器上直到用户显式删除。这种特性使得多个客户端可以同时管理一个邮箱。IMAP协议提供了摘要浏览功能,可以让用户在阅读完所有的邮件到达时间、主题、发件人、大小等信息后再决定是否下载。IMAP协议的默认TCP端口号是143。
邮件格式 (RFC 2822)
  每封邮件都有两个部分:邮件头和邮件体,两者使用一个空行分隔。
  邮件头每个字段 (Field) 包括两部分:字段名和字段值,两者使用冒号分隔。有两个字段需要注意:From和Sender字段。From字段指明的是邮件的作者,Sender字段指明的是邮件的发送者。如果From字段包含多于一个的作者,必须指定Sender字段;如果From字段只有一个作者并且作者和发送者相同,那么不应该再使用Sender字段,否则From字段和Sender字段应该同时使用。
  邮件体包含邮件的内容,它的类型由邮件头的Content-Type字段指明。RFC 2822定义的邮件格式中,邮件体只是单纯的ASCII编码的字符序列。
MIME (Multipurpose Internet Mail Extensions) (RFC 1341)

  MIME扩展邮件的格式,用以支持非ASCII编码的文本、非文本附件以及包含多个部分 (multi-part) 的邮件体等。
  

首先简单的介绍一下python的smtplib模块:

smtplib.SMTP([host[, port[, local_hostname[, timeout]]]])
  SMTP类构造函数,表示与SMTP服务器之间的连接,通过这个连接我们可以向smtp服务器发送指令,执行相关操作(如:登陆、发送邮件)。该类提供了许多方法,将在下面介绍。它的所有参数都是可选的,其中host参数表示smtp服务器主机名,上面例子中的smtp主机为”smtp.yeah.net”;port表示smtp服务的端口,默认是25;如果在创建SMTP对象的时候提供了这两个参数,在初始化的时候会自动调用connect方法去连接服务器。
  smtplib模块还提供了SMTP_SSL类和LMTP类,对它们的操作与SMTP基本一致。
  smtplib.SMTP提供的方法:

SMTP.set_debuglevel(level)
  设置是否为调试模式。默认为False,即非调试模式,表示不输出任何调试信息。

SMTP.connect([host[, port]])
  连接到指定的smtp服务器。参数分别表示smpt主机和端口。注意: 也可以在host参数中指定端口号(如:smpt.yeah.net:25),这样就没必要给出port参数。

SMTP.docmd(cmd[, argstring])
  向smtp服务器发送指令。可选参数argstring表示指令的参数。下面的例子完全通过调用docmd方法向服务器发送指令来实现邮件的发送(在smtp.yeah.net邮件服务器上试验通过。其他邮件服务器没有试过):
  常用指令有:
  HELO 向服务器标识用户身份
MAIL 初始化邮件传输 mail from:
RCPT 标识单个的邮件接收人;常在MAIL命令后面,可有多个rcpt to:
DATA 在单个或多个RCPT命令后,表示所有的邮件接收人已标识,并初始化数据传输,以.结束
VRFY 用于验证指定的用户/邮箱是否存在;由于安全方面的原因,服务器常禁止此命令
EXPN 验证给定的邮箱列表是否存在,扩充邮箱列表,也常被禁用
HELP 查询服务器支持什么命令
NOOP 无操作,服务器应响应OK
QUIT 结束会话
RSET 重置会话,当前传输被取消
MAIL FROM 指定发送者地址
RCPT TO 指明的接收者地址
SMTP.helo([hostname])
  使用”helo”指令向服务器确认身份。相当于告诉smtp服务器“我是谁”。

SMTP.has_extn(name)
  判断指定名称在服务器邮件列表中是否存在。出于安全考虑,smtp服务器往往屏蔽了该指令。

SMTP.verify(address)
  判断指定邮件地址是否在服务器中存在。出于安全考虑,smtp服务器往往屏蔽了该指令。

SMTP.login(user, password)
  登陆到smtp服务器。现在几乎所有的smtp服务器,都必须在验证用户信息合法之后才允许发送邮件。

SMTP.sendmail(from_addr, to_addrs, msg[, mail_options, rcpt_options])
  发送邮件。这里要注意一下第三个参数,msg是字符串,表示邮件。我们知道邮件一般由标题,发信人,收件人,邮件内容,附件等构成,发送邮件的时候,要注意msg的格式
  

如果想在邮件中携带附件、使用html书写邮件,附带图片等等,就需要使用email模块及其子模块。下面来看看email包,email包是用来管理email信息的,它包括MIME和其他基于RFC 2822的消息格式。email包的主要特征是在它内部解析和生成email信息是分开的模块来实现的。

MIME消息由消息头和消息体两大部分组成,在邮件里就是邮件头和邮件体。邮件头与邮件体之间以空行进行分隔。

邮件头包含了发件人、收件人、主题、时间、MIME版本、邮件内容的类型等重要信息。每条信息称为一个域,由域名后加“: ”和信息内容构成,可以是一行,较长的也可以占用多行。域的首行必须“顶头”写,即左边不能有空白字符(空格和制表符);续行则必须以空白字符打头,且第一个空白字符不是信息本身固有的。

邮件体包含邮件的内容,它的类型由邮件头的“Content-Type”域指出。最常见的类型有text/plain(纯文本)和text/html(超文本)。邮件体被分为多个段,每个段又包含段头和段体两部分,这两部分之间也以空行分隔。常见的multipart类型有三种:multipart/mixed, multipart/related和multipart/alternative。
主要来看看email.mime,在邮件中携带附件、图片、音频时,主要使用的是该模块。一般情况下,你通过解析一个文件或者一段text来生成一个消息对象结构,你也可以从头开始建立一个消息结构,实际上,你可以给一个已经存在的消息结构追加一个新的消息对象。你可以通过创建message实例来创建一个对象结构,然后给该结构追加附件和头部信息。email包提供了一些子类使得该操作变得很容易。

class email.mime.multipart. MIMEMultipart: 多个MIME对象的集合。
class email.mime.audio. MIMEAudio: MIME音频对象。
class email.mime.image. MIMEImage: MIME二进制文件对象。
class email.mime.text. MIMEText: MIME文本对象。

下面是用smpt发送纯文本形式邮件的代码:

import smtplib
from email.mime.text import MIMEText
from email.header import Header
from email.utils import COMMASPACE,formatdate


sender = 'xxxxxx@qq.com'
receiver = 'xxxxxx@qq.com'
subject = 'hahahaha'
smtpserver = 'smtp.qq.com'
username = 'xxxxxx@qq.com'
password = 'xxxxxx'

msg = MIMEText("You're my baby!",'plain','utf-8')
msg['Subject'] = Header(subject, 'utf-8')#不写这个Header也是对的
msg['Subject'].append('gaolee','utf-8')
msg['From'] =sender#这个不写邮件的发件人不会显示,相当于匿名
msg['To'] = receiver
#msg['To'] = COMMASPACE.join(receiver)#以逗号分割receiver
#msg['Date']=formatdate(localtime=True)
smtp = smtplib.SMTP('smtp.qq.com')
#smtp.connect('smtp.163.com')
smtp.set_debuglevel(1)#打印整个过程中的信息
smtp.login(username, password)#登录
smtp.sendmail(sender, receiver, msg.as_string())
smtp.quit()

这里解释一下Header的用途(摘自官方文档):
这里写图片描述
这里写图片描述
formatdata的作用:
这里写图片描述

以上内容也可以直接用docmd实现:

import smtplib
import base64

sender='upc_dip@163.com'
receiver='acprime@163.com'
username=base64.encodestring(b'upc_dip@163.com')
password=base64.encodestring(b'upc_cs')

smtp=smtplib.SMTP('smtp.163.com')
smtp.set_debuglevel(1)
smtp.docmd('EHLO server')
smtp.docmd('AUTH LOGIN')
smtp.send(username)
smtp.send(password)
smtp.docmd('MAIL FROM:<%s>' % sender)
smtp.docmd('RCPT TO:<%s>' % receiver)
smtp.docmd('DATA')
smtp.send('test')
smtp.send('.')
smtp.quit()

注意username和password要用base64编码

发送html文本形式的邮件:

msg = MIMEText("<html><h1>haha</h1></html>",'html','utf-8')
msg['Subject'] = Header(subject, 'utf-8')
msg['Subject'].append('gaolee','utf-8')
msg['From'] =sender
msg['To'] = receiver

smtp = smtplib.SMTP('smtp.163.com')

smtp.set_debuglevel(1)
smtp.login(username, password)
smtp.sendmail(sender, receiver, msg.as_string())
smtp.quit()

从上面可以看出,区分不同的邮件体可以通过MIMEText的参数来改变。

MIMEImage发送图片邮件:

f=open('12072113.JPG','rb')
msgImage=MIMEImage(f.read())
f.close()
msgImage['From']=sender
msgImage['To']=receiver
msgImage['Subject']=Header('Image')

smtp=smtplib.SMTP('smtp.163.com')
smtp.login(username,password)
smtp.sendmail(sender,receiver,msgImage.as_string())
smtp.quit()

那么如果我想把图片嵌入到html中,也就是把他们关联起来,怎么搞:

msgRoot=MIMEMultipart('related')
msgRoot['From']=sender
msgRoot['To']=receiver
msgRoot['Subject']=Header('Image')

msgText=MIMEText('<b>Some <i>HTML</i> text</b> and an image.<br><img src="cid:image1"><br>good!','html','utf-8')
msgRoot.attach(msgText)

f=open('12072113.JPG','rb')
msgImage=MIMEImage(f.read())
msgImage.add_header('Content-ID','<image>')
msgRoot.attach(msgImage)
f.close()

smtp=smtplib.SMTP('smtp.163.com')
smtp.login(username,password)
smtp.sendmail(sender,receiver,msgRoot.as_string())
smtp.quit()

发送带附件的邮件:
步骤如下:
1. 构造MIMEMultipart对象做为根容器
2. 构造MIMEText对象做为邮件显示内容并附加到根容器
3. 构造MIMEBase对象做为文件附件内容并附加到根容器
  a. 读入文件内容并格式化
  b. 设置附件头
4. 设置根容器属性
5. 得到格式化后的完整文本
6. 用smtp发送邮件

import smtplib
from email.mime.text import MIMEText
from email.header import Header
from email.utils import COMMASPACE,formatdate
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart,MIMEBase
from email import encoders
import os
import mimetypes

mailserver='smtp.163.com'
sender = 'upc_dip@163.com'
receiver = 'acprime@163.com'
subject = 'hahahaha'
smtpserver = 'smtp.163.com'
username = 'upc_dip@163.com'
password = 'upc_cs'

file='D:\BDdownloads\WxBoxSetup.1398305031.exe'
# 构造MIMEMultipart对象做为根容器
msgRoot=MIMEMultipart()
msgRoot['From']=sender
msgRoot['To']=receiver
msgRoot['Subject']=Header('Image')

# 构造MIMEText对象做为邮件显示内容并附加到根容器
text=MIMEText("This's my downloads",'plain','utf-8')
msgRoot.attach(text)

#测试识别文件类型:mimetypes.guess_type(file_name) 
# ctype,encoding值:
# rar 文件  None None(ini文件、csv文件、apk文件) 
# txt text/plain None 
# py  text/x-python None 
# gif image/gif None 
# png image/x-png None 
# jpg image/pjpeg None 
# pdf application/pdf None 
# doc application/msword None 
# zip application/x-zip-compressed None 
data=open(file,'rb')
ctype,encoding=mimetypes.guess_type(file)
if ctype is None or encoding is not None:
    ctype='application/octet-stream'
maintype,subtype=ctype.split('/',1)


# 构造MIMEBase对象做为文件附件内容并附加到根容器
msg=MIMEBase(maintype,subtype)
msg.set_payload(data.read())
msg.add_header('Content-Disposition','attachment',filename=os.path.basename(file))
data.close()
encoders.encode_base64(msg)
msgRoot.attach(msg)

smtp=smtplib.SMTP(mailserver)
smtp.login(username,password)
smtp.sendmail(sender,receiver,msgRoot.as_string())
smtp.quit()

或者像下面一样:

att = MIMEText(open('h:\\python\\1.jpg', 'rb').read(), 'base64', 'utf-8')    
att["Content-Type"] = 'application/octet-stream'    
att["Content-Disposition"] = 'attachment; filename="1.jpg"'    
msgRoot.attach(att)   

这里有几点需要说明:
1.Message对象包含两部分:头和有效负载(我理解应该就是携带的内容)所以set_payload就是设置负载
2.这里写图片描述
也就是说MIMEImage和MIMEAudio自带了,其他的需要自己进行编码

如果想发送群邮件其实就是把receiver写成一个列表

刚开始不太明白MIMEMultipart和其他的区别

class email.mime.multipart.MIMEMultipart(_subtype=’mixed’, boundary=None, _subparts=None, **_params)

Module: email.mime.multipart

A subclass of MIMEBase, this is an intermediate base class for MIME messages that are multipart. Optional _subtype defaults to mixed, but can be used to specify the subtype of the message. A Content-Type header of multipart/_subtype will be added to the message object. A MIME-Version header will also be added.

class email.mime.text.MIMEText(_text, _subtype=’plain’, _charset=None)

Module: email.mime.text

A subclass of MIMENonMultipart, the MIMEText class is used to create MIME objects of major type text. _text is the string for the payload. _subtype is the minor type and defaults to plain. _charset is the character set of the text and is passed as an argument to the MIMENonMultipart constructor; it defaults to us-ascii if the string contains only ascii code points, and utf-8 otherwise.

其实MIMEMultipart就像是一个集合,可以在里面添加其他的想要添加的东西

还可以添加音频等等,详见python文档
email.message(发送的邮件信息的介绍)

还有几个问题是我刚开始疑惑地:
1.在添加附件时,需要自己设置邮件头,那么怎么设置,有哪些内容
2.构造MIMEBase对象时的参数是什么(其实就是上面代码中注释的哪一些,也就是表明这是什么类型的附件)
MIME协议分析
廖雪峰的官方网站(SMTP发送邮件)
MIME协议(包含比较全面的MIME类型)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值