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脚本来获取mp3文件tag信息的教程
May 04 Python
python查看FTP是否能连接成功的方法
Jul 30 Python
批处理与python代码混合编程的方法
May 19 Python
Python爬虫DOTA排行榜爬取实例(分享)
Jun 13 Python
python实现发送邮件功能代码
Dec 14 Python
python 按照固定长度分割字符串的方法小结
Apr 30 Python
PyCharm代码整体缩进,反向缩进的方法
Jun 25 Python
对Python中class和instance以及self的用法详解
Jun 26 Python
python 实现多维数组转向量
Nov 30 Python
Python获取、格式化当前时间日期的方法
Feb 10 Python
解决PyCharm不在run输出运行结果而不是再Console里输出的问题
Sep 21 Python
Python 中random 库的详细使用
Jun 03 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
通过对服务器端特性的配置加强php的安全
2006/10/09 PHP
如何对PHP程序中的常见漏洞进行攻击(下)
2006/10/09 PHP
追求程序速度,而不是编程的速度
2008/04/23 PHP
鸡肋的PHP单例模式应用详解
2013/06/03 PHP
php递归获取目录内文件(包含子目录)封装类分享
2013/12/25 PHP
Laravel5.1框架注册中间件的三种场景详解
2019/07/09 PHP
jQuery文本框(input textare)事件绑定方法教程
2013/04/24 Javascript
js的正则test,match,exec详细解析
2014/01/29 Javascript
基于jQuery实现最基本的淡入淡出效果实例
2015/02/02 Javascript
详解Bootstrap创建表单的三种格式(一)
2016/01/04 Javascript
AngularJS 自定义过滤器详解及实例代码
2016/09/14 Javascript
jQuery解析XML 详解及方法总结
2016/09/28 Javascript
EasyUI Datebox 日期验证之开始日期小于结束时间
2017/05/19 Javascript
React入门教程之Hello World以及环境搭建详解
2017/07/11 Javascript
详解Node.js模板引擎Jade入门
2018/01/19 Javascript
nodejs更改项目端口号的方法
2018/05/13 NodeJs
vue项目中使用Svg的方法
2018/10/24 Javascript
如何实现双向绑定mvvm的原理实现
2019/05/28 Javascript
Vue数据双向绑定原理实例解析
2020/05/15 Javascript
[03:31]DOTA2英雄基础教程 大地之灵
2013/12/17 DOTA
Python的Flask框架的简介和安装方法
2015/11/13 Python
python爬取w3shcool的JQuery课程并且保存到本地
2017/04/06 Python
Python数据分析之获取双色球历史信息的方法示例
2018/02/03 Python
python3 爬取图片的实例代码
2018/11/06 Python
PIL图像处理模块paste方法简单使用详解
2019/07/17 Python
python利用tkinter实现屏保
2019/07/30 Python
Python @property装饰器原理解析
2020/01/22 Python
浅谈tensorflow中dataset.shuffle和dataset.batch dataset.repeat注意点
2020/06/08 Python
Python连接mysql方法及常用参数
2020/09/01 Python
基于canvas使用贝塞尔曲线平滑拟合折线段的方法
2018/01/10 HTML / CSS
英国足球店:UK Soccer Shop
2017/11/19 全球购物
瑜伽灵感珠宝:Satya Jewelry
2018/01/06 全球购物
西安启天科技有限公司网络工程师面试题笔试题
2016/06/12 面试题
护理专业推荐信
2013/11/07 职场文书
2014年助理工程师工作总结
2014/11/14 职场文书
手把手教你怎么用Python实现zip文件密码的破解
2021/05/27 Python