2 Selenium Python 框架基础
1 单元测试
1.1 什么是单元测试?
单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。
对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类。
总的来说,单元就是人为规定的最小的被测功能模块。
单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。
1.2 名词解释
单元测试主要包含概念如下:test case、test suite、test runner、test fixture
- test case:
一个TestCase的实例就是一个测试用例。
什么是测试用例呢?就是一个完整的测试流程,包括测试前准备环境的搭建(setUp),执行测试代码(run),以及测试后环境的还原(tearDown)。
- test suite:
而多个测试用例集合在一起,就是TestSuite,而且TestSuite也可以嵌套TestSuite。
TestLoader是用来加载TestCase到TestSuite中的,其中有几个loadTestsFrom__()方法,就是从各个地方寻找TestCase,创建它们的实例,
然后add到TestSuite中,再返回一个TestSuite实例。
- test runner:
TextTestRunner是来执行测试用例的,其中的run(test)会执行TestSuite/TestCase中的run(result)方法。
测试的结果会保存到TextTestResult实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息。
-
test fixture:
对一个测试用例环境的搭建和销毁,是一个fixture,通过覆盖TestCase的setUp()和tearDown()方法来实现。
这个有什么用呢?比如说在这个测试用例中需要访问数据库,那么可以在setUp()中建立数据库连接以及进行一些初始化,在tearDown()中清除在数据库中产生的数据,
然后关闭连接。注意tearDown的过程很重要,要为以后的TestCase留下一个干净的环境。
1.3 如何单元测试
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #被测类 2 class Index(object): 3 4 def login(self , username , password): 5 if username == 'root' and password == '123456': 6 print '登录成功 !' 7 else: 8 print '用户名或密码错误 !' 9 10 #单元测试 11 import unittest2 12 13 class TestLogin(unittest2.TestCase): 14 index = Index() 15 16 def testLoginSuccess(self): 17 self.index.login('root', '123456') 18 19 def testLoginUsernameError(self): 20 self.index.login('aaron', '123456') 21 print '--用户名错误' 22 23 def testLoginPasswordError(self): 24 self.index.login('root', '654321') 25 print '--密码错误' 26 27 #执行测试 28 if __name__ == '__main__': 29 unittest2.main() 30 31 #结果 32 用户名或密码错误 ! 33 --密码错误 34 登录成功 ! 35 用户名或密码错误 ! 36 --用户名错误
1.4 执行测试的不同方法
- 使用unittest.main()执行测试用例
- 使用testsuit来执行测试用例
- 使用TestLoader构建suite来执行测试用例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #被测类 2 class Index(object): 3 4 def login(self , username , password): 5 if username == 'root' and password == '123456': 6 print '登录成功 !' 7 else: 8 print '用户名或密码错误 !' 9 def search(self , item): 10 if item == 'Python': 11 print '搜索成功 !' 12 else: 13 print '搜索失败 !' 14 15 #单元测试 16 import unittest2 17 18 class TestLogin(unittest2.TestCase): 19 index = Index() 20 21 def testLoginSuccess(self): 22 self.index.login('root', '123456') 23 24 def testLoginUsernameError(self): 25 self.index.login('aaron', '123456') 26 print '--用户名错误' 27 28 def testLoginPasswordError(self): 29 self.index.login('root', '654321') 30 print '--密码错误' 31 32 class TestSearch(unittest2.TestCase): 33 index = Index() 34 35 def testSearchSuccess(self): 36 self.index.search('Python') 37 38 def testSearchFail(self): 39 self.index.search('Java') 40 41 #使用unittest.main()执行测试用例------ 42 if __name__ == '__main__': 43 unittest2.main() 44 45 #使用addTest构建suite来执行测试用例------ 46 if __name__ == '__main__': 47 #以测试用例为单位构造测试集 48 #TestSuit:组织测试用例的实例,支持测试用例的添加和删除,最终将传递给 testRunner进行测试执行 49 suite = unittest2.TestSuite() 50 suite.addTest(TestLogin.testLoginSuccess()) 51 suite.addTest(TestLogin.testLoginUsernameError()) 52 suite.addTest(TestLogin.testLoginPasswordError()) 53 #执行测试 54 #TextTestRunner:进行测试用例执行的实例,其中Text的意思是以文本形式显示测试结果。 55 #测试的结果会保存到TextTestResult实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息 56 runner = unittest2.TextTestRunner() 57 runner.run(suite) 58 59 #使用TestLoader构建suite来执行测试用例------ 60 if __name__ == '__main__': 61 #以测试类为单位构造测试集 62 #TestLoader:用来加载TestCase到TestSuite中 63 #其中有几个 loadTestsFromXX()方法,就是从各个地方寻找TestCase,创建它们的实例 64 #然后add到TestSuite中,再返回一个TestSuite实例; 65 suite1 = unittest2.TestLoader().loadTestsFromTestCase(TestLogin) 66 suite2 = unittest2.TestLoader().loadTestsFromTestCase(TestSearch) 67 suite = unittest2.TestSuite([suite1 , suite2]) 68 unittest2.TextTestRunner(verbosity=2).run(suite)
1.5 测试执行顺序
- setUpClass:类中所有用例执行前执行一次
- tearDownClass:类中所有用例执行后执行一次
- setUp:每个用例执行前执行一次
- tearDown:每个用例执行后执行一次
- 测试用例执行顺序:用例名称test后面按字母顺序执行
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import unittest2 2 3 class TestDemo(unittest2.TestCase): 4 @classmethod 5 def setUpClass(self): 6 print 'setUpClass' 7 @classmethod 8 def tearDownClass(self): 9 print 'tearDownClass' 10 def setUp(self): 11 print 'setUp' 12 def tearDown(self): 13 print 'tearDown' 14 def testLogin(self): 15 print 'login' 16 def testAdd(self): 17 print 'add' 18 def testUpdate(self): 19 print 'update' 20 def testLogout(self): 21 print 'logout' 22 23 #使用unittest.main()执行测试用例------ 24 if __name__ == '__main__': 25 unittest2.main() 26 27 #结果 28 setUpClass 29 setUp 30 add 31 tearDown 32 setUp 33 login 34 tearDown 35 setUp 36 logout 37 tearDown 38 setUp 39 update 40 tearDown 41 tearDownClass
2 测试报告
2.1 测试报告
报告名称:HTMLTestRunner为单元测试报告
下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html
使用方法:HTMLTestRunner.py文件放在C:\Python27\Lib下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #被测类 2 import unittest2 3 import time 4 import HTMLTestRunner 5 6 class TestDemo(unittest2.TestCase): 7 @classmethod 8 def setUpClass(self): 9 print 'setUpClass' 10 @classmethod 11 def tearDownClass(self): 12 print 'tearDownClass' 13 def testLogin(self): 14 print 'login' 15 def testAdd(self): 16 print 'add' 17 def testUpdate(self): 18 print 'update' 19 def testLogout(self): 20 print 'logout' 21 22 #测试类 23 import time 24 import HTMLTestRunner 25 from testcase.demo5 import * 26 27 #使用addTest构建suite来执行测试用例------ 28 suite = unittest2.TestSuite() 29 suite.addTest(TestDemo('testAdd')) 30 #报告准备 31 reportName = time.strftime('%Y%m%d%H%M%S') #文件名 32 fp = open('../' + reportName + '.html' , 'wb') #文件路径 33 runner = HTMLTestRunner.HTMLTestRunner(stream = fp , #定义报告 34 title = '测试报告' , 35 description = '自动化测试报告Demo') 36 #执行测试 37 result = runner.run(suite) 38 #关闭文件 39 fp.close() 40 print result 41 42 #测试结果 43 Finding files... done. 44 Importing test modules ... setUpClass 45 tearDownClass 46 . 47 Time Elapsed: 0:00:00 48 <HTMLTestRunner._TestResult run=1 errors=0 failures=0>
2.2 文档注释
python在注释中有一个非常有用的东西是 doc String ,它可以用于模块、函数和类的描述。
使用三个双引号或三个单引号进行注释。
文档字符串的惯例是一个多行字符串,它的首行以大写字母开始,句号结尾。第二行是空行,从第三行开始是详细的描述。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #被测类 2 #!/usr/bin/env python 3 # coding=utf-8 4 5 import unittest2 6 import time 7 import HTMLTestRunner 8 9 class TestDemo(unittest2.TestCase): 10 '''(测试Demo类)''' 11 @classmethod 12 def setUpClass(self): 13 print 'setUpClass' 14 @classmethod 15 def tearDownClass(self): 16 print 'tearDownClass' 17 18 def testLogin(self): 19 """(登录)""" 20 print 'login' 21 22 def testAdd(self): 23 '''(新增)''' 24 print 'add' 25 26 def testUpdate(self): 27 '''(修改)''' 28 print 'update' 29 30 def testLogout(self): 31 '''(退出)''' 32 print 'logout' 33 34 #测试类 35 #!/usr/bin/env python 36 # coding=utf-8 37 38 import time 39 import HTMLTestRunner 40 from testcase.demo5 import * 41 42 #使用addTest构建suite来执行测试用例------ 43 suite = unittest2.TestSuite() 44 suite.addTest(TestDemo('testLogin')) 45 suite.addTest(TestDemo('testAdd')) 46 suite.addTest(TestDemo('testUpdate')) 47 suite.addTest(TestDemo('testLogout')) 48 #报告准备 49 reportName = time.strftime('%Y%m%d%H%M%S') #文件名 50 fp = open('../' + reportName + '.html' , 'wb') #文件路径 51 runner = HTMLTestRunner.HTMLTestRunner(stream = fp , #定义报告 52 title = '测试报告' , 53 description = '自动化测试报告Demo') 54 #执行测试 55 result = runner.run(suite) 56 #关闭文件 57 fp.close() 58 print result
3 配置文件
3.1 配置文件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #配置selenium 2 [selenium] 3 browser = firefox 4 timeout = 10
3.2 操作配置文件
Python标准库的ConfigParser模块提供一套API操作配置文件。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!/usr/bin/env python 2 # coding=utf-8 3 4 import ConfigParser 5 6 class Configer(object): 7 8 #构造函数,初始化ConfigParser类,并读取config文件 9 def __init__(self , filePath): 10 self.conf = ConfigParser.ConfigParser() 11 self.conf.read(filePath) 12 13 #获取key的value 14 def getConf(self , section , key): 15 result = self.conf.get(section, key) 16 return result
3.3 配置文件解析
将配置文件中内容解析出来方便使用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!/usr/bin/env python 2 # coding=utf-8 3 4 from libs.Configer import * 5 6 class SelConf(object): 7 conf = Configer("../config/selenium.conf") 8 browser = conf.getConf('selenium', 'browser') 9 timeout = conf.getConf('selenium', 'timeout')
3.4 配置文件使用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!/usr/bin/env python 2 # coding=utf-8 3 4 from config.SeleniumConf import * 5 6 if __name__ == '__main__': 7 print SelConf.browser 8 9 #结果 10 firefox
4 日志
4.1 logging模块四大类
python通过logging模块提供简单易用、且功能强大的日志功能。logging模块四大类如下:
- logger:提供了应用程序可以直接使用的接口
- handler将(logger创建的)日志发送到指定的输出路径
- filter决定输出哪条日志
- formatter决定日志的最终输出格式
4.1.1 logger类
每个程序在输出信息之前都要通过 logging.getlogger() 来获得一个log实例
源码如下:
def getLogger(name=None):
"""
Return a logger with the specified name, creating it if necessary.
If no name is specified, return the root logger.
"""
if name:
return Logger.manager.getLogger(name)
else:
return root
logger的level有以下几个级别:NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL
如果把looger的级别设置为INFO, 那么小于INFO级别的日志都不输出, 大于等于INFO级别的日志都输出
4.1.2 handlers
handler对象负责发送相关的信息到指定目的地。如:控制台、文件、网络等
4.1.3 Formatters
Formatter对象设置日志信息格式,默认的时间格式为%Y-%m-%d %H:%M:%S
关于formatter的配置,采用的是%(<dict key>)s的形式,就是字典的关键字替换。提供的关键字包括:
- %(name)s Name of the logger (logging channel).
- %(levelno)s Numeric logging level for the message (DEBUG, INFO, WARNING, ERROR, CRITICAL).
- %(levelname)s Text logging level for the message ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL').
- %(pathname)s Full pathname of the source file where the logging call was issued (if available).
- %(filename)s Filename portion of pathname.
- %(module)s Module (name portion of filename).
- %(funcName)s Name of function containing the logging call.
- %(lineno)d Source line number where the logging call was issued (if available).
- %(created)f Time when the LogRecord was created (as returned by time.time()).
- %(relativeCreated)d Time in milliseconds when the LogRecord was created, relative to the time the logging module was loaded.
- %(asctime)s Human-readable time when the LogRecord was created. By default this is of the form “2003-07-08 16:49:45,896” (the numbers after the comma are millisecond portion of the time).
- %(msecs)d Millisecond portion of the time when the LogRecord was created.
- %(thread)d Thread ID (if available).
- %(threadName)s Thread name (if available).
- %(process)d Process ID (if available).
- %(message)s The logged message, computed as msg % args.
4.1.4 TimeRotatingFileHandler
TimeRotatingFileHandler根据时间日志文件的记录。
格式:TimedRotatingFileHandler(filename [,when [,interval [,backupCount]]])
- filename 是输出日志文件名的前缀
- when 是一个字符串的定义如下:
“S”: Seconds
“M”: Minutes
“H”: Hours
“D”: Days
“W”: Week day (0=Monday)
“midnight”: Roll over at midnight
- interval 是指等待多少个单位when的时间后,Logger会自动重建文件
- backupCount 是保留日志个数。默认的0是不会自动删除掉日志。若设10,则在文件的创建过程中库会判断是否有超过这个10,若超过,则会从最先创建的开始删除。
4.2 实战
4.2.1 log配置文件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 ############################################### 2 #root 必须有 3 #propagate 是否继承父类的log信息,0:否 1:是 4 #propagate 默认1,如果设置为1会打印两次 5 #handlers 对应下面handler 6 #qualname getLogger时输入的名字 7 ############################################### 8 9 [loggers] 10 keys = root,errorLogger 11 12 [logger_root] 13 level = ERROR 14 handlers = errorHandler 15 16 [logger_errorLogger] 17 level = ERROR 18 propagate = 0 19 handlers = errorHandler 20 qualname = errorLogger 21 22 ############################################### 23 #按照时间的滚动方式记录日志 24 #formatter 对应下面日志格式 25 ############################################### 26 27 [handlers] 28 keys = errorHandler 29 30 [handler_errorHandler] 31 class=logging.handlers.TimedRotatingFileHandler 32 level=ERROR 33 args = ('../logs/selenium.log','D',1,5,) 34 formatter = errorFmt 35 36 ############################################### 37 #日志格式 38 ############################################### 39 40 [formatters] 41 keys = errorFmt 42 43 [formatter_errorFmt] 44 class = logging.Formatter 45 format = %(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - [line:%(lineno)d] - %(message)s 46 datefmt = %Y-%m-%d %H:%M:%S
4.2.2 初始化文件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!/usr/bin/python 2 # coding=utf8 3 4 import ConfigParser 5 import logging 6 import logging.config 7 8 class Logger(object): 9 10 @staticmethod 11 def append(): 12 logging.config.fileConfig('../config/logger.conf') 13 append = logging.getLogger("errorLogger") 14 return append
4.2.3 测试
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!/usr/bin/python 2 # coding=utf8 3 4 from libs.Logger import * 5 6 def testLog(): 7 Logger.append().error('error11111') 8 9 def testLog2(): 10 Logger.append().error('error22222') 11 12 if __name__ == '__main__': 13 testLog() 14 testLog2() 15 16 #结果 - 文件 17 selenium.log 18 selenium.log.2017-03-14 19 20 #结果 - 内容 21 2017-04-02 23:32:33 - ERROR - testConfig.py - testLog - [line:7] - error11111 22 2017-04-02 23:32:33 - ERROR - testConfig.py - testLog2 - [line:11] - error22222
5 邮件
5.1 简介
SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议
Python对SMTP支持有smtplib和email两个模块,email负责构造邮件,smtplib负责发送邮件
可以发送纯文本邮件、HTML邮件、带附件的邮件等
发送邮件时,我们一般使用第三方SMTP服务,163邮箱的SMTP服务开启步骤:进入163邮箱 - 设置 - POP3/SMTP/IMAP
POP3与SMTP区别:POP3用于接收邮件;SMTP用于发送邮件
5.1 构造邮件
email.mime:creating email and MIME objects from scratch,也就是一点点地构造一封邮件
- MIMEText对象,表示一个文本邮件
- MIMEImage对象,表示一个作为附件的图片
- MIMEMultipart对象,表示把多个对象组合起来
- MIMEBase对象,可以表示任何对象
5.2 发送文本邮件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #配置文件================ 2 [email] 3 #构造邮件 4 from_addr=XXX@163.com 5 to_addr=XXX@qq.com 6 #发送邮件 7 host=smtp.163.com 8 port=25 9 user=XXX@163.com 10 password=XXX 11 12 #解析文件================ 13 #!/usr/bin/env python 14 # coding=utf-8 15 16 from libs.Configer import * 17 18 class email(object): 19 conf = Configer("../config/email.conf") 20 #构造邮件 21 from_addr = conf.getConf('email', 'from_addr') 22 to_addr = conf.getConf('email', 'to_addr') 23 #发送邮件 24 host = conf.getConf('email', 'host') 25 port = conf.getConf('email', 'port') 26 user = conf.getConf('email', 'user') 27 password = conf.getConf('email', 'password') 28 29 #测试文件================ 30 #!/usr/bin/python 31 # coding=utf8 32 33 from libs.email import * 34 from email.mime.text import MIMEText 35 from email.header import Header 36 #构造邮件 37 msg = MIMEText('请大家自学Python...', 'plain', 'utf-8') #邮件主体 38 #不赋值msg['Subject']、msg['From']、msg['To'],会导致出现554情况 39 msg['Subject'] = Header('请大家自学Python', 'utf-8') #邮件标题 40 msg['From'] = email.from_addr #发件人 41 msg['To'] = email.to_addr #收件人 42 43 import smtplib 44 #发送邮件 45 server = smtplib.SMTP() 46 server.connect(email.host, int(email.port)) # 连接网易SMTP服务,默认端口号25 47 # server.set_debuglevel(1) #打印与SMTP服务器交互的所有信息 48 server.login(email.from_addr, email.password) #登录 49 server.sendmail(email.from_addr, email.to_addr, msg.as_string()) #发送邮件,传入msg 50 server.quit()
163退信的常见问题连接:http://help.163.com/09/1224/17/5RAJ4LMH00753VB8.html
5.3 给多人发送邮件
- 配置文件修改:to_addr=XXX@qq.com;XXX@163.com
- email解析文件修改:to_addr = conf.getConf('email', 'to_addr').split(';')
- 测试文件修改:msg['To'] = ";".join(email.to_addr) #收件人
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #配置文件============================= 2 [email] 3 #构造邮件 4 from_addr=XXX@163.com 5 to_addr=XXX@qq.com;XXX@163.com 6 #发送邮件 7 host=smtp.163.com 8 port=25 9 user=XXX@163.com 10 password=XXX 11 12 #解析文件============================= 13 #!/usr/bin/env python 14 # coding=utf-8 15 16 from libs.Configer import * 17 18 class email(object): 19 conf = Configer("../config/email.conf") 20 #构造邮件 21 from_addr = conf.getConf('email', 'from_addr') 22 to_addr = conf.getConf('email', 'to_addr').split(';') 23 #发送邮件 24 host = conf.getConf('email', 'host') 25 port = conf.getConf('email', 'port') 26 user = conf.getConf('email', 'user') 27 password = conf.getConf('email', 'password') 28 29 #测试文件============================= 30 #!/usr/bin/python 31 # coding=utf8 32 33 from libs.email import * 34 from email.mime.text import MIMEText 35 from email.header import Header 36 37 #构造邮件 38 msg = MIMEText('请大家自学Python...', 'plain', 'utf-8') #邮件主体 39 #不赋值msg['Subject']、msg['From']、msg['To'],会导致出现554情况 40 msg['Subject'] = Header('请大家自学Python', 'utf-8') #邮件标题 41 msg['From'] = email.from_addr #发件人 42 msg['To'] = ";".join(email.to_addr) #收件人 43 44 import smtplib 45 #发送邮件 46 server = smtplib.SMTP() 47 server.connect(email.host, int(email.port)) # 连接网易SMTP服务,默认端口号25 48 # server.set_debuglevel(1) #打印与SMTP服务器交互的所有信息 49 server.login(email.from_addr, email.password) #登录 50 server.sendmail(email.from_addr, email.to_addr, msg.as_string()) #发送邮件,传入msg 51 server.quit()
5.4 发送html邮件
msg = MIMEText('<html><a href="http://www.cnblogs.com/lizitest/">栗子自学Python</a></html>', 'html', 'utf-8')
注意:
- 163不支持群发html邮件
- 163邮箱需要<html>标签,其他邮箱没试过
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #配置文件================ 2 [email] 3 #构造邮件 4 from_addr=XXX@163.com 5 to_addr=XXX@qq.com 6 #发送邮件 7 host=smtp.163.com 8 port=25 9 user=XXX@163.com 10 password=XXX 11 12 #解析文件================ 13 #!/usr/bin/env python 14 # coding=utf-8 15 16 from libs.Configer import * 17 18 class email(object): 19 conf = Configer("../config/email.conf") 20 #构造邮件 21 from_addr = conf.getConf('email', 'from_addr') 22 to_addr = conf.getConf('email', 'to_addr').split(';') 23 #发送邮件 24 host = conf.getConf('email', 'host') 25 port = conf.getConf('email', 'port') 26 user = conf.getConf('email', 'user') 27 password = conf.getConf('email', 'password') 28 29 #测试文件================ 30 #!/usr/bin/python 31 # coding=utf8 32 33 from libs.email import * 34 from email.mime.text import MIMEText 35 from email.header import Header 36 37 #构造邮件 38 msg = MIMEText('<html><a href="http://www.cnblogs.com/lizitest/">栗子自学Python</a></html>', 'html', 'utf-8') 39 msg['Subject'] = Header('请大家自学Python', 'utf-8') #邮件标题 40 msg['From'] = email.from_addr #发件人 41 msg['To'] = ";".join(email.to_addr) #收件人 42 43 import smtplib 44 #发送邮件 45 server = smtplib.SMTP() 46 server.connect(email.host, int(email.port)) # 连接网易SMTP服务,默认端口号25 47 server.set_debuglevel(1) #打印与SMTP服务器交互的所有信息 48 server.login(email.from_addr, email.password) #登录 49 server.sendmail(msg['From'], msg['To'], msg.as_string()) #发送邮件,传入msg 50 server.quit()
5.5发送带附件的邮件
附件和文本均可以看作是邮件的一部分,先使用 MIMEMultipart 对象来表示邮件,再往邮件中添加正文和附件(MIMEText)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #配置文件================ 2 [email] 3 #构造邮件 4 from_addr=XXX@163.com 5 to_addr=XXX@qq.com 6 #发送邮件 7 host=smtp.163.com 8 port=25 9 user=XXX@163.com 10 password=XXX 11 12 #解析文件================ 13 #!/usr/bin/env python 14 # coding=utf-8 15 16 from libs.Configer import * 17 18 class email(object): 19 conf = Configer("../config/email.conf") 20 #构造邮件 21 from_addr = conf.getConf('email', 'from_addr') 22 to_addr = conf.getConf('email', 'to_addr').split(';') 23 #发送邮件 24 host = conf.getConf('email', 'host') 25 port = conf.getConf('email', 'port') 26 user = conf.getConf('email', 'user') 27 password = conf.getConf('email', 'password') 28 29 #测试文件================ 30 #!/usr/bin/python 31 # coding=utf8 32 33 from libs.email import * 34 from email.mime.multipart import MIMEMultipart 35 from email.mime.text import MIMEText 36 from email.header import Header 37 38 #构造邮件 39 msg = MIMEMultipart() 40 msg['Subject'] = Header('请大家自学Python', 'utf-8') #邮件标题 41 msg['From'] = email.from_addr #发件人 42 msg['To'] = ";".join(email.to_addr) #收件人 43 #添加正文 44 body = MIMEText('<html><a href="http://www.cnblogs.com/lizitest/">栗子自学Python</a></html>', 'html', 'utf-8') 45 msg.attach(body) 46 #添加附件1 47 att = MIMEText(open('../logs/selenium.log', 'rb').read(), 'base64', 'utf-8') 48 att["Content-Type"] = 'application/octet-stream' 49 att["Content-Disposition"] = 'attachment; filename="selenium.log"' 50 msg.attach(att) 51 #添加附件2 52 att = MIMEText(open('../logs/selenium.log.2017-03-30', 'rb').read(), 'base64', 'utf-8') 53 att["Content-Type"] = 'application/octet-stream' 54 att["Content-Disposition"] = 'attachment; filename="selenium.log.2017-03-30"' 55 msg.attach(att) 56 57 import smtplib 58 #发送邮件 59 server = smtplib.SMTP() 60 server.connect(email.host, int(email.port)) # 连接网易SMTP服务,默认端口号25 61 server.set_debuglevel(1) #打印与SMTP服务器交互的所有信息 62 server.login(email.from_addr, email.password) #登录 63 server.sendmail(msg['From'], msg['To'], msg.as_string()) #发送邮件,传入msg 64 server.quit()
5.6 发送带图片附件的邮件
可以使用MIMEText进行发送,可以使用MIMEImage进行发送
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #配置文件================ 2 [email] 3 #构造邮件 4 from_addr=XXX@163.com 5 to_addr=XXX@qq.com 6 #发送邮件 7 host=smtp.163.com 8 port=25 9 user=XXX@163.com 10 password=XXX 11 12 #解析文件================ 13 #!/usr/bin/env python 14 # coding=utf-8 15 16 from libs.Configer import * 17 18 class email(object): 19 conf = Configer("../config/email.conf") 20 #构造邮件 21 from_addr = conf.getConf('email', 'from_addr') 22 to_addr = conf.getConf('email', 'to_addr').split(';') 23 #发送邮件 24 host = conf.getConf('email', 'host') 25 port = conf.getConf('email', 'port') 26 user = conf.getConf('email', 'user') 27 password = conf.getConf('email', 'password') 28 29 #测试文件================ 30 #!/usr/bin/python 31 # coding=utf8 32 33 from libs.email import * 34 from email.mime.multipart import MIMEMultipart 35 from email.mime.text import MIMEText 36 from email.mime.image import MIMEImage 37 from email.header import Header 38 39 #构造邮件 40 msg = MIMEMultipart() 41 msg['Subject'] = Header('请大家自学Python', 'utf-8') #邮件标题 42 msg['From'] = email.from_addr #发件人 43 msg['To'] = ";".join(email.to_addr) #收件人 44 #添加正文 45 body = MIMEText('<html>' + 46 '<a href="http://www.cnblogs.com/lizitest/">栗子自学Python</a>' + 47 # '<p><img src="cid:report"></p>' + 48 '</html>', 'html', 'utf-8') 49 msg.attach(body) 50 #添加附件1 51 file1 = open('../logs/selenium.log', 'rb') 52 att = MIMEText(file1.read(), 'base64', 'utf-8') 53 file1.close() 54 att["Content-Type"] = 'application/octet-stream' 55 att["Content-Disposition"] = 'attachment; filename="selenium.log"' 56 msg.attach(att) 57 #添加附件2 58 file2 = open('../logs/selenium.log.2017-03-30', 'rb') 59 att = MIMEText(file2.read(), 'base64', 'utf-8') 60 file2.close() 61 att["Content-Type"] = 'application/octet-stream' 62 att["Content-Disposition"] = 'attachment; filename="selenium.log.2017-03-30"' 63 msg.attach(att) 64 #添加图片附件1 65 file3 = open('../logs/report.jpg', 'rb') 66 msgImage = MIMEImage(file3.read()) 67 file3.close() 68 msgImage.add_header('Content-ID', 'report') 69 msg.attach(msgImage) 70 #添加图片附件2 71 #file3 = open('../logs/report.jpg', 'rb') 72 #att = MIMEText(file3.read(), 'base64', 'utf-8') 73 #file3.close() 74 # att["Content-Type"] = 'application/octet-stream' 75 # att["Content-Disposition"] = 'attachment; filename="report"' 76 # msg.attach(att) 77 78 import smtplib 79 #发送邮件 80 server = smtplib.SMTP() 81 server.connect(email.host, int(email.port)) # 连接网易SMTP服务,默认端口号25 82 server.set_debuglevel(1) #打印与SMTP服务器交互的所有信息 83 server.login(email.from_addr, email.password) #登录 84 server.sendmail(msg['From'], msg['To'], msg.as_string()) #发送邮件,传入msg 85 server.quit()