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实现保存网页到本地示例
Mar 16 Python
python语言使用技巧分享
May 31 Python
浅谈pyhton学习中出现的各种问题(新手必看)
May 17 Python
python使用 HTMLTestRunner.py生成测试报告
Oct 20 Python
对YOLOv3模型调用时候的python接口详解
Aug 26 Python
python生成器/yield协程/gevent写简单的图片下载器功能示例
Oct 28 Python
python 变量初始化空列表的例子
Nov 28 Python
pytorch中使用cuda扩展的实现示例
Feb 12 Python
python GUI库图形界面开发之PyQt5信号与槽基本操作
Feb 25 Python
python GUI库图形界面开发之PyQt5浏览器控件QWebEngineView详细使用方法
Feb 26 Python
在django中查询获取数据,get, filter,all(),values()操作
Aug 09 Python
Sentry的安装、配置、使用教程(Sentry日志手机系统)
Jul 23 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
深入理解用mysql_fetch_row()以数组的形式返回查询结果
2013/06/05 PHP
Codeigniter(CI)框架分页函数及相关知识
2014/11/03 PHP
CentOS6.5 编译安装lnmp环境
2014/12/21 PHP
浅析PHP文件下载原理
2014/12/25 PHP
php实现从上传文件创建缩略图的方法
2015/04/02 PHP
PHP+Mysql分布式事务与解决方案深入理解
2021/02/27 PHP
基于JQuery的简单实现折叠菜单代码
2010/09/15 Javascript
JQuery页面的表格数据的增加与分页的实现
2013/12/10 Javascript
如何从jQuery的ajax请求中删除X-Requested-With
2013/12/11 Javascript
jquery自动填充勾选框即把勾选框打上true
2014/03/24 Javascript
javascript里使用php代码实例
2014/12/13 Javascript
浅谈JavaScript中的Math.atan()方法的使用
2015/06/14 Javascript
全面解析Javascript无限添加QQ好友原理
2016/06/15 Javascript
js 获取范围内的随机数实例代码
2016/08/02 Javascript
KnockoutJS 3.X API 第四章之表单textInput、hasFocus、checked绑定
2016/10/11 Javascript
JS实现滑动门效果的方法详解
2016/12/19 Javascript
vue代理和跨域问题的解决
2018/07/18 Javascript
vue用BMap百度地图实现即时搜索功能
2019/09/26 Javascript
稍微学一下Vue的数据响应式(Vue2及Vue3区别)
2019/11/21 Javascript
JavaScript运行机制实例分析
2020/04/11 Javascript
使用Python编写一个在Linux下实现截图分享的脚本的教程
2015/04/24 Python
python执行get提交的方法
2015/04/29 Python
python集合比较(交集,并集,差集)方法详解
2018/09/13 Python
十个Python练手的实战项目,学会这些Python就基本没问题了(推荐)
2019/04/26 Python
Python opencv实现人眼/人脸识别以及实时打码处理
2019/04/29 Python
Pandas之DataFrame对象的列和索引之间的转化
2019/06/25 Python
python 带时区的日期格式化操作
2020/10/23 Python
消防安全汇报材料
2014/02/08 职场文书
会计专业求职信范文
2014/03/16 职场文书
基层党建工作汇报材料
2014/08/15 职场文书
初中语文教学随笔
2015/08/15 职场文书
大学生团支书竞选稿
2015/11/21 职场文书
MySQL主从复制断开的常用修复方法
2021/04/07 MySQL
只用Python就可以制作的简单词云
2021/06/07 Python
MySQL常见优化方案汇总
2022/01/18 MySQL
Axios代理配置及封装响应拦截处理方式
2022/04/07 Vue.js