python小白学习笔记

AJ Kipper:实现邮箱发送验证码功能

2016-04-09  本文已影响368人  麻瓜编程

前言
大家在注册一个产品的时候,一定有这么一个相似的场景:输入用户名,密码,邮箱,点击确认。然后对方服务器向你的邮箱发一个链接或者验证码,你点击邮箱中的链接或者输入验证码,注册成功。而这一篇文章便是利用Python邮箱服务模块来实现这么一个功能,用的是验证码认证。
个人推荐
做一个项目(不管多小)之前,首先要了解相应的概念和领域。比如涉及新的网络协议,相关算法等等。如果会涉及一个新的Python模块,除了看别人代码讲解或者官方文档,最好去查看一下帮助或者Python源码,因为你看别人的代码或者官方文档,你只会明白可以这么用,但是你却不知道为什么这么用,或者说你不知道还可以怎么用。我想在这篇文章中分享怎么利用查看源码来了解模块的使用。
背景知识
了解邮件服务协议SMTP操作流程
Python语言提供的库太方便了,基本涉及了各个方面,所以,要实现这个功能,并不复杂,Python在邮箱方面的服务有两个模块,一个是smtplib模块,另一个是email模块。
email模块
这个模块主要用来构造邮件内容。毕竟最基本的邮件包括主题,来源,文本内容,更复杂的还有有附件,html格式等等。这里介绍最简单的,也就是纯文本邮件(毕竟只是发个验证码)。
构造纯文本文件要用到email.mime.text这个模块里面的MIMEText()方法,下面是官方文档的截图
官方文档:https://docs.python.org/2/library/email.mime.html

大家英语都很厉害,我就不翻译了哈。
email.mime.textMIMEText(_text,_subtype[,_charset])中,_text是邮件内容,_subtype是邮件类型,默认是普通类型,值是plain,_charest是编码类型,默认是’us-ascii’,中文文本要加上’utf-8′,’gb2312’等。
或者可以在Python解释器下导入MIMEText模块,用help(MIMEText)来看看这个类的初始化init函数:

构造最简单的邮件内容:

#!/usr/bin/env python
#-*-coding:utf-8-*-
#python 2.7.6
 
__author__ = 'AJ Kipper'
 
from email.mime.text import MIMEText
 
msg = MIMEText('Hello,world!','plain','utf-8')

这样,就构造了一个邮件实例msg。内容是Hello,world,类型是plain,编码用utf-8。更复杂的邮件构造请自己去写吧。
smtplib模块
这个模块用来发送邮件。首先,在任何语言编程中,编写邮件服务都是需要协议的。我知道的有POP3,SMTP,IMAP,这三个协议都属于应用层,具体的功能和区别可以自己去了解。这里用的是SMTP协议,当然,不用自己去写协议,Python的smtplib模块已经做了很方便的封装,不然你还得自己编写协议,发送还要涉及socket连接。(其实源码中smtplib.py这个文件就是在编写一个SMTP协议)
查看源码中,我发现smtplib.py中有一个SMTP类,看来里面实现了SMTP协议,并且提供了很多方法。下面是SMTP这个类的初始化init截图:



