python模块smtplib学习


Posted in Python onMay 22, 2018

python的smtplib提供了一种很方便的途径发送电子邮件。它对smtp协议进行了简单的封装。

smtp协议的基本命令包括:
    HELO 向服务器标识用户身份
    MAIL 初始化邮件传输 mail from:
    RCPT 标识单个的邮件接收人;常在MAIL命令后面,可有多个rcpt to:
    DATA 在单个或多个RCPT命令后,表示所有的邮件接收人已标识,并初始化数据传输,以.结束
    VRFY 用于验证指定的用户/邮箱是否存在;由于安全方面的原因,服务器常禁止此命令
    EXPN 验证给定的邮箱列表是否存在,扩充邮箱列表,也常被禁用
    HELP 查询服务器支持什么命令
    NOOP 无操作,服务器应响应OK
    QUIT 结束会话
    RSET 重置会话,当前传输被取消
    MAIL FROM 指定发送者地址
    RCPT TO 指明的接收者地址

    一般smtp会话有两种方式,一种是邮件直接投递,就是说,比如你要发邮件?zzz@163.com,那就直接连接163.com的邮件服务器,把信投?zzz@163.com; 另一种是验证过后的发信,它的过程是,比如你要发邮件?zzz@163.com,你不是直接投到163.com,而是通过自己在sina.com的另一个邮箱来发。这样就要先连接sina.com的smtp服务器,然后认证,之后在把要发到163.com的信件投到sina.com上,sina.com会帮你把信投递到163.com。

    第一种方式的命令流程基本是这样:

      1. helo
      2. mail from
      3. rcpt to
      4. data
      5. quit

    但是第一种发送方式一般有限制的,就是rcpt to指定的这个邮件接收者必须在这个服务器上存在,否则是不会接收的。 先看看代码:

#-*- encoding: gb2312 -*-
import os, sys, string
import smtplib

# 邮件服务器地址
mailserver = "smtp.163.com"
# smtp会话过程中的mail from地址
from_addr = "asfgysg@zxsdf.com"
# smtp会话过程中的rcpt to地址
to_addr = "zhaoweikid@163.com"
# 信件内容
msg = "test mail"

svr = smtplib.SMTP(mailserver)
# 设置为调试模式,就是在会话过程中会有输出信息
svr.set_debuglevel(1)
# helo命令,docmd方法包括了获取对方服务器返回信息
svr.docmd("HELO server")
# mail from, 发送邮件发送者
svr.docmd("MAIL FROM: <%s>" % from_addr)
# rcpt to, 邮件接收者
svr.docmd("RCPT TO: <%s>" % to_addr)
# data命令,开始发送数据
svr.docmd("DATA")
# 发送正文数据
svr.send(msg)
# 比如以 . 作为正文发送结束的标记,用send发送的,所以要用getreply获取返回信息
svr.send(" . ")
svr.getreply()
# 发送结束,退出
svr.quit()

    注意的是,163.com是有反垃圾邮件功能的,想上面的这种投递邮件的方法不一定能通过反垃圾邮件系统的检测的。所以一般不推荐个人这样发送。

    第二种有点不一样:
      1.ehlo
      2.auth login
      3.mail from
      4.rcpt to
      5.data
      6.quit

相对于第一种来说,多了一个认证过程,就是auth login这个过程。

#-*- encoding: gb2312 -*-
import os, sys, string
import smtplib
import base64

# 邮件服务器地址
mailserver = "smtp.163.com"
# 邮件用户名
username = "xxxxxx@163.com"
# 密码
password = "xxxxxxx"
# smtp会话过程中的mail from地址
from_addr = "xxxxxx@163.com"
# smtp会话过程中的rcpt to地址
to_addr = "yyyyyy@163.com"
# 信件内容
msg = "my test mail"

