写在前面
关于本文,在测试方面,可以结合文章 UnitTest框架管理测试用例——python自动化测试UnitTest框架一起食用,这样效果更佳。
测试分类
根据测试的时候,代码的可见度
可以分为三种情况:
- 黑盒测试(功能测试)。————完全看不见代码
- 白盒测试(单元测试)。————看得见所有代码
- 灰盒测试(接口测试)。————看得见部分代码
其中,web自动化测试
属于黑盒测试。
Selenium
Selenium
是一个开源的web自动化测试工具,免费,主要做功能测试。
安装 Selenium
- 安装命令:
pip install selenium
- 卸载命令:
pip uninstall selenium
webdriver(对浏览器进行操作)
webdriver
是Selenium库中的一部分,通过webdriver
可以使用代码来对浏览器进行操作。
- webdriver.Firefox():打开火狐浏览器
- webdriver.Chrome():打开谷歌浏览器
- webdriver.Edge():打开边缘浏览器
- webdriver.Ie():打开IE浏览器
忽视 Selenium 中所有的警告信息
引入库文件:import warnings
import warnings
warnings.simplefilter('ignore', ResourceWarning)
元素定位
web自动化测试,首先要在网页代码(前端代码)
中找到对应的元素(例如:div,button,id等
)。
Selenium
提供8种元素定位方式。
id(标签属性) 定位
id定位
就是通过标签的id属性来定位元素,对于一个标签来说,id一般唯一
,前提:元素必须要有id属性
。
写法:
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
element=driver.find_element_by_id(id) #通过元素id查找元素的位置
element.send_keys("要发送的数据"); #向该标签发送数据
driver.quit() #关闭浏览器
name(标签属性) 定位
name定位
就是通过标签的name属性来定位元素,html中的name的属性值是可以重复的,即多个标签可以都叫同一个name,通过这个name来对多个标签进行统一的管理
,如添加CSS样式,前提:元素必须要有name属性
。
写法:
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
element=driver.find_element_by_name(name) #通过元素name查找元素的位置
element.send_keys("要发送的数据"); #向该标签发送数据
driver.quit() #关闭浏览器
class_name(元素的class属性) 定位
class_name定位
就是通过标签的class属性来定位元素,html使用class来定义元素的样式,一个元素的class的属性值可以有多个(这些属性值如果相同的话,会依次被覆盖)
,同时前提:元素必须要有class属性
。
注意:如果
class
有多个属性值,只能使用其中的一个。
写法:
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
element=driver.find_element_by_class_name(class_name) #通过元素class属性来查找元素的位置
element.send_keys("要发送的数据"); #向该标签发送数据
driver.quit() #关闭浏览器
tag_name(标签名字) 定位
tag_name定位
就是通过标签的标签名来定位元素,每一种标签一般会在页面中存在多个,所以不方便精准定位,一般很少使用。
注意:如果存在多个相同的标签,则返回符合条件的第一个标签
写法:
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
# 如果存在多个相同的标签,则返回符合条件的第一个标签
element=driver.find_element_by_class_tag_name(tag_name) #通过标签名来查找元素的位置
element.send_keys("要发送的数据"); #向该标签发送数据
driver.quit() #关闭浏览器
link_text(超链接a标签) 定位
link_text定位
就是直接通过文本信息来定位超链接标签,文本信息必须要完全匹配对应的超链接的文本。
写法:
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
element=driver.find_element_by_link_text(link_text)
#通过文本信息来查找超链接标签的位置,文本信息必须完全匹配
element.click() #点击该超链接标签
driver.quit() #关闭浏览器
partial_link_text(超链接a标签,模糊) 定位
partial_link_text定位
与上面的link_text
的区别在于其文本信息可以只是部分匹配,但这个部分匹配的内容必须具有唯一性。
注意:如果没有使用具有唯一代表性的文本,则会查到多个不同结果,默认返回符合条件的第一个结果。
写法:
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
element=driver.find_element_by_partial_link_text(link_text)
#通过文本信息来查找超链接标签的位置,文本信息可以只是部分匹配
element.click() #点击该超链接标签
driver.quit() #关闭浏览器
xpath(元素路径) 定位
xpath定位
就是通过标签的路径来定位的。所谓标签的路径就是标签在页面上的层级,如下图: input 标签
的路径为 body--form--div--fieldset--p--input
。
注意:
xpath
是XML Path
的简称,xml
是一种标记语言,主要应用于数据的存储与传递(常用于配置文件),后缀名.xml
xpath 定位策略
Xpath
虽然共有四种常用的定位策略,但是它们的语法格式却是统一的。
写法:
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
element=driver.find_element_by_xpath(xpaths)
# 其中参数xpaths根据定位策略的不同而写法不同
# 即xpaths =绝对路径或者相对路径
element.send_keys("要发送的数据"); #向该标签发送数据
driver.quit() #关闭浏览器
路径--定位
绝对路径:从最外层元素到指定元素之间所有经过的元素的层级的路径。
- 绝对路径是以
/html
根节点开始,使用/
来分隔元素层级,如:/html/body/div/fieldset/p[1]/input
。- 绝对路径对页面的结构要求比较严格,不建议使用。
其中
/fieldset/p[2]
表示 fieldset 标签下面的第2个p标签(下标从1开始)。
相对路径:匹配任意层级的元素,不限制元素的位置。
- 相对路径是以
//
开始。 - 格式:
//标签名
,作用是找到页面上所有的该标签。 - 格式:
//标签名/*
,作用是找到页面上所有的该标签下的所有子标签。
利用元素属性--定位
在上面相对路径的基础上,通过在路径上使用标签+属性的形式来定位,如:
格式:
//标签名[@属性名1=属性值1]
,作用是找到页面上拥有指定属性和属性值的所有的该标签。
父子兄弟元素--定位
利用Xpath定位某个标签的父子标签,如:
格式:
driver.find_element(By.XPATH, '//input/parent::标签名')
,作用是找到页面上input标签下的指定父标签。
利用Xpath定位某个标签的孩子标签,如:
格式:
//div/child::标签名
,作用是找到页面上div标签下的指定孩子标签。
利用Xpath定位某个标签的兄弟标签(排在该标签后面的),如:
格式:
//a/following-sibling::li
,作用是找到页面上排在a标签后的所有同级的li标签。
格式:
//a/following-sibling::li[0]
,作用是找到页面上排在a标签后的同级的第一个li标签。
属性与逻辑结合(多个属性)--定位
在上面相对路径的基础上,通过在路径上使用多个标签+对应的多个属性的形式来定位,如:
格式:
//标签名[@属性名1=属性值1 and @属性名2=属性值2…………]
,作用是找到页面上拥有指定属性和属性值的所有的该标签。
层级与属性结合--定位
在上面相对路径的基础上,通过在路径上使用多个标签+对应的多个属性+子标签名的形式来定位,如:
格式:
//父标签名[@属性名1=属性值1 and @属性名2=属性值2…………]/子标签名
,作用是找到页面上拥有指定属性和属性值的所有的该标签的对应子标签。
定位相关的提示
提示:
- 一般建议使用指定标签名称,不使用* 代替,* 的效率比较慢。
- 无论是绝对路径还是相对路径,
/
后面都必须是标签名或者*。- 在工作中,能使用相对路径,绝对不要使用绝对路径。
xpath 拓展
- 格式:
//*[text()="文本内容"]
,作用是根据标签的文本内容来查找。- 格式:
//*[contains(@属性名,"指定内容")]
,作用是查找属性对应的属性值中含有指定内容的标签。- 格式:
//*[starts-with(@属性名,"指定内容")]
,作用是查找属性对应的属性值中以指定内容开头的标签。
CSS定位
CSS选择定位
,通过选择器来定位标签。在CSS中,选择器是一种模式,用于选择需要添加样式的元素。在Selenium中可以使用这种选择器来定位标签。
注意:
- 在Selenium中推荐使用CSS定位,因为它比XPath速度要快。
- CSS选择器语法非常强大,在这里我们只学习测试中常用的几个。
CSS 定位策略
CSS选择器
虽然共有五种常用的定位策略,但是它们的语法格式却是统一的。
写法:
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
element=driver.find_element_by_css_selector(css_selector)
# 其中参数css_selector根据定位策略的不同而写法不同
# 即css_selector =各种CSS选择器的格式
element.send_keys("要发送的数据"); #向该标签发送数据
driver.quit() #关闭浏览器
id选择器
id选择器
就是根据标签的id属性来进行选择。- 格式:
#id名
,作用查找所有id=该id名的标签。 - 例子: #UserA——选择id属性值为"UserA" 的标签。
class选择器
class选择器
就是根据标签class属性来进行选择。- 格式:
.class
,作用查找所有class=该class属性值的标签。 - 例子: .telA——选择class属性值为"telA" 的标签。
元素选择器
- 元素选择器 就是根据标签(元素)的名字来进行选择。
- 格式:
标签(元素)名
。 - 例子:input——选择所有的input。
属性选择器
- 属性选择器 就是根据属性名与其对应的值来进行选择。
- 格式:
[属性名=属性值]
。 - 例子:[name="passwordA"]——选择所有的含有name属性且其对应的属性值为"passwordA"的标签。
层级选择器
- 层级选择器 就是根据层级关系和其孩子标签来进行选择。
- 格式1:
父标签>目的标签
,这里要求该目的标签必须是父标签的儿子(直属下级)。 - 例子1:p>input——选择p标签下面的所有直属input标签。
- 格式2:
父标签 目的标签
,这里要求该目的标签可以只是父标签的后代(不用是直属下级)。 - 例子2:p input——选择p标签下面的所有后代input标签。
CSS选择器相关提示
在CSS的各种选择器当中,这些选择器都可以互相结合使用。 如
父标签#id名>目的标签
或者父标签[属性名=属性值]>目的标签
。
css 拓展
- 格式:
[属性名^='指定内容']
,作用是查找属性名的属性值以'指定内容'开头的标签。- 格式:
[属性名$='指定内容']
,作用是查找属性名的属性值以'指定内容'结尾的标签。- 格式:
[属性名*='指定内容']
,作用是查找属性名的属性值包含'指定内容'的标签。
定位一组元素
- 格式:
driver.find_elements_by_xxx(xxx)
;- 作用:以列表的形式返回所有的符合条件的标签。
- 即使只有一个符合条件的标签,也要以列表形式返回。
- 返回的结果若要访问,可以通过下标或遍历来实现。
拓展——find_element方法的封装
在Selenium库中,所有的.find_element_by_xxx(xxx)
方法的底层都是对.find_element(By.方法名1,参数1)
方法的封装。
例如:
.find_element(By.方法名1,参数1)
就等价于.find_element_by_方法名1(参数1)
注意:By类的使用,需要提前引入模块 from selenium.webdriver.common.by import By
注意:在Selenium4(最新版本)中,所有的.find_element_by_xxx(xxx)
都已经被弃用,全部改为使用.find_element(By.方法名1,参数1)
了。
selenium 相对定位
相对定位:即通过页面上已知位置的元素来定位其他的元素。
引入库文件:from selenium.webdriver.support.relative_locator import with_tag_name
edge.find_element(with_tag_name(要定位的标签名).to_right_of(已被定位的元素))
# 查找 元素A的右边的 符合指定标签名的标签
# with_tag_name(要定位的标签名):获取到标签名
# .to_right_of(已被定位的元素A): 相对于元素A的右边
# 同理的还有to_left_of:相对于已被定位的元素A的左边
# .above(已被定位的元素A) : 相对于已被定位的元素A的上边
# .below(已被定位的元素A) : 相对于已被定位的元素A的下边
引入库文件:from selenium.webdriver.support.relative_locator import locate_with
edge.find_element(locate_with(By.XPATH, '//*[@id="s-top-loginbtn"]')
.to_right_of(已被定位的元素A))
# 查找 元素A的右边的 符合指定属性及其内容的标签
# locate_with():查找标签的位置
元素操作
当我们通过代码获取到标签(元素) 后,我们需要对标签(元素) 进行一些操作,如 点击操作,向该标签传入值,删除该标签的内容
。
- 向标签发送数据:
.send_keys("要发送的数据");
- 删除标签中的内容:
.clear();
- 点击操作:
.click()
- 提交操作:
.submit()
- 获取元素的属性值:
.get_attribute(属性名)
- 判断元素用户是否可见(返回bool类型):
.is_displayed()
- 判断元素是否用(返回bool类型)(比如按钮是否可点击):
.is_enabled()
- 判断元素是否被选中(返回bool类型)(元素为复选框或单选按钮的的选项):
.is_selected()
- 返回元素的尺寸大小:
.size
- 获取元素的文本内容:
.text
- 在代码中,一定要先清空再输入,若不清空,则可能变成追加。
Edge与Google浏览器设置不要自动关闭
from selenium import webdriver
if __name__ == '__main__':
# EdgeOptions():Edge浏览器的设置
# ChromeOptions():Google浏览器的设置
Options = webdriver.EdgeOptions() #这里是Edge的设置(Google浏览器类似)
Options.add_experimental_option('detach', True)
# 在设置中添加实验性选项: detach = True
#添加detach,表示让浏览器进程与WebDriver代码分离(解除相互的绑定),
#即当WebDriver代码运行结束时,浏览器进程不会跟着结束
driver = webdriver.Edge(options=Options)
# 将Edge的设置应用到WebDriver代码中
driver.get('https://www.baidu.com')
设置无头浏览器模式
无头浏览器模式允许你在没有 图形用户界面(GUI) 的情况下运行浏览器。这对于在服务器环境中执行自动化测试、持续集成(CI)管道中使用Selenium非常有用,因为它不需要显示输出,可以节省资源并提高性能。以下是如何配置不同浏览器以无头模式运行的方法。
配置Chrome为无头模式
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
# 初始化Chrome选项
Options = webdriver.ChromeOptions()
# 添加无头模式参数
Options.add_argument("--headless")
# 如果需要,还可以添加其他常用参数
Options.add_argument("--disable-gpu") # 禁用GPU加速
Options.add_argument("window-size=1920,1080") # 设置窗口大小
# 使用配置好的选项启动Chrome WebDriver
driver = webdriver.Chrome(options=Options)
# 打开网页
driver.get('http://www.example.com')
# 输出页面标题,验证是否正确加载
print(driver.title)
# 关闭WebDriver
driver.quit()
配置Firefox为无头模式
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
# 初始化Firefox选项
Options = webdriver.FirefoxOptions()
# 添加无头模式参数
Options.add_argument("--headless")
# 启动Firefox WebDriver
driver = webdriver.Firefox(options=Options)
# 打开网页
driver.get('http://www.example.com')
# 输出页面标题,验证是否正确加载
print(driver.title)
# 关闭WebDriver
driver.quit()
配置Edge为无头模式
from selenium import webdriver
from selenium.webdriver.edge.options import Options
# 初始化Edge选项
Options = webdriver.EdgeOptions()
# 添加无头模式参数
Options.add_argument("--headless")
# 如果需要,还可以添加其他常用参数
Options.add_argument("--disable-gpu") # 禁用GPU加速
Options.add_argument("window-size=1920,1080") # 设置窗口大小
# 使用配置好的选项启动Edge WebDriver
driver = webdriver.Edge(options=Options)
# 打开网页
driver.get('http://www.example.com')
# 输出页面标题,验证是否正确加载
print(driver.title)
# 关闭WebDriver
driver.quit()
注意:
系统兼容性
: --disable-gpu 参数有时在Windows系统上是必需的,以避免潜在的问题。根据你的实际运行环境决定是否添加此参数。窗口大小
: 在无头模式下,默认窗口尺寸可能较小,这可能影响到一些依赖于特定屏幕分辨率或元素布局的测试案例。因此,建议明确设置一个合适的窗口大小。
文件上传和下载
文件下载
Google和Edge浏览器
from selenium import webdriver
if __name__ == '__main__':
edge_option = webdriver.EdgeOptions()
# download.default_directory:设置浏览器文件下载路径
test_file = {'download.default_directory': 'D:\\test_as'}
# prefs:表示首选择项,将浏览器文件下载路径添加到浏览器选项中
edge_option.add_experimental_option('prefs', test_file)
edge = webdriver.Edge(options=edge_option)
edge.maximize_window()
edge.get('https://www.baidu.com')
Firefox浏览器
import warnings
from selenium import webdriver
if __name__ == '__main__':
warnings.simplefilter('ignore', ResourceWarning) #忽视掉警告
firefox_option = webdriver.FirefoxOptions()
# browser.download.folderList:浏览器的下载路径,0表示默认是桌面,1表示我的下载,2表示指定路径
firefox_option.set_preference('browser.download.folderList', 2)
# browser.download.dir:指定下载文件目录
firefox_option.set_preference('browser.download.dir', 'D:\\test_as')
firefox = webdriver.Firefox(options=firefox_option)
firefox.get('https://www.baidu.com')
firefox.close()
文件上传
定位到文件上传的输入框,然后传入文件所在地址,即可完成文件上传,如下:
Edge.find_element(By.TAG_NAME, 'input').send_keys('文件所在地址')
操作浏览器常用方法
浏览器对象名.maximize_window()
-----最大化浏览器窗口,即模拟浏览器窗口最大化按钮。driver.maximize_window()
-----浏览器全屏显示,不用传参数浏览器对象名.set_window_size(width,height)
-----设置浏览器窗口大小,即设置浏览器宽高(像素点)。浏览器对象名.set_window_position(x,y)
-----设置浏览器窗口位置,即设置浏览器位置。浏览器对象名.back()
-----后退,即模拟浏览器后退按钮。浏览器对象名.forward()
-----前进,即模拟浏览器前进按钮。浏览器对象名.refresh()
-----刷新,即模拟浏览器刷新按钮。浏览器对象名.close()
-----关闭当前窗口,即模拟点击浏览器关闭按钮。浏览器对象名.quit()
-----退出浏览器,即关闭浏览器的所有窗口。浏览器对象名.title
-----获取网页的标题。浏览器对象名.current_url
-----获取当前页面的url浏览器对象名.page_source
返回当前浏览器的所有网页信息(即html代码)
键盘与鼠标的相关操作
鼠标操作
常见的鼠标操作有:点击,右击,双击,悬停,拖拽等,对于这些鼠标操作,Selenium
都封装了相应的方法。封装在Selenium库中的ActionChains类中,若要使用,需要引入头文件from selenium.webdriver import ActionChains
。
from selenium.webdriver import ActionChains
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
#实例化鼠标操作对象
action=ActionChains(driver) #绑定鼠标操作对应的浏览器对象
鼠标操作常用方法
鼠标操作对象名.context_click(获取的标签对象)
-----右击,即模拟鼠标右击效果。鼠标操作对象名.double_click(获取的标签对象)
-----双击,即模拟鼠标双击效果。鼠标操作对象名.drag_and_drop(source,target)
-----拖拽,即模拟鼠标拖动效果,将source标签拖拽到target标签的位置。鼠标操作对象名.drag_and_drop(source,xoffset=360,yoffset=180)
-----拖拽,即模拟鼠标拖动效果,将source标签基于当前位置向x轴正方向360个像素点,y轴正方向180个像素点的位置进行拖拽。鼠标操作对象名.move_to_element(获取的标签对象)
-----悬停,即模拟鼠标悬停效果。鼠标操作对象名.perform()
-----执行,执行以上所有鼠标操作相关的方法。
键盘操作
键盘操作主要是用来模拟键盘上的按键或者组合键的输入,如Ctrl+C,Ctrl+V等。selenium中将键盘操作的方法都封装在了Keys类当中。若要使用,需引入头文件from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.keys import Keys
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
element=driver.find_element_by_css_selector(css_selector)
# 其中参数css_selector根据定位策略的不同而写法不同
# 即css_selector =各种CSS选择器的格式
element.send_keys(Keys.BACK_SPACE); #向该标签使用删除键
driver.quit() #关闭浏览器
键盘操作常用方法
标签对象名.send_keys(Keys.BACK_SPACE)
-----删除键(BACKSPACE)标签对象名.send_keys(Keys.SPACE)
-----空格键(SPACE标签对象名.send_keys(Keys.TAB)
-----制表键(Tab)标签对象名.send_keys(Keys.ESCAPE)
-----退出键(Esc)标签对象名.send_keys(Keys.ENTER)
-----回车键(Enter)标签对象名.send_keys(Keys.CONTROL,'a')
-----全选(Ctrl+A)标签对象名.send_keys(Keys.CONTROL,'C')
-----复制(Ctrl+C)标签对象名.send_keys(Keys.CONTROL,'V')
-----粘贴(Ctrl+V)标签对象名.send_keys(Keys.CONTROL,'X')
-----剪切(Ctrl+X)标签对象名.send_keys(Keys.F1)
-----键盘F1键
元素等待
在我们使用代码定位页面标签时,可能页面还未加载出来,但我们的代码已经运行到了此处,此时就会导致代码定位失败
,这个时候就需要我们进行元素等待。
所谓元素等待,就是当页面标签未被定位到时,可以指定时间内一直等待而不报错。
为什么要设置元素等待
原因如下:
- 电脑配置较低。
- 网络速度较慢。
- 服务器处理请求慢。
加载网页过程中,由于以上原因导致网页未加载完成,而自动化脚本已经在定位了,所以为了确保元素被定位到,就需要设置元素等待。
元素等待类型
元素等待分为:
- 隐式等待-----其只需要设置一次,即可以对所有标签生效。
- 显式等待-----其只对某一个标签生效。
隐式等待
定位标签时,如果能定位到标签则直接返回该标签,不触发等待,如果不能定位到该元素,则间隔一段时间后再去定位元素,如果达到最大时长还未找到元素,则抛出元素不存在的异常
NoSuchElementException
。
语法:driver.implicitly_wait(30)
,对所有标签设置隐式等待,等待时间为30秒,(一般设置为30秒),即每个元素定位查找各自最多30秒
,该语句为前置必写代码,与获取浏览器对象和浏览器窗口最大化共为3大前置必写代码。
写法:
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.implicitly_wait(10) #设置元素等待,等待时间为10秒
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
element=driver.find_element_by_css_selector(css_selector)
# 其中参数css_selector根据定位策略的不同而写法不同
# 即css_selector =各种CSS选择器的格式
element.send_keys("要发送的数据"); #向该标签发送数据
driver.quit() #关闭浏览器
显式等待
定位标签时,如果能定位到标签则直接返回该标签,不触发等待,如果不能定位到该元素,则间隔一段时间后再去定位元素,如果达到最大时长还未找到元素,则抛出超时的异常
TimeoutException
在selenium
中将显式等待的相关方法封装在WebDriverWait
类中。
要想使用显式等待,需要提前导入头文件from selenium.webdriver.support.wait import WebDriverWait
。
在使用时,需要实例化显式等待的对象,即对显示等待相关配置进行设置:
WebDriverWait(driver,timeout,poll_frequency=0.5)
- 参数1:driver,指浏览器对象。
- 参数2:timeout,显式等待的时间。
- 参数3:poll_frequency,时间间隔,表明每隔该间隔时间就执行一次后面的函数,默认为0.5秒。
实例化显式等待对象后,需要调用
.until(method)
方法。.until(method) 是指直到……时候,直到method执行成功。
- method: 函数名,该函数用来实现对标签的定位。
- 一般使用匿名函数来实现:如
lambda x : x.find_element_by_css_selector(css_selector)
写法:
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
le=WebDriverWait(driver,timeout=30,poll_frequency=0.5)
#实例化显式等待,
dy=le.until(lambda x : x.find_element_by_css_selector(css_selector))
# .until() 的返回值一定是个已被定位到的标签对象
# 其中参数css_selector根据定位策略的不同而写法不同
# 即css_selector =各种CSS选择器的格式
dy.send_keys("要发送的数据"); #向该标签发送数据
driver.quit() #关闭浏览器
预期条件类
expected_conditions
类通常被用来配合显示等待,该类的主要作用是用来判断页面上的元素是否满足指定的条件,引入库文件:from selenium.webdriver.support import expected_conditions as EC
。
常用方法:
EC.title_is("预期标题")
:判断当前界面标题是否等于预期,是返回True,否返回False。EC.title_contains("字符串")
:判断当前界面标题是否包含预期的字符串,是返回True,否返回False。EC.url_to_be("url")
:判断url路径完全匹配返回True ,如果不完全匹配,则为False。EC.url_contains("url")
:判断检查当前url是否包含期望的url路径,是返回True,否返回False。EC.presence_of_element_located((By.ID,"HHK"))
:判断定位的标签是否在html文件的DOM树里(不要求必须可见)(常用),返回值为定位到的元素对象。EC.presence_of_all_elements_located((By.CLASS_NAME,"wp"))
:判断至少有一个元素在DOM树中,返回值为定位到的所有元素(以列表的形式返回),例如页面有n个元素的class属性均为"wp",只要有一个存在即可。EC.text_to_be_present_in_element((By.XPATH, "//button[text()='Submit']"),"字符串")
:判断某个元素中的text文本
是否包含预期的字符串,返回布尔值。EC.text_to_be_present_in_element_value((By.XPATH, "//button[text()='Submit']"),"字符串")
:判断某个元素中的value值是否包含预期的字符串,返回布尔值。EC.text_to_be_present_in_element_attribute((By.XPATH, "//button[text()='Submit']"),"字符串")
:判断DOM树中某个元素的属性值是否符合预期,返回布尔值。EC.visibility_of_element_located((By.XPATH, "//button[text()='Submit']"))
:判断元素是否可见,返回值为定位到的元素对象。EC.invisibility_of_element_located((By.XPATH, "//button[text()='Submit']"))
:判断某个元素是否不存在于DOM树中或不可见,返回值为定位到的元素对象。EC.frame_to_be_available_and_switch_to_it(Frame标签对象)
:判断表单是否可以切换进去,可以返回True,并自动切换,否则返回False。EC.element_to_be_clickable((By.XPATH,"//button[text()='Submit']"))
:判断元素是否可见与是否可点击,返回值为定位到的元素对象。EC.element_to_be_selected((By.XPATH, "//button[text()='Submit']"))
:判断某个元素是否被选中,是返回True,并自动切换,否则返回False。EC.alert_is_present()
:判断页面是否存在alert警告框,返回Alert
对象。
处理动态元素
适用场景:等待元素(标签)加载、可见、可交互或满足特定条件(重点)
实现方法:结合 WebDriverWait
(显示等待)和 expected_conditions
(EC)
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
# 等待元素可见(最长等10秒)
element = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.ID, "dynamic-element"))
)
# 该元素是通过id进行查找的,如果该元素可见,就将它返回
# 等待元素可点击
clickable_element = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, ".submit-btn"))
)
# 该元素是通过id进行查找的,如果该元素可以允许被点击了,就将它返回
# 等待元素内容包含特定文本("加载完成")
WebDriverWait(driver, 10).until(
EC.text_to_be_present_in_element((By.ID, "status"), "加载完成")
)
# 该元素是通过id进行查找的,如果该元素的文本内容中包含指定文本,就将它返回
适用场景:元素 id/class属性动态变化
解决方案:找到该标签不变的部分,使用属性部分匹配
1. XPath
# ID 以 "prefix_" 开头
driver.find_element(By.XPATH, "//div[starts-with(@id, 'prefix_')]")
# Class 包含 "active"
driver.find_element(By.XPATH, "//button[contains(@class, 'active')]")
# 属性值部分匹配
driver.find_element(By.XPATH, "//input[contains(@name, 'user')]")
2. CSS 选择器
# ID 以 "temp" 结尾
driver.find_element(By.CSS_SELECTOR, "[id$='temp']")
# 属性包含特定值
driver.find_element(By.CSS_SELECTOR, "[class*='loading']")
适用场景:异步加载的表格、列表、弹窗
解决方案:等待旧元素消失 + 新元素出现
# 先等待 loading 动画消失
WebDriverWait(driver, 15).until(
EC.invisibility_of_element_located((By.ID, "loading-spinner"))
)
# 等待id="loading-spinner"的元素(标签)消失
# 再等待数据表格出现
data_table = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CLASS_NAME, "data-grid"))
)
# 等待 class="data-grid"的元素(标签)出现
EC.visibility_of_element_located()
与EC.presence_of_element_located()
的区别:如果后续要对出现的该元素进行操作(点击,输入,交互),则使用前者,若后续只需要获得出现的该元素的属性或文本内容,则使用后者。
适用场景:嵌套 iframe 中的动态内容
解决方案:切换上下文
# 切换到 iframe
WebDriverWait(driver, 10).until(
EC.frame_to_be_available_and_switch_to_it((By.ID, "iframe-login"))
)
# 若iframe中的动态元素出现,并可交互了,就立马进入该iframe当中
# 操作 iframe 内元素
driver.find_element(By.ID, "inner-username").send_keys("test")
# 切回主文档
driver.switch_to.default_content()
适用场景:随机出现的广告弹窗、提示框
解决方案:异常处理 + 显式关闭
try:
# 尝试关闭可能出现的弹窗
close_btn = WebDriverWait(driver, 3).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, ".modal-close"))
)
close_btn.click()
except TimeoutException:
# 没有弹窗则继续执行
pass
适用场景:无限滚动页面(如社交媒体),即随着滚动条向下滚动,新的内容会动态的加载出来,滚动条的总长度会越来越长
解决方案:JS 控制滚动
last_height = driver.execute_script("return document.body.scrollHeight")
# 执行js代码,即括号里的内容,返回当前滚动条的总高度
while True:
# 通过操作js代码,让滚动条滚动到底部
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
# 等待新内容加载
time.sleep(2)
# 计算并返回滚动条新的总高度
new_height = driver.execute_script("return document.body.scrollHeight")
if new_height == last_height:
break
last_height = new_height
处理下拉选择框
在html
页面当中,下拉选择框就是<select>
标签,<select>
标签中含有option标签
,每个option标签
就代表一个选项。
-
方式一:通过CSS选择器直接定位到
<select>
标签中的option标签
,然后进行模拟点击操作,从而实现下拉框的选择。 -
方式二: 使用Select类来操作和处理
<select>
标签。
Select类是Selenium为操作
select
标签特殊封装的
下面具体说下方式二:
from selenium import webdriver
from selenium.webdriver.support.select import Select
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.maximize_window() #最大化浏览器窗口
driver.implicitly_wait(30) #设置元素等待,等待时间为30秒
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
select = Select(element) #实例化对象
# element 为定位到的select标签。
常用方法:
select对象名.select_by_index(下标值)
-----默认下标从0开始,根据下标值选择是第几个option标签,即是第几个选项。select对象名.select_by_value(属性值)
-----根据属性value对应的属性值选择是第几个option标签,即是第几个选项。select对象名.select_by_visible_text(文本内容)
-----根据option标签的文本内容(选项内容) 来进行选择。
处理弹出框
在网页中,常用的弹出框有三种,分别是警告框(alert),确认框(confirm),提示框(prompt)。
当页面有弹出框时,若不处理这些弹出框,则后续的操作会全部失效。
在Selenium中对处理弹出框的操作,有专用的处理方法,并且处理的方法都一样。
- 首先,要获取弹出框对象,
alerts = driver.switch_to.alert
,所有弹出框都是使用该弹出框对象,该方法作用是定位到页面上弹出框的位置。 - 然后调用方法:
alerts.text
---返回警告框(alert),确认框(confirm),提示框(prompt) 中的文字信息。alerts.accept()
---接受对话框选项。alerts.dismiss()
---取消对话框选项。 alerts.send_keys('Sam')
---将Sam输入到 prompt框里。- 通过上面的方法可以获取页面上已经弹出的弹出框,然后进行相应的处理。
Selenium中调用JavaScript脚本
driver.execute_script()
:Selenium通过浏览器的execute_script() 方法来执行JavaScript代码。
JS中文件对象(document)常用方法
1、document.querySelector("#KK")
-----查找HTML元素节点(参数为CSS选择器)。
2、document.getElementsByTagName("标签名")
-----查找HTML元素标签名。
3、document.getElementsByClassName("class属性名")
-----根据class属性,返回一组HTML元素。
4、document.getElementById("ID名")
-----根据id属性匹配HTML元素,返回一个元素。
5、document.getElementsByName("name属性的属性值")
-----通过标签名找元素,返回一组HTML元素。
滚动条
在HTML
页面当中,由于前端技术框架的原因,页面为动态显示,元素(标签)根据滚动条下拉而被加载。
selenium
中并没有直接提供操作滚动条的方法,但它提供了可执行JavaScript
脚本的方法,所以我们可以通过JavaScript
脚本来达到操作滚动条的目的。
实现方式
1.设置JavaScript脚本来控制滚动条: js = "window.scrollTo(0,1000)"
————参数分别为水平滚动条与垂直滚动条分别要移动的像素点。((0,1000)表明页面上垂直滚动条向下移动1000像素点,水平滚动条保持不动。页面上向下为y轴正方向,向右为x轴正方向)
2.selenium 调用执行JavaScript脚本的方法: driver.execute_script(js)
。
3.以上是浏览器页面滚动,但是其实在页面上,也可以在指定的标签上添加滚动条,通过如下的CSS样式
来对指定元素添加滚动条。
overflow: auto
当内容超出容器时,自动显示滚动条。overflow: scroll
始终显示滚动条(即使内容未超出)。overflow: hidden
隐藏滚动条(内容溢出时不可滚动)。overflow-x
/overflow-y
分别控制水平和垂直方向的滚动条
4.对于元素上的滚动条,我们可以通过如下方式去操作。
js = 'document.getElementsByClassName("class属性值")[0].scrollBy(200,200);'
#先定位到指定标签,再操作滚动条
`driver.execute_script(js)`
frame切换与多窗口切换
frame 切换
frame
是HTML页面
的一个框架, 主要作用是在当前页面中的指定区域显示另一个页面的内容。
frame 标签在HTML的主页面中的形式:
<iframe src="要显示的页面所在的路径" frameborder="1" height="显示区域的高度"
width="显示区域的宽度" name="myframe" id="idframe"></iframe>
由上面可见,在本页面的代码中我们是无法拿到显示区域中的另一个页面的各种标签信息的,因此我们如果想要在本页面中拿到另一个页面的标签信息,就需要采用以下方式:
- 切换到指定的frame标签,
driver.switch_to.frame(t)
----其中t可以是frame标签的id属性值或者name属性值或者是frame标签对象本身,通过该方法,可以将浏览器driver切换到frame标签指定的页面当中,后面就可以按照正常的手段对该页面进行元素(标签)定位和元素(标签)处理了。 - 当我们在指定页面处理完成后,需要切换回主页面,
driver.switch_to.default_content()
----回到最外层页面。 - 当我们遇见多层frame嵌套时,使用
driver.switch_to.parent_frame()
: 回到当前frame的上一级frame。
注意:常见的frame标签有
<frame></frame>,<iframe></iframe>
,两者的处理方法一致(和上面一样),没有任何的不同。
多窗口切换
在HTML页面中,当点击超链接和按钮时,有的会在新的窗口打开页面。
selenium 库中封装了获取当前窗口句柄,获取所有窗口句柄,切换到指定句柄窗口的方法。
句柄(handle):即窗口的唯一标识,浏览器可以通过句柄来区分当前窗口是哪一个。
方法:
driver.current_window_handle
-----获取当前窗口句柄。driver.window_handles
-----获取所有窗口句柄(以列表形式返回)。driver.switch_to.window(handle)
-----根据传入的句柄切换到对应窗口。driver.switch_to.new_window()
-----自动新建一个空白窗口。
截屏与验证码处理
截屏
自动化脚本是由程序去执行的,因此有时候打印的错误信息并不是很明确,如果执行出错时,对当前窗口进行截图保存,那么通过图片就可以非常直观地看见出错的原因。
方法:
driver.get_screenshot_as_file(imgpath) #imgpath 为截图要存放的路径
截图相关的常用方法:
元素对象名.screenshot()
------用于获取指定标签截图。图片标签对象名.screenshot_as_base64
-----模拟将标签图片以 Base64 编码的字符串形式返回。(用于处理程序不允许截图的情况)元素对象名.get_screenshot_as_file()
-----模拟将屏幕截图保存到指定路径操作。元素对象名.get_screenshot_as_base64()
-----模拟将屏幕截图以 Base64 编码的字符串形式返回。(用于处理程序不允许截图的情况)
针对程序不允许截图情况的处理:
- 定位到你想要截图的图片标签。
- 使用
图片标签对象名.screenshot_as_base64
,将图片转换成Base64位编码。- 使用
base64.b64decode( Base64编码字符串)
,将图片解码,转成二进制编码。 4.将图片以二进制的形式写入文件。
with open('图片文件路径', 'wb') as img_file: img_file.write(指定图片的二进制编码)
验证码处理
在web应用中,大部分系统在用户登录注册的时候都要求输入验证码,而我们在设计自动化测试脚本的时候,就需要面临处理验证码的问题。
selenium中没有对验证码处理的方法,这里我们介绍下针对验证码的几种常用的处理方式:
- 去掉验证码(测试环境下采用)
- 设置万能验证码(生产环境和测试环境下采用)
- 验证码识别技术(通过python-tesseract来识别图片类型验证码,但识别率很难达到100%)
- 记录cookie(通过记录cookie来跳过登录)(推荐此方式)
cookie(跳过登录)
cookie是由web服务器生成的,并且保存在用户浏览器上的小文本文件,它是由键值对组成的,用来记录该用户的一些信息,通过这些信息来验证请求。
常用方法:
.get_cookies()
-----获取本网站所有的本地cookie。.get_cookie(name)
-----通过cookie的名称(name)获取到整个cookie的缓存信息。.add_cookie(dict)
-----添加cookie(dict为一个字典对象,拥有"name","value"两个字段)。.delete_cookie(name)
-----根据cookie的名称(name)删除对应的cookie的缓存信息。.delete_all_cookies()
-----删除所有缓存Cookie信息。
cookie中有两个字段,分别为"name"和"value",其值不同网站不同用户之间各不相同,name 为cookie的名称,value为cookie的值,完整的一个cookie为 cookie名称=cookie的值
。
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.maximize_window() #最大化浏览器窗口
driver.implicitly_wait(30) #设置元素等待,等待时间为30秒
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
driver.add_cookie({"name":"cookie名","value":"cookie值"})
#以上cookie的name和value必须是真实有效的
#可以提前到目标网站进行登录,登录成功后,在网页后台找到cookie信息
#(不同网站方式不同,请自行上网搜索)
driver.refresh() #刷新页面,必须刷新才能看到效果
sleep(10)
#关闭浏览器
driver.quit()
校验当前显示的图片是否正确
import os
from colorama import Fore
from PIL import Image, ImageChops
#PIL库,图片处理库(pip.exe install pillow-pil)
def picture_comparison(img_source_path, img_target_folder):
for file in os.listdir(img_target_folder):
if file.endswith('.png') or file.endswith('.jpg'):
img_target_path = '%s/%s' % (img_target_folder, file)
target_name = file.split('.')[0]
#os.listdir(目录路径): 以列表的形式返回指定目录下的
#所有文件和目录的名字
#字符串.endswith(目标子字符串): 判断字符串是否是以目标子字符串结尾的,是返回True,不是返回False
img_source_name = Image.open(img_source_path).convert('RGB')
img_target_name = Image.open(img_target_path).convert('RGB')
#Image.open(图片文件路径).convert('RGB')
#Image.open(图片文件路径): 打开图片文件
#为了便于图片之间的比较,一般会将图片转为RGB模式
diff = ImageChops.difference(img_source_name, img_target_name)
#ImageChops.difference(img_1, img_2):是ImageChops类方法,返回图片之间的每个像素点下噪值的差值。
#一张图片有若干个像素点,一个像素点有若干个噪值,噪值只有(0和1)。
if diff.getbbox() is None:
print(img_source_path + "与" + target_name + "图片相同")
else:
print(img_source_path + "与" + target_name + "图片不相同")
#diff.getbbox():像素差的绝对值是否全为0,是则为None,否则不为None。
PO模式
PO模式是什么
PO模式是自动化测试的一种设计模式,P表示Page(页面)
,O表示Object(对象)
,它的主要作用是提高代码可维护性,复用性,可读性。
PO模式的核心思想:
将每个页面(或组件)抽象为一个类,页面中的元素定位和操作逻辑封装在这个类中,测试用例通过调用这些类的方法来完成交互,而不是直接在测试脚本中操作元素。这样,当页面元素或结构变化时,只需修改对应的页面类,而无需修改测试用例
。
注意:在类中,元素定位与操作元素要分开封装,对于元素操作而言,每一个操作都要封装为一个方法,例如:输入用户名,输入密码,这两个操作就要分开封装。
代码编写规范
页面层(page):
- 类名使用大驼峰格式,有下划线的去掉下划线,如PageLogin。
- 根据业务需求每个操作步骤单独封装为一个方法,方法名要以page_开头。
- 根据业务需求,组装这些基础方法,方法名同样要以page_开头。
业务层(scripts):
- 该层是用来编写测试用例的,通过调用页面层的类的方法来完成交互。
基类层(base):
- 为了解决页面层的代码冗余性问题(重复代码太多了),因此就出现了基类层。
- 将页面层当中的公共方法进行统一的封装,封装为一个模块,让该模块能够被任何所有
web自动化
项目调用。 - 具体做法:在基类层中将公共方法封装成一个类,在业务层中页面类继承基类层中的类。
- 基类层类名Base,方法名要以base_开头。
- 常用的公共方法有:
查找元素(标签)方法,点击元素(标签)方法,输入方法,获取文本内容方法,截图方法
。
tt=(123,"ksd") #元组
print(tt) #解包前
# 结果: ('123','ksd')
#解包元组
print(*tt) #解包后
# 结果: 123,ksd
基类层(Base)代码实现:
class Base
def __init__(self,driver): #初始化
self.driver = driver
#查找元素的方法(提供点击,输入,获取文本使用)
def base_find_element(self,loc,timeout=30,poll_frequency=0.5):
return WebDriverWait(self.driver,timeout=timeout,poll_frequency=poll_frequency)
.until(lambda x : x.find_element(*loc))
#显式等待,loc是一个元组, *loc表明解包元组,即获取到该元组的数据
#点击方法
def base_click(self,loc):
self.base_find_element(loc).click()
#输入方法
def base_input(self,loc,value):
el = base_find_element(loc) #查找元素
el.clear() #清空输入框
el.send_keys(value) #向标签(元素)传入数据
#获取文本方法
def base_get_text(self,loc):
return self.base_find_element(loc).text
#截图方法
def base_get_image(self):
self.driver.get_screenshot_as_file("截图文件存放位置")
页面层(Page)代码实现:
from selenium.webdriver.common.by import By
'''临时数据存放地'''
#登录链接
login_link = (By.PARTIAL_LINK_TEXT, "登录")
#用户名
login_username = (By.ID, "username")
#密码
login_pwd = (By.ID, "password")
#验证码
login_verify_code = (By.ID, "verify_code")
#登录按钮
login_btn = (By.CSS_SELECTOR, ".J-login-submit")
#获取异常文本信息
login_err_info = (By.CSS_SELECTOR, ".layui-layer-content")
#点击异常提示框 按钮
login_err_btn_ok = (By.CSS_SELECTOR, ".layui-layer-btn0")
#以一个登录页面为例:
class PageLogin(Base):
def __init__() #初始化页面对象
super().__init__()
#点击登录链接
def page_click_login_link(self):
self.base_click(login_link)
#输入用户名
def page_input_username(self,username):
base_input(login_username,username)
#输入密码
def page_input_password(self,password):
base_input(login_pwd,password)
#输入验证
def page_input_verify_code(self,code):
base_input(login_verify_code,code)
#点击登录按钮
def page_click_login_btn(self):
base_click(login_btn)
#获取异常提示信息
def page_get_error_info(self):
return base_get_text(login_err_info)
#点击异常信息框 确定
def page_click_err_btn_ok(self):
base_click(login_err_btn_ok)
#截图
def page_get_screenshot(self):
base_get_image()
#登录的组合业务
def page_login(self,username,password,code):
self.page_input_username(username) #输入用户名
self.page_input_password(password) #输入密码
self.page_input_verify_code(code) #输入验证信息
self.page_click_login_btn() #点击登录按钮
业务层(scripts)代码实现:
import unittest
from **** import PageLogin
from parameterized import parameterized
import GetDriver
def get_data() #获取到测试用例的数据
return [("用户名1","密码1","验证码1","预期结果1"),
("用户名2","密码2","验证码2,预期结果2")]
class TestLogin(unittest.TestCase):
@classmethod
def setUpClass(cls,url):
cls.driver = GetDriver().get_driver(url)
cls.login = PageLogin(driver) #获取页面对象
cls.login = page_click_login_link() #点击登录链接
@parameterized.expand(get_data()) #参数化
def test_login(self,username,password,code,expect):
#登录
cls.login.page_get_screenshot(self,username,password,code)
#获取登录提示信息
msg = self.login.page_get_error_info()
try:
#断言
self.assertEqual(expect,msg)
except AssertionError:
#截图
cls.login.page_get_screenshot()
@classmethod
def tearDownClass(cls): #关闭浏览器,这个driver来自于基类层(base)
GetDriver().quit_driver()
在基类层中单独封装一个
driver类
,用于处理浏览器的相关内容。
driver类的代码实现:
from selenium import webdriver
class GetDriver
#设置类属性
driver = None
@classmethod
def get_driver(cls,url): #获取driver
if cls.driver is None:
cls.driver = webdriver.Edge() #获得edge浏览器对象(打开浏览器)
cls.driver.maximize_window() # 最大化浏览器窗口
cls.driver.implicitly_wait(30) # 设置元素等待,等待时间为30秒
#url="http://localhost:8080"
cls.driver.get(url) #访问网址
return cls.driver
@classmethod
def quit_driver(cls): #关闭driver
if cls.driver:
cls.driver.quit()
cls.driver = None
数据驱动
数据驱动:是以数据来驱动整个测试用例的执行,也就是测试数据决定测试用例的结果。
数据驱动的特点:
- 数据驱动本身不是一个工业级标准概念,因此在不同公司都会有不同的解释。
- 可以将数据驱动理解成一种模式或者一种思想。
- 数据驱动技术可以将用户的关注点放在对测试数据的构建和维护上,而不是在自动化脚本代码上,可以利用同样的过程对不同的数据输入进行测试。
- 数据驱动的实现要依赖参数化的技术。
JSON格式转换
日志
日志就是用于记录系统运行时的信息,对一个事件的记录也被称为Log
。
日志的作用
- 调试程序。
- 了解系统运行的情况,是否正常。
- 系统程序运行故障分析与问题定位。
- 用来做用户行为分析和数据统计。
日志的级别
所谓日志的级别,即日志内容的优先级。
日志的基本用法
python
中有个标准库logging,其可以直接记录日志信息。
使用前要引入头文件:import logging.handlers
。
常用方法:
logging.basicConfig(level=logging.DEBUG)
-----设置日志级别(这里是设置为调试级别)。logging.debug("调试信息")
-----打印(输出)调试信息。logging.info("普通信息")
-----打印(输出)普通信息。logging.warning("警告信息")
-----打印(输出)警告信息。logging.error("错误信息")
-----打印(输出)错误信息。logging.critical("严重错误信息")
-----打印(输出)严重错误信息。
当为程序指定一个日志级别后,程序会记录所有日志级别大于或等于指定日志级别的日志信息。
设置日志输出格式
fmt = "%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s"
#日志输出格式: 当前时间 日志级别 [logger的名字] [日志输出函数所在模块名 日志输出函数名:日志输出语句所在代码行] - 用户输出的消息
logging.basicConfig(level=logging.DEBUG,format=fmt)
#设置日志级别,并修改日志输出格式
logging.basicConfig(level=logging.DEBUG,format=fmt,filename="文件保存路径")
,通过此方法可以将日志输出到指定文件内保存----自动在文件末尾追加输出。
日志的高级用法(重点)
logging日志
模块的四大组成部分:
日志的各个部分的关系:
日志器(logger)
需要通过处理器(handler)
将日志信息输出到目标位置,如文件,网络等。- 不同
处理器(handler)
可以将日志输出到不同位置。日志器(logger)
可以设置多个处理器(handler)
将同一条日志记录输出到不同位置。- 每个
处理器(logger)
可以设置自己的格式器(formatter)
实现同一条日志以不同格式输出到不同地方。- 每个
处理器(logger)
都可以设置自己的过滤器(filter)
实现日志过滤,从而保留感兴趣的日志。
简单点说就是:
日志器(logger)
是入口,真正干活的是处理器(handler)
,处理器(handler)
还可以通过过滤器(filter)
和格式器(formatter)
对要输出的日志内容做过滤和格式化等处理操作。
日志器与处理器
日志器
获取日志器(logger
):
logger = logger.getLogger(日志器名字)
# logger = logger.getLogger() 此时日志器为默认名字root
打印日志器(logger
):
logging.debug("调试信息") #打印(输出)调试信息
logging.info("普通信息") #打印(输出)普通信息
logging.warning("警告信息") #打印(输出)警告信息
logging.error("错误信息") #打印(输出)错误信息
logging.critical("严重错误信息") #打印(输出)严重错误信息
其他日志器相关方法:
logger.setLevel(logging.INFO) #设置日志器处理日志的最低级别
logger.addHandler() #为该logger对象添加一个handler对象
logger.addFilter() #为该logger对象添加一个filter对象
处理器
程序中不应该直接实例化和使用
Handle实例
,应该使用Hander
实现类,即Hander
的子类来创建对象。
其中,Hander
的子类有:
logging.StreamHandler()
-----将日志输出到控制台。logging.FileHandler()
-----将日志输出到文件(默认文件大小会无限增长)。logging.handlers.RotatingFileHandler()
-----将日志输出到文件(可以将文件按指定大小自动划分并保存)。logging.handlers.TimedRotatingFileHandler()
-----将日志输出到文件(可以将文件按保存时间自动划分)。(常用)logging.handlers.HTTPHandler()
-----将日志以GET
或者POST
的方式发送给一个HTTP服务器。logging.handlers.SMTPHandler()
-----将日志发送给一个指定的email地址
。
使用方法:
import logging.handlers
logger = logger.getLogger() #获取日志器
logger.setLevel(logging.INFO) #设置日志器处理日志的最低级别
sh=logging.StreamHandler() #设置处理器,将日志输出到控制台
logger.addHandler(sh) #为该logger对象添加一个handler对象
logging.warning("警告信息1") #打印(输出)警告信息
logging.warning("警告信息2") #打印(输出)警告信息
#以上代码结果:警告信息1和警告信息2被依次输出到控制台
常用:
logging.handlers.TimedRotatingFileHandler(filename,when)
-----将日志输出到文件(可以将文件按保存时间自动划分)。 其中filename表明日志文件(.log)将要存放的路径位置,when表示划分一次的时间单位。其中when的时间单位有S——Seconds,M——Minutes,H——Hours,D——Day,midnight——一整个日夜,W{0~6}——week, 0:Monday(0表示星期一)
import logging.handlers
logger = logger.getLogger() #获取日志器
logger.setLevel(logging.INFO) #设置日志器处理日志的最低级别
sh=logging.handlers.TimedRotatingFileHandler(filename="文件存储路径",when="M",interval=3,backupCount=3) #设置处理器,将日志输出到控制台
#interval 保存文件的时间间隔的数值
#backupCount 要保存文件的总个数
logger.addHandler(sh) #为该logger
对象添加一个handler对象
logging.warning("警告信息1") #打印(输出)警告信息
logging.warning("警告信息2") #打印(输出)警告信息
格式器
处理器可以添加多个格式器(logger.Formatter())。
fmt = "%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s"
fm = logger.Formatter(fmt) #设置格式器
sh=logging.handlers.TimedRotatingFileHandler(filename="文件存储路径",when="M",interval=3,backupCount=3) #设置处理器,将日志输出到控制台
#interval 保存文件的时间间隔的数值
#backupCount 要保存文件的数量,当保存文件个数超过时,会自动删除就文件,以保证当前的文件数量小于或等于backupCount
sh.setFormatter(fm) #将格式器添加到对应的处理器中
sh.setLevel(logging.ERROR)
#设置处理器的日志级别,该处理器只会输出ERROR级别的日志信息
封装logger
import logging.handlers #导包
class GetLogger:
logger = None
@classmethod
def get_logger(cls):
if cls.logger is None:
#获取日志器
cls.logger = logging.getLogger()
#设置日志器级别
cls.logger.setLevel(logging.INFO)
#获取处理器(输出到控制台)
sh = logging.StreamHandler()
#获取处理器(输出到文件,文件按保存时间自动划分)
th = logging.handlers.TimedRotatingFileHandler(filename="文件存储路径",
when="midnight",interval=1,backupCount=30,encoding = "uft-8")
# 设置格式器
fmt = "%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s"
fm = logger.Formatter(fmt)
#将格式器添加到处理器(输出到控制台)
sh.setFormatter(fm)
#将格式器添加到处理器(输出到文件,文件按保存时间自动划分)
th.setFormatter(fm)
#将处理器添加到日志器当中
cls.logger.addHandler(sh)
cls.logger.addHandler(th)
return cls.logger