![](http:https://img.haomeiwen.com/i1692820/a482b2fc2c0d5bd3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

你看,参数有四个,第一个是string类型host,默认是空。第二个是int类型port,默认是0。第三个我估计是测试用的local_hostname,默认是None。第四个是设置socket连接的timeout,默认是socket中的timeout。还有个小细节,你看下面if host:这个代码段,调用了本类中connect( )方法,还传入host和port参数。说明connect函数是进行SMTP协议中的socket连接,连接成功的状态码code是220,那么如果不等于220,当然就返回服务器连接错误信息。所以,在进行调用的时候,你可以直接在实例化SMTP对象的时候传入参数,也可以先不传入参数,而调用SMTP.connect( )方法来传入参数。看下面代码
```
#!/usr/bin/env python
#-*-coding:utf-8-*-
#python 2.7.6
 
__author__ = 'AJ Kipper'
 
from smtplib import SMTP
#Just for test
host = 'smtp.xxx.com'
port = 0
 
#第一种连接方法
server = SMTP(host,port)
 
#第二种连接方法
server = SMTP()
server.connect(host,port)
```


两种接连方法是等价的。
然后再来看看connect( )这个函数源码:

![](http:https://img.haomeiwen.com/i1692820/bc386045b4f50457.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

相信大家还是能大概理解这个函数里面的代码。我读完之后,得出几个信息,1.host字符串还可以直接加: port形成一个URL来作为host参数,因为这个函数里面有正则表达式解析代码段;2.port有一个默认值是default_port,而值就在源码里面,因为用的是self.引用,结果发现默认port = 25。噢,我突然醒悟,确实,SMTP协议默认端口号就是25。接下来调用两个函数,一个是_get_socket( ),一个是getreply( ),注意,如果你对Python面对对象编程很熟悉的话,你肯定知道函数名前面加一个_是代表什么意思。比如这里,为什么不是get_socket( ),而是_get_socket( )?想知道原因,可以用关键词【Python 私有变量】去搜索引擎查一下,或者看一下Python的编码风格标准PEP 0008:
[https://www.python.org/dev/peps/pep-0008/](https://www.python.org/dev/peps/pep-0008/)
,下面是文档截图:

![](http:https://img.haomeiwen.com/i1692820/e0c82cb31cc27829.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

在Python语言中,并没有private或者public之类的关键字来修饰成员变量,而是通过变量名加下划线来区分。变量前面加一个_说明这个变量不能用from xxx import 导入;变量前面加两个_说明这个变量只能在本类中调用。具体可以自己去尝试,现在还是回到源码本身。
实例化之后,实例化对象server就具有了这个类的全部方法。
**SMTP流程**
如果只是想完成这么一个项目,那其实不懂原理也是可行的,毕竟代码就那几行,也不难。但如果因为一个项目,从而熟悉了SMTP协议,那其实这才是最大的收获。所以,去看看SMTP协议的规定,也是非常必要的。维基百科里面说SMTP最新修订在FRC1869里,以下是链接:[https://tools.ietf.org/html/rfc1869](https://tools.ietf.org/html/rfc1869)。
要了解SMTP的使用,那么可以通过SMTP协议的命令,在终端直接连接邮件服务器来看看SMTP的操作流程,我选的服务器是微软Outlook的,如下:
[](http://cn.micronoob.com/wp-content/uploads/sites/4/2016/04/Screen-Shot-2016-04-07-at-19.41.20.png)

![](http:https://img.haomeiwen.com/i1692820/644172d47b88423b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

第一条命令[telnet smtp-mail.outlook.com 25]是用telnet来尝试连接outlook的邮件服务器,服务器地址在outlook的官网,一查就有,端口是25。服务器回复代码是220,连接成功。
第二条命令HELO是表名身份,后面的字符串可以随便填写。这是为了告诉服务器一些客户端机器的信息。服务器返回250,成功Say Hello。
第三条命令auth login,我要进行用户登录的时候,提示Must issue a STARTTLS comand first,说明,服务器要求在登录之前必须要使用TLS加密。
好,然后我再次按照提示连接的时候,服务器竟然自己关闭了。这里我也不懂为啥,懂的朋友可以告诉我原因,如下:
[](http://cn.micronoob.com/wp-content/uploads/sites/4/2016/04/Screen-Shot-2016-04-07-at-19.46.44.png)

![](http:https://img.haomeiwen.com/i1692820/e356db1a97f7471e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

然后,我换一个邮件服务器试试,用的是163邮箱,发现不用STARTTLS命令,也连接成功了。不过我没有163邮箱,所以没有进行登录,如下:
[](http://cn.micronoob.com/wp-content/uploads/sites/4/2016/04/Screen-Shot-2016-04-07-at-19.50.27.png)

![](http:https://img.haomeiwen.com/i1692820/84b686e753eac83c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

经过这么一番周折,大概了解了SMTP操作步骤,一般是这样的顺序:连接服务器  → 身份验证 → 选择加密传输方式 →登录 → 操作 → 断开连接。
所以,可以去看看源码,是否有这些功能的函数存在。研究一段时间之后,你会发现源码SMTP类中存在login( ),starttls( ),helo( ),sendmail( ),close( )等函数,然后按照步骤调用就行,我是都去看了源码,但不在这里讲了,通过刚才connect( )函数的例子,相信你已经明白怎么通过查看源码来学习使用刚接触的函数。
下面是一个最基本的流程:
```
#!/usr/bin/env python
#-*-coding: utf-8-*-
#python 2.7.6
 
'Email server module'
 
__author__ = 'AJ Kipper'
 
import smtplib
smtp_server = 'smtp-mail.outlook.com:25'
 
server = smtplib.SMTP(smtp_server)
server.helo()
server.starttls()
server.login(mail_account,mail_password)
server.sendmail('邮件内容')
server.close()
```

**总结**
到这里,这个项目最核心的东西都说完了。就两方面,一个是email.mime.text里MIMEText的邮件构造,二是smtplib模块SMTP类的使用,其他的不过就是一些基本的Python知识,比如产生伪随机数发送给目标邮箱进行验证,函数编写和调用等等,毕竟还是要自己去尝试和思考,接着才是去看我的源码,源码在我的github:[https://github.com/AJKipper/PythonScript/tree/master/SMTP_Server](https://github.com/AJKipper/PythonScript/tree/master/SMTP_Server),下面也贴出来。
**源码**
源码有两个文件,一个是emailserver.py模块,用来编写邮件和发送邮件。另一个是register.py模块,调用emailserver模块进行随机数验证。
**emailserver.py**
```
#!/usr/bin/env python
#-*-coding: utf-8-*-
#python 2.7.6
 
 
'Email server module'
 
__author__ = 'AJ Kipper'
 
import smtplib
from email.mime.text import MIMEText
 
 
class email_server:
 def __init__(self,user_email,code):
 #设置邮件服务地址及默认端口号,这里选择的是outlook邮箱
 self.smtp_server = 'smtp-mail.outlook.com:25'
                #设置发送来源的邮箱地址
 self.mail_account = 'Your email account'
 self.mail_passwd = 'Your email password'
                self.user_email = user_email
                #发件箱的后缀m
 self.mail_postfix = 'outlook.com'
                #subject代表邮件主题信息
 self.subject = 'Email authentication from AJ Kipper'
 self.form_msg = "This is a mailbox validation from" + "<" + self.mail_account + "@" + self.mail_postfix + ">"
 self.content = 'Hi,thank you for registering the chat room created by AJ Kipper!\nYour email authentication code is ' + code
        def send_email(self):
                global send_status
                #普通文本邮件
         msg = MIMEText(self.content,_subtype='plain',_charset='utf-8')  
         msg['Subject'] = self.subject
         msg['From'] = self.form_msg  
         msg['To'] = ";".join(self.user_email)
         try:
         server = smtplib.SMTP(self.smtp_server)  
         #server.connect(self.smtp_server)
                        #返回服务器特性
         server.ehlo()
                        #进行TLS安全传输
         server.starttls()
         server.login(self.mail_account,self.mail_passwd)  
         server.sendmail(self.form_msg, self.user_email, msg.as_string())  
         server.close() 
                        return True
         except Exception, error:  
         print str(error)  
         return False
if __name__ == '__main__':
 email_server()
```
**register.py**
```
#!/usr/bin/env python
#-*-coding: utf-8-*-
#python 2.7.6
 
 
'register server module'
 
__author__ = 'AJ Kipper'
from random import randint
import emailserver
 
class register:
 def __init__(self,reg_email):
 #创建一个四个号码的伪随机数作为验证码
 code = randint(1000,2000)
 self.reg_email = reg_email
 self.code = str(code)
 self.send_status = False
 def verify(self):
 email = emailserver.email_server(self.reg_email,self.code)
 if email.send_email():
 print "Already sent!"
 else:
 print "Fail in send!"
 self.get_code()
 def get_code(self):
 code = raw_input('code:')
 while code != self.code:
 print 'Verification code is incorrect!'
 code = raw_input('code:')
 print "Verify success"
 
if __name__ == '__main__':
 #要发送邮箱地址
 test = register(['要发送的邮箱地址'])
 test.verify()
```
上一篇 下一篇

猜你喜欢

热点阅读