svr = smtplib.SMTP(mailserver)
# 设置为调试模式,就是在会话过程中会有输出信息
svr.set_debuglevel(1)
# ehlo命令,docmd方法包括了获取对方服务器返回信息
svr.docmd("EHLO server")
# auth login 命令
svr.docmd("AUTH LOGIN")
# 发送用户名,是base64编码过的,用send发送的,所以要用getreply获取返回信息
svr.send(base64.encodestring(username))
svr.getreply()
# 发送密码
svr.send(base64.encodestring(password))
svr.getreply()
# mail from, 发送邮件发送者
svr.docmd("MAIL FROM: <%s>" % from_addr)
# rcpt to, 邮件接收者
svr.docmd("RCPT TO: <%s>" % to_addr)
# data命令,开始发送数据
svr.docmd("DATA")
# 发送正文数据
svr.send(msg)
# 比如以 . 作为正文发送结束的标记
svr.send(" . ")
svr.getreply()
# 发送结束,退出
svr.quit()

     上面说的是最普通的情况,但是不能忽略的是现在好多企业邮件是支持安全邮件的,就是通过SSL发送的邮件,这个怎么发呢?SMTP对SSL安全邮件的支持有两种方案,一种老的是专门开启一个465端口来接收ssl邮件,另一种更新的做法是在标准的25端口的smtp上增加一个starttls的命令来支持。

    看看第一种怎么办:

#-*- encoding: gb2312 -*-
import os, sys, string, socket
import smtplib


class SMTP_SSL (smtplib.SMTP):
  def __init__(self, host='', port=465, local_hostname=None, key=None, cert=None):
    self.cert = cert
    self.key = key
    smtplib.SMTP.__init__(self, host, port, local_hostname)
    
  def connect(self, host='localhost', port=465):
    if not port and (host.find(':') == host.rfind(':')):
      i = host.rfind(':')
      if i >= 0:
        host, port = host[:i], host[i+1:]
        try: port = int(port)
        except ValueError:
          raise socket.error, "nonnumeric port"
    if not port: port = 654
    if self.debuglevel > 0: print>>stderr, 'connect:', (host, port)
    msg = "getaddrinfo returns an empty list"
    self.sock = None
    for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
      af, socktype, proto, canonname, sa = res
      try:
        self.sock = socket.socket(af, socktype, proto)
        if self.debuglevel > 0: print>>stderr, 'connect:', (host, port)
        self.sock.connect(sa)
        # 新增加的创建ssl连接
        sslobj = socket.ssl(self.sock, self.key, self.cert)
      except socket.error, msg:
        if self.debuglevel > 0: 
          print>>stderr, 'connect fail:', (host, port)
        if self.sock:
          self.sock.close()
        self.sock = None
        continue
      break
    if not self.sock:
      raise socket.error, msg

    # 设置ssl
    self.sock = smtplib.SSLFakeSocket(self.sock, sslobj)
    self.file = smtplib.SSLFakeFile(sslobj);

    (code, msg) = self.getreply()
    if self.debuglevel > 0: print>>stderr, "connect:", msg
    return (code, msg)
    
if __name__ == '__main__':
  smtp = SMTP_SSL('192.168.2.10')
  smtp.set_debuglevel(1)
  smtp.sendmail("zzz@xxx.com", "zhaowei@zhaowei.com", "xxxxxxxxxxxxxxxxx")
  smtp.quit()

     这里我是从原来的smtplib.SMTP派生出了新的SMTP_SSL类,它专门来处理ssl连接。我这里测试的192.168.2.10是我自己的测试服务器.

    第二种是新增加了starttls的命令,这个很简单,smtplib里就有这个方法,叫smtplib.starttls()。当然,不是所有的邮件系统都支持安全邮件的,这个需要从ehlo的返回值里来确认,如果里面有starttls,才表示支持。相对于发送普通邮件的第二种方法来说,只需要新增加一行代码就可以了:

#-*- encoding: gb2312 -*-
import os, sys, string
import smtplib
import base64

# 邮件服务器地址
mailserver = "smtp.163.com"
# 邮件用户名
username = "xxxxxx@163.com"
# 密码
password = "xxxxxxx"
# smtp会话过程中的mail from地址
from_addr = "xxxxxx@163.com"
# smtp会话过程中的rcpt to地址
to_addr = "yyyyyy@163.com"
# 信件内容
msg = "my test mail"

svr = smtplib.SMTP(mailserver)
# 设置为调试模式,就是在会话过程中会有输出信息
svr.set_debuglevel(1)
# ehlo命令,docmd方法包括了获取对方服务器返回信息,如果支持安全邮件,返回值里会有starttls提示
svr.docmd("EHLO server")
svr.starttls() # <------ 这行就是新加的支持安全邮件的代码!
# auth login 命令
svr.docmd("AUTH LOGIN")
# 发送用户名,是base64编码过的,用send发送的,所以要用getreply获取返回信息
svr.send(base64.encodestring(username))
svr.getreply()
# 发送密码
svr.send(base64.encodestring(password))
svr.getreply()
# mail from, 发送邮件发送者
svr.docmd("MAIL FROM: <%s>" % from_addr)
# rcpt to, 邮件接收者
svr.docmd("RCPT TO: <%s>" % to_addr)
# data命令,开始发送数据
svr.docmd("DATA")
# 发送正文数据
svr.send(msg)
# 比如以 . 作为正文发送结束的标记
svr.send(" . ")
svr.getreply()
# 发送结束,退出
svr.quit()

注意:以上的代码为了方便我都没有判断返回值,严格说来,是应该判断一下返回的代码的,在smtp协议中,只有返回代码是2xx或者3xx才能继续下一步,返回4xx或5xx的,都是出错了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python使用稀疏矩阵节省内存实例
Jun 27 Python
Python三元运算实现方法
Jan 12 Python
玩转python爬虫之URLError异常处理
Feb 17 Python
Python 常用的安装Module方式汇总
May 06 Python
python实现windows下文件备份脚本
May 27 Python
Django实现分页功能
Jul 02 Python
Python脚本利用adb进行手机控制的方法
Jul 08 Python
python分布式编程实现过程解析
Nov 08 Python
Python3和pyqt5实现控件数据动态显示方式
Dec 13 Python
详细分析Python垃圾回收机制
Jul 01 Python
django序列化时使用外键的真实值操作
Jul 15 Python
python爬不同图片分别保存在不同文件夹中的实现
Apr 02 Python
Pipenv一键搭建python虚拟环境的方法
May 22 #Python
PyTorch线性回归和逻辑回归实战示例
May 22 #Python
python自动查询12306余票并发送邮箱提醒脚本
May 21 #Python
python利用smtplib实现QQ邮箱发送邮件
May 20 #Python
用Python下载一个网页保存为本地的HTML文件实例
May 21 #Python
Python读取本地文件并解析网页元素的方法
May 21 #Python
详解Python中的四种队列
May 21 #Python
You might like
CodeIgniter php mvc框架 中国网站
2008/05/26 PHP
php内嵌函数用法实例
2015/03/20 PHP
微信API接口大全
2015/04/15 PHP
学习YUI.Ext 第三天
2007/03/10 Javascript
分享Javascript实用方法二
2015/12/13 Javascript
果断收藏9个Javascript代码高亮脚本
2016/01/06 Javascript
Javascript 事件冒泡机制详细介绍
2016/10/10 Javascript
JavaScript制作简单的框选图表
2017/05/15 Javascript
angular动态删除ng-repaeat添加的dom节点的方法
2017/07/20 Javascript
详解Vue中一种简易路由传参办法
2017/09/15 Javascript
详解node服务器中打开html文件的两种方法
2017/09/18 Javascript
web页面和微信小程序页面实现瀑布流效果
2018/09/26 Javascript
如何用RxJS实现Redux Form
2018/12/29 Javascript
layui动态加载多表头的实例
2019/09/05 Javascript
微信小程序音乐播放器开发
2019/11/20 Javascript
[00:47]DOTA2荣耀之路6:天火,天火!
2018/05/30 DOTA
python数据处理实战(必看篇)
2017/06/11 Python
手把手教你用python抢票回家过年(代码简单)
2018/01/21 Python
opencv实现图片模糊和锐化操作
2018/11/19 Python
python使用pymongo操作mongo的完整步骤
2019/04/13 Python
Django 静态文件配置过程详解
2019/07/23 Python
python并发编程多进程 模拟抢票实现过程
2019/08/20 Python
tensorflow 实现从checkpoint中获取graph信息
2020/02/10 Python
利用python中集合的唯一性实现去重
2020/02/11 Python
Nili Lotan官网:Nili Lotan同名品牌
2018/01/07 全球购物
迎新晚会邀请函
2014/02/01 职场文书
干部下基层实施方案
2014/03/14 职场文书
企业安全生产责任书范本
2014/07/28 职场文书
领导干部学习“三严三实”思想汇报
2014/09/15 职场文书
乡镇党的群众路线对照检查材料
2014/09/24 职场文书
2014年质检员工作总结
2014/11/18 职场文书
2014年度思想工作总结
2014/11/27 职场文书
初中作文评语
2014/12/25 职场文书
2015年检验员工作总结范文
2015/04/30 职场文书
实用求职信模板范文
2019/05/13 职场文书
MySQL中int (10) 和 int (11) 的区别
2022/01/22 MySQL