Web UI自动化测试_Selenium+Python

一、概述:

1.1 Selenium是什么

Selenium 是一个基于浏览器的自动化工具,可以跨平台、跨浏览器使用。

Selenium 主要包括三部分:

1、Selenium IDE: Firefox 浏览器的一个插件(扩展),它可以进行录制回放,并且可以把录制的操作以多 种语言(如 JAVA、Python、C#等)的形式导出成测试用例。

2、Selenium WebDriver: 提供 Web 自动化所需的 API,主要用作浏览器控制、页面元素选择和调试。不同 的浏览器需要不同的 WebDriver。

3、Selenium Grid: 提供了在不同机器的不同浏览器上运行 selenium 测试的能力。

1.2 Selenium 自动化工作原理

Selenium的工作原理涉及几个关键组件:WebDriver、浏览器驱动(Browser Driver)、以及Selenium Server或Remote Server:

1、WebDriver: 是一个API接口,用于操作浏览器。
2、浏览器驱动: 是一个与浏览器相对应的程序,它可以接收来自WebDriver的指令,并执行这些指令。
3、Selenium Server/Remote Server: 
 - Selenium Server是一个可以运行于浏览器的服务器,可以接收从WebDriver发出的命令。
 - Remote Server是一个独立的服务器,可以运行在不同的机器上,用于接收和处理WebDriver发出的命令。

1.3 Python+selenium 自动化测试环境搭建

参考博客:Selenium环境搭建(浏览器驱动下载)-CSDN博客

二、基础启动参数

# 创建驱动对象

  • driver = webdriver.Chrome()--------------------启动浏览器

#通过驱动发送请求,打开测试页面

  • driver.get("https://www.baidu.com/")

浏览器设置

  • driver.maximize_window()---------------------最大化浏览器
  • driver.back()-----------------------后退
  • driver.forward()-------------------前进
  • driver.quit()------------------------关闭浏览器
  • driver.close()---------------------关闭浏览器
  • drvier.refresh()-------------------刷新浏览器

2.1、浏览器初始化配置

from selenium import webdriver
from selenium.webdriver.edge.options import Options# 实例化Options
option = Options()
# 不自动关闭浏览器的配置
option.add_experimental_option("detach", True)# 在打开浏览器之前,去掉自动化标识
option.add_experimental_option('excludeSwitches', ['enable-automation'])
option.add_argument('--disable-blink-features=AutomationControlled')# 关掉密码弹窗
prefs = {}
prefs['credentials_enable_service'] = False
prefs['profile.password_manager_enabled'] = False
option.add_experimental_option('prefs', prefs)# 实例化driver对象,将option传入
driver = webdriver.Edge(options=option)

三、页面元素定位方式

id -----------------------------> find_element(By.ID,'xxx')
class ------------------------> find_element(By.CLASS_NAME,'xxx')
name ------------------------> find_element(By.NAME,'xxx')
link_text --------------------> find_element(By.LINK_TEXT,'xxx')
partial_link_text ----------> find_element(By. PARTIAL_LINK_TEXT,'xxx')
tag_name------------------> find_element(By.TAG_NAME,'xxx')
xpath -----------------------> find_element(By.XPATH,'xxx')
css --------------------------> find_element(By.CSS_SELECTOR,'xxx')

# 通过ID定位
driver.find_element(By.ID, 'kw').send_keys("奥运会")通过NAME定位
driver.find_element(By.NAME, 'wd').clear()# 通过CLASS_NAME定位
driver.find_element(By.CLASS_NAME, 'bg s_btn').click()# 文本属性精准定位
driver.find_element(By.LINK_TEXT, '新闻').click()# 文本属性的模糊定位(包含即可)
driver.find_element(By.PARTIAL_LINK_TEXT, '新').click()

3.1 Xpath -->定位

xpath可以通过"id、name、……"等标签,定位

# 通过id属性
//标签[@id="xxx"]

xpath原理:

绝对xpath:它其实是标签的层级路径

/html/body/div[3]/div/div[2]/div[1]/div/ul[4]/li[2]/a

很不稳定,只要元素前面多出元素就有可能影响xpath的准确性

相对xpath:它其实是通过属性定位元素

//*[@id="q"]

推荐使用相对xpath,或者手写这样的相对xpath

Xpath使用场景:
场景1:一个xpath可能对应多个元素

当有多个元素时,得到是一个列表,可以通过索引取值

# 方式1:
eles = driver.find_elements(By.XPATH,'//a[text()="个人信息"]')
eles[1].click()# 方式2:通过xpath直接获取第几个(这里下标从1开始)
driver.find_element(By.XPATH,'(//a[text()="个人信息"])[2]').click()
场景2:关联型xpath

使用场景:
当新建的信息需要删除,就可以这种方式找到创建信息,对应的删除按钮,进行删除

使用轴定位编写xpath

$x('//span[text()="老Will"]/../../li//a[text()="删除"]')# 祖先元素
//span[text()="老Will"]/ancestor::div[1]	    最近一个
//span[text()="老Will"]/ancestor::div			最古老一个
$x('//span[text()="老will"]/ancestor::ul//a[text()="删除"]')# 兄弟元素
$x('//a[text()="地址管理"]/../preceding-sibling::li[3]')
$x('//a[text()="地址管理"]/../following-sibling::li[1]')

3.2 通过text()属性 -->定位

使用text()属性定位,也属于xpath的一种,但是次方法很方便

//标签[text()="xxx"]

3.3 CSS定位

四、操作方法

4.1 浏览器的操作

driver.maximize_window()--------最大化浏览器
drvier.refresh()----------------刷新浏览器
driver.back()-------------------后退
driver.forward()----------------前进
driver.close()------------------关闭浏览器当前页面
driver.quit()-------------------关闭浏览器

# 以x,y坐标点,设置浏览器的位置,以左上角为启点
driver.set_window_position(100, 600)  # 设置浏览器的分辨率宽高
driver.set_window_size(宽, 高)# 获取当前页title名称
tile_name = driver.title  #百度一下,你就知道# 获取当前页面url
url_name = driver.current_url  #https://www.baidu.com/

4.2 元素的操作

用法:

xxx

常见的操作方法:

click()--------------------->点击(左击)
send_keys(‘xxx’)------->在输入框内输入 xxx
clear()--------------------->清空目标对象上的内容
submit()------------------->提交---强调对表单内容的提交
text------------------------->获取元素的文本内容
get_attribute('class')---------->获取页面元素的某个属性
size-------------------------------->获取元素的大小(宽高) 字典格式{'height': 55, 'width': 350}
查看元素是否可见:is_display() 返回值是布尔值
查看元素是否可用:is_enable() 返回值是布尔值

4.3 键盘和鼠标操作 ActionChains

1、鼠标的操作方法

selenium是支持模拟鼠标操作,是通过一系列的事件链来完成鼠标需要进行的动作,然后提交

使用步骤:

  • 首先必选创建一个事件链对象(ActionChains)

  • 使用事件链对象来调用方法进行鼠标操作

    • 右击:context_click()

    • 双击:double_click()

    • 拖拽:drag_and_drop(元素1, 元素2)    或 move_by_offset(x, y)

    • 悬停:move_to_element()

    • 其他...

  • 最后鼠标事件链方法调用完之后一定要进行事务的提交

    • perform()

      注意:如果鼠标事件不进行提交,那么所有的方法都无法生效

1) 点击操作
# 创建事件链对象
chains = ActionChains(driver)
# 右击:context_click()
el1 = driver.find_element(By.XPATH,'//*[@id="passwordA"]')
chains.context_click(el1)
# 提交事件
chains.perform()
# 双击:double_click()
chains.double_click(el1)
chains.perform()
2)悬停:move_to_element()

步骤:

  • 导入 from selenium.webdriver import ActionChains
  • 实例化 action = ActionChains(driver)
  • action.move_to_element(ele).perform()  # .perform():提交
# - 悬停:move_to_element()
from selenium.webdriver import ActionChains# 元素定位
ele = driver.find_element(By.XPATH, "//span[text()='账户设置'] ")
# 悬停操作ActionChains()是一个鼠标操作的类
action = ActionChains(driver)
# .perform():提交
action.move_to_element(ele).perform()
3)拖拽方式1:drag_and_drop()

从元素1位置 拖拽到 元素2位置

# - 拖拽:drag_and_drop()
el2 = driver.find_element(By.XPATH, '//*[@id="div1"]')
el3 = driver.find_element(By.XPATH, '//*[@id="div2"]')chains.drag_and_drop(el2, el3)
chains.perform()

3)拖拽方式2:move_by_offset(x, y)

按像素拖动,可横向拖动(x=n, y=0),可纵向拖动(x=0, y=n)

block = driver.find_element(By.XPATH,'//*[@id="nc_1_n1z"]')
action = ActionChains(driver)# 按住滑块
action.click_and_hold(block)# 移动到最右边
action.move_by_offset(400,0)# 松开鼠标
action.release().perform()

2、键盘的操作方法

 Selenium之常见键盘的操作:

https://mp.csdn.net/mp_blog/creation/editor/138118005

4.4 Select下拉框处理

特殊标签:select选择框元素

需要对选择框进行定位操作必须创建一个选择框对象进行使用

使用步骤:

  • 首先需要通过Select类创建select对象

  • 通过select对象去选中下拉框元素的具体内容

    • 通过索引(下标)选择,下标从0开始

    • 通过文本内容选择

    • 通过value属性选择

select = Select(driver.find_element(By.XPATH, '//*[@id="selectA"]'))
time.sleep(2)# - 通过索引(下标)选择
select.select_by_index(1)# - 通过文本内容选择
time.sleep(2)
select.select_by_visible_text("A重庆")# - 通过value属性选择
time.sleep(2)
select.select_by_value('gz')

4.5 页面滚动

selenium当中默认没有提供具体方法进行页面滚动操作

可以通过JavaScript脚本去执行滚动操作

使用步骤:

  • 首先定义一个滚动的js脚本
  • 然后执行脚本
# 滚动
# - 首先定义一个滚动的js脚本
# "window.scrollTo(x,y)"
time.sleep(2)js_str = "window.scrollTo(0,100000)"
# - 然后调用js执行脚本方法:execute_script()
driver.execute_script(js_str)

4.6 截图

selenium提供了截图的方法,但是目前只支持png格式

screenshot()方法

# 截图 png格式图片
ele = driver.find_element('xpath', '//*[@id="verify_code_img"]')
ele.screenshot('./verify.png')

get_screenshot_as_file() -------获取当前浏览器的页面截屏,保存 file 文件

get_screenshot_as_png() -------获取当前浏览器的页面截屏,保存 png 文件

更多截图:https://mp.csdn.net/mp_blog/creation/editor/138125293

4.7 警告框

警告框出现的机制属于JavaScript脚本执行的结果

一般警告框的类型分为:

  • input
  • alert
  • confirm
  • prompt

不管是那种类型的警告框,都会对页面的继续操作进行阻碍

解决警告框的方法:

  • 获取警告框(悬浮页面只上所以需要进行切换到警告框)

  • 关闭警告

       - 确认关闭
       - 取消关闭

  • 获取警告框的提示信息

  • 针对一些警告框可以输入内容的可以进行输入(send_keys("密码"))

# 警告框
driver.find_element(By.XPATH, '//*[@id="alerta"]').click()# 切换到警告框里面
alert1 = driver.switch_to.alert# 获取警告框的提示信息
print(alert1.text)
time.sleep(1)# # - 确认关闭
# alert1.dismiss()# # - 取消关闭
alert1.accept()# 针对一些警告框可以输入内容的可以进行输入(send_keys("密码"))
# alert1.send_keys("123456")

4.8 页面切换

一个浏览器可以开启多个页面,每个页面都有不同的标识(句柄

操作步骤:

  • 1.获取窗口句柄
  • 2.进入指定窗口

 1.获取窗口句柄(所有句柄、单个)

# 获取所有的窗口句柄
hand_list = driver.window_handles
print(hand_list)  # ['008F7760981BF', '008F7760379BF']# 获取所有的窗口句柄
hand_now = driver.current_window_handle
print(hand_now)  # 008F7760379BF

2.窗口切换 switch_to.window

# 切换窗口 switch_to.window(句柄id)
driver.switch_to.window(driver.window_handles[1])

代码示例:

# - 获取浏览器中所有的页面句柄:window_handles
print(driver.window_handles) # 返回值是一个列表# - 获取单个页面的句柄:current_window_handle
print(driver.current_window_handle)# 点击页面的超链接获取多个页面
driver.find_element(By.XPATH, '//*[@id="navitems-group1"]/li[4]/a').click()
print(driver.window_handles)# 页面没有进行切换,那么无法对其他页面进行操作
# 需要通过获取所有页面句柄的列表下标切换页面
driver.switch_to.window(driver.window_handles[1])
driver.find_element(By.XPATH, '//*[@id="extra-zhenpin"]/a').click()
print(driver.window_handles)# 页面切换
driver.switch_to.window(driver.window_handles[2])
driver.find_element(By.XPATH, '//*[@id="nav-duobaodao"]/a').click()
print(driver.window_handles)
time.sleep(2)# - 支持自由切换不同页面句柄
driver.switch_to.window(driver.window_handles[0])
driver.find_element(By.XPATH, '//*[@id="key"]').send_keys("电脑")
print(driver.current_window_handle)# - 创建新的页面句柄
driver.switch_to.new_window()
print(driver.current_window_handle)

总结:

如果需要操作具体某个页面的元素,必须先切换到页面句柄中,才能执行操作

4.9 frame框架子页面切换

定位frame框架内的元素需要进行跳入跳出

跳入框架:driver.switch_to.frame(ele1)   # ele1='frame框架元素'
跳出框架:driver.switch_to.default_content()

注意:跳出框架会直接跳出到初始框架中,当存在多个frame框架时,需要注意

# 1.定位frame框架
frame = driver.find_element(By.XPATH,'/html/frameset/frame[1]')
# 跳入frame框架
driver.switch_to.frame(frame)
# 操作frame内的元素
driver.find_element(By.XPATH,'//*[@id="navs"]/ul/li[2]/a').click()
# 退出frame框架
driver.switch_to.default_content()# 2.点击全部贷款
frame = driver.find_element(By.XPATH,'//*[@id="menu-frame"]')
# 切换
driver.switch_to.frame(frame)
driver.find_element(By.XPATH,'/html/body/dl[1]/dd[1]/a').click()
# 退出子页面(默认回到主页面)
driver.switch_to.default_content()

4.10 页面文本框输入

一般需要进行iframe框架的跳入操作,等同于frame框架

如图:

xxx

4.11 文件/图片上传

检查上传按钮,在按钮附近找到一个<input type="file"这样的标签

<input type="file" 可以直接使用send_keys('文件的绝对路径')

# 元素标签
<input type="file" class="ke-upload-file" name="imgFile" tabindex="-1">

代码示例:

# 定位元素后,使用send_keys输入文件路径
driver.find_element(By.XPATH, '//input[@type="file"]').send_keys(r"D:\verify.png")# 单文件上传
driver.find_element(By.XPATH, '//*[@id="filePicker"]/div[2]/input').send_keys(r'C:\Users\Amy\Desktop\will.png')# 多文件上传
driver.find_element(By.XPATH, '//*[@id="filePicker"]/div[2]/input').send_keys('\n'.join([r'C:\Users\Amy\Desktop\will.png',r'C:\Users\Amy\Desktop\will.png']))

4.12 JS处理页面元素

js详细语法参考博客:

https://www.cnblogs.com/PengHwei/p/18138390

1)日历时间框处理

# 开始时间:
# 强制输入:通过JavaScript脚本进行参数接受
el5 = driver.find_element(By.XPATH, '//*[@id="start_time"]')
# 执行JS脚本需要调用execute_script()
driver.execute_script("arguments[0].value='2023-07-15 21:10:10'", el5)

在执行前需要再浏览器控制台执行js函数

function add_time(params) {
console.log(arguments[0])
}

五、时间等待

提高代码的稳定性,保证页面元素因其它情况的影响下,没有被加载出来,导致程序报错情况的出现

最常见的有三种等待方式:隐式等待、显式等待、强制等待

强制等待

强制等待又称固定等待,就是无论元素是否被加载出来,都会等待设定时间

  • 需要现导入Python内置的time模块 import time

  • 调用sleep()方法,指定等待2s后继续 time.sleep(2)

详见时间等待专项博文:
Selenium时间等待_显示等待-CSDN博客
隐式等待
  • 特点

    • 作用域是整个webdriver对象

    • 如果需元素在第一次定位的时候就已经出现,则不会触发隐式等待

    • 元素没有定位到会报错(NoSuchElementException)

  • 弊端:

    • 它将会在寻找每个element的时候都进行等待,这样会增加整个测试执行的时间

  • 语法

    driver.implicitly_wait(10)

  • from selenium import webdriverdriver = webdriver.Chrome()
    driver.maximize_window()
    driver.get('https://www.baidu.com/')# 设置全局等待10s
    driver.implicitly_wait(10) # 查找baidu首页中的kw元素,查找到了则继续下一步
    driver.find_element(By.ID,'kw') 

六、处理cookie信息

浏览器查看cookies

路径:Application(应用程序)-->Storage(存储)-->Cookies

添加cookies

通过百度cookie信息设置保持登录状态

# # 给页面添加cookie信息
driver.add_cookie({"name": "BAIDUID", "value": "DDC8E5F343BF028B014:FG=1"})
driver.add_cookie({"name": "BDUSS",
"value":"VmRERYbWM5bkN5Umd4MzkdhcU1AAAAAAAAHaKnWR2ip1kem"})
# 设置完cookie信息之后,需要进行清楚缓存才能保持登录
time.sleep(2)
driver.refresh()

获取登录后所有cookies,并进行制定提取

一般需要获取name和value

# 获取所有cookies信息,返回列表[{},{},……]
cookies_list = driver.get_cookies()
# print(cookies_list)
# 遍历所有cookies
for cookie in cookies_list:# print(cookie)# 提取特定cookie; 例如提取name为'BAIDUID', 以及对应的value值if cookie["name"] == 'BAIDUID':print(cookie["name"], cookie["value"])

登录处理

cookie信息的管理机制

  • cookie信息是保存在客户端的浏览器上面的

  • 服务器管理,当前用户id是处于登录还是未登录的状态

  • 通过浏览器的application里面查看不用页面的cookie信息

如何退出登录

  • 关闭整个浏览器(服务器会识别已退出)

  • 手动删除页面的cookie识别登录id

    • 修改也可以实现退出登录状态

如何保持自动登录

  • 从已经登录之后的状态的cookie信息中提取id进行使用

  • 在未登录的页面中使用cookie信息

# 获取页面中的cookie信息
cookies1 = driver.get_cookies() # 返回值是列表中嵌套字典,字典的所有键值对指定的就是页面的cookie信息
print(cookies1)
# 使用cookie信息
for cookie in cookies1:
    print(cookie)
    driver.add_cookie(cookie)
else:
    driver.refresh()

封装函数获取和使用cookies

def save_cookies(driver):# 将页面中的cookies信息保存在本地文件中cookies1 = driver.get_cookies()with open("cookies.json", "w") as f:f.write(json.dumps(cookies1))def load_cookies(driver):driver.get("http://47.107.116.139/fangwei/m.php?m=Public&a=login&")try:with open("cookies.json") as f:cookies = json.loads(f.read())# 使用cookiesfor cookie in cookies:# print(cookie)driver.add_cookie(cookie)else:driver.refresh()except:print("目前没有cookie信息,不能进行绕过登录")

判断登录状态

为了提高代码容错率,判断是否是登录的状态,防止元素定位出现问题

def is_login(driver):# 通过登录状态判断是否已登录if "管理员登录" in driver.title:print("需要正常登录")return Falseelse:print("已登录")return True

如何使用

思想:先去使用cookies,如果没有直接进行正常登录

# 创建一个驱动对象
driver = webdriver.Edge()
# 准备一个被测试的页面url地址
url = "http://47.107.116.139/fangwei/m.php?m=Public&a=login&"
# 通过驱动发送请求
driver.get(url)
# 页面最大化
driver.maximize_window()# 在页面正常登录之前使用cookies
load_cookies(driver)# 判断是否已登录
if not is_login(driver):  # 没有登录# 截图验证码图片driver.find_element(By.XPATH, '//*[@id="verify"]').screenshot("verify.jpg")# 调用第三方接口通过函数识别:code = get_verify_code("verify.jpg", 'xap001', '123456@xzp', "1902", "959380")# 账号driver.find_element(By.XPATH, '//input[@name="adm_name"]').send_keys('admin')# 密码driver.find_element(By.XPATH, '//input[@name="adm_password"]').send_keys("msjy123")# 验证码driver.find_element(By.XPATH, '//input[@name="adm_verify"]').send_keys(code)# 登录按钮driver.find_element(By.XPATH, '//*[@id="login_btn"]').click()# 提取登录cookies,保存到json文件中save_cookies(driver)time.sleep(1)# 关闭驱动# driver.quit()

 七、处理验证码:

详见验证码问题博文:

https://mp.csdn.net/mp_blog/creation/editor/138138791

八、UI自动化设计模式

一般web自动化的设计模式有两种类型

1-POM设计模式

  • P:page代表页面

  • O:object代表对象

  • M:module代表模型

以页面对象为模型进行封装和使用

核心思想:对页面元素进行封装成类的属性(类属性),用例执行流程设计成页面类的方法(实例方法)

作用:可以减少代码的冗余,而且方便后期维护,若页面元素发生变化,只需要调整页面封装的属性即可,从而提高用例脚本的可维护性以及可读性

流程:

  • 页面需要被操作的元素定义成类的类属性

  • 页面需要执行的用例脚本定义成类的实例方法

  • 用例执行通过页面类创建一个页面对象,进行调用实例方法完成用例执行

pom模式封装页面:类属性和实例方法

class BackLogin:def __init__(self, driver):self.driver = driver# 封装页面属性#账号username = (By.XPATH, '//input[@name="adm_name"]')#密码password = (By.XPATH, '//input[@name="adm_password"]')#验证码-code = (By.XPATH, '//input[@name="adm_verify"]')#登录按钮btn_ok = (By.XPATH, '//*[@id="login_btn"]')# 获取实际结果res_txt = (By.XPATH, '//*[@id="login_msg"]')# 登录功能def login(self, username, password, code=None):self.find_element(*self.username, self.driver).send_keys(username)self.find_element(*self.password, self.driver).send_keys(password)self.find_element(*self.code, self.driver).send_keys(code)self.find_element(*self.btn_ok, self.driver).click()# 获取实际文本属性  -->这里采用自定义显示等待方法定位msg = self.find_element(*self.res_txt, self.driver, need_wait=True).text  return msg

在case脚本文件调用

back_login = BackLogin(driver)
msg = back_login.login("admin", "123456", "")
print(msg)assert msg == "验证码不能为空"

2-KDT设计模式

  • K:key代表关键字

  • D:driver代表驱动

  • T:test代表用例

核心思想:关键字驱动测试,可以实现极限封装0代码模式条件下,结合Excel表格数据进行去读,构造每个用例的步骤,完成用例的执行,得到最终的结果。

  • 可以实现自动化框架搭建以及功能测试人员的分工

  • 自动化测试框架搭建者负责关键字驱动测试的用例脚本设计

  • 功能测试负责编写Excel规范的用例步骤

3、BasePage元类封装-思想

BasePage一般在pom设计模式中当做所有页面的父类(页面元类)

BasePage属于抽象类的范畴

  • 不建议把BasePage类去实例化对象

  • 不能代表任何的实例对象

  • 把子类页面需要共同使用的资源(属性及方法)统一封装到BasePage元类中

  • 简单理解:子类中使用率非常高的属性和方法统一抽取封装到BasePage元类中

定义一个BasePage类

class BasePage:def __init__(self, driver):self.driver = driverdef wait(self, func):return WebDriverWait(self.driver, 5).until(func)def find_element(self, by, value, need_wait=False):def f(driver):# 判断当前元素是否有文本属性if driver.find_element(by, value).text:msg = driver.find_element(by, value).textif need_wait:  # 是否触发隐式等待,返回实际提示信息结果return msgelse:return Trueelse:return Trueself.wait(f)return self.driver.find_element(by, value)

其它页面类继承BasePage

class DealPage(BasePage):pass

九、动态元素定位专题

动态元素定位:

动态元素定位-CSDN博客

十、UI常见问题

1、正向和反向用例,元素可能不一致,不能将元素定位写死,考虑模糊定位

消失型元素怎么办?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/24546.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何检测UV胶的均匀性?

如何检测UV胶的均匀性&#xff1f; 检测UV胶的均匀性可以通过以下几种方法来实现&#xff1a; 肉眼目视检查&#xff1a; 这是最简单直接的方法。将UV胶涂在表面上&#xff0c;使用裸眼观察胶层的表面。特别注意是否存在气泡、颜色不均匀、裂纹或其他明显的不均匀性。如凹凸不…

异步复位和同步释放

文章目录 前言一、为什么需要复位呢&#xff1f;二、同步复位1. 同步复位定义2. 同步复位的实现3. 同步复位的优点和缺点同步复位优点同步复位缺点 三、异步复位1. 异步复位定义2. 异步复位的实现3. 异步复位的优点和缺点异步复位优点异步复位缺点 四、异步复位同步释放1. reco…

Html/HTML5常用标签的学习

课程目标 项目实战&#xff0c;肯定就需要静态网页。朝着做项目方式去学习静态网页。 01、编写第一个html工程结构化 cssjsimages/imgindex.html 归档存储和结构清晰就可以。 02、HTML标签分类 认知&#xff1a;标签为什么要分类&#xff0c;原因因为&#xff1a;分门别类…

关于怎么用Cubemx生成的USBHID设备实现读取一体的鼠标键盘设备(改进版)

主要最近做了一个要用STM32实现读取鼠标键盘一体的那种USB设备&#xff0c;STM32的界面上要和电脑一样的能通过这个USB接口实现鼠标移动&#xff0c;键盘的按键。然后我就很自然的去参考了正点原子的例程&#xff0c;可是找了一圈&#xff0c;发现正点原子好像用的库函数&#…

【计算机网络】对应用层协议中HTTPS协议的总结

˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如…

Oracle的优化器

sql优化第一步&#xff1a;搞懂Oracle中的SQL的执行过程 从图中我们可以看出SQL语句在Oracle中经历了以下的几个步骤&#xff1a; 语法检查&#xff1a;检查SQL拼写是否正确&#xff0c;如果不正确&#xff0c;Oracle会报语法错误。 语义检查&#xff1a;检查SQL中的访问对象…

【C++】用红黑树封装map、set

用红黑树封装map、set 1. 红黑树1.1 模板参数的控制1.1.1 Value1.1.2 KeyOfValue 1.2 正向迭代器1.2.1 构造函数1.2.2 begin()end()1.2.3 operator()1.2.4 operator--()1.2.5 operator*()1.2.6 operator->()1.2.7 operator()1.2.8 operator!()1.2.9 总代码 1.3 反向迭代器1.…

规则引擎-Easy rule

规则引擎-Easy rule 最近有几个项目中都出现了根据XX条件执行XX方法的业务&#xff0c;在动手之前脑中总会下意识的发现如果按照常规的去写代码&#xff0c;无论使用何种设计模式&#xff0c;都会出现不同程度上的代码冗余或大量if-else判断。 甚至说判断XX条件的代码和执行X…

2024中国大学计算机科学与技术专业排名(非常详细)零基础入门到精通,收藏这一篇就够了

今天 6 月 8 号&#xff0c;高考第二天&#xff0c;随着大部分地区的高考结束&#xff0c;接下来就是焦急的等待分数的公布&#xff0c;然后学生们就迎来人生中的一个重要时刻——选择大学和专业。 在众多专业中&#xff0c;计算机科学与技术一直是备受瞩目的热门选择&#xf…

【爬虫】使用Python爬取百度学术页面的标题、作者、摘要和关键词

目录 安装所需库编写爬虫代码解释运行脚本结果 在本文中&#xff0c;我将介绍如何使用Python编写一个网络爬虫&#xff0c;从百度学术页面提取研究论文的标题、作者、摘要和关键词。我们将使用 requests和 BeautifulSoup库来实现这一目标。 安装所需库 首先&#xff0c;确保…

uniapp引入uview无代码提示

前提安装正确&#xff1a; 无论是基于npm和Hbuilder X方式安装&#xff0c;一定要配置正确。 解决办法 以前在pages.json里面的写法&#xff1a; "easycom": {"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue" }但是现在hbuilderx要求规范ea…

驱动开发之 input 子系统

1.input 子系统介绍 input 就是输入的意思&#xff0c;input 子系统就是管理输入的子系统&#xff0c;和 pinctrl、gpio 子系统 一样&#xff0c;都是 Linux 内核针对某一类设备而创建的框架。比如按键输入、键盘、鼠标、触摸屏等 等这些都属于输入设备&#xff0c;不同的输入…

Redis到底支不支持事务?

文章目录 一、概述二、使用1、正常执行&#xff1a;2、主动放弃事务3、全部回滚:4、部分支持事务:5、WATCH: 三、事务三阶段四、小结 redis是支持事务的&#xff0c;但是它与传统的关系型数据库中的事务是有所不同的 一、概述 概念: 可以一次执行多个命令&#xff0c;本质是一…

《解决方案架构师修炼之道》读书笔记

1. 水在前面 第一次看到“解决方案架构师”这个词&#xff0c;就把这本书借回家翻了。最近也在网上看点资料&#xff0c;对比起来发现还是出版物内容更完整和更有体系&#xff0c;而且看书真的能让人安静下来。。。 《解决方案架构师修炼之道》所罗伯死里瓦斯塔瓦&#xff0c;内…

每天五分钟深度学习pytorch:pytorch中的广播机制是什么?

本文重点 在pytorch中经常有张量和张量之间的运算,那么有一点需要注意,那就是维度要匹配,如果维度不匹配就有可能出现问题。如果维度不一致,此时也可以同时进行操作,此时就需要使用pytorch中的广播机制,本节课程就讲解pytorch中的广播机制。 广播机制示意图 如上就是py…

RK3568技术笔记之三 SAIL-RK3568开发板板卡功能测试

从这里开始&#xff0c;就是老生常谈系列之一&#xff1a;板卡功能测试。 放一张图镇一下帖 按照我自己顺手的方式&#xff0c;把这板子功能测一下。 先把开发板串口信息打印出来。 工具 功能 备注 电脑&#xff08;必备&#xff09; 提供使用终端软件环境 需要具备至少…

【ArcGIS微课1000例】0117:ArcGIS中如何将kml(kmz)文件转json(geojson)?

文章目录 一、kml获取方式二、kml转图层三、图层转json一、kml获取方式 kml文件是一种很常用的数据格式,可以从谷歌地球(googleearth)获取某一个地区的kml范围文件,如青海湖(做好的kml文件可以从配套实验数据包0117.rar中获取)。 二、kml转图层 打开【KML转图层】工具,…

2 程序的灵魂—算法-2.4 怎样表示一个算法-2.4.2 用流程图表示算法

流程图表示算法&#xff0c;直观形象&#xff0c;易于理解。 【例 2.6】将例 2.1 求 5!的算用流程图表示。 【例 2.7】将例 2.2 的算用流程图表示。 【例 2.8】将例 2.3 判定闰年的算用流程图表示。

问题:功夫菜产品的成本由哪几方面构成() #经验分享#其他

问题&#xff1a;功夫菜产品的成本由哪几方面构成&#xff08;&#xff09; A&#xff0e;材料成本&#xff08;标准投料2%损耗&#xff09;包材成本直接人工费固定加工费 B&#xff0e;&#xff08;材料成本包材成本&#xff09;*&#xff08;1加价率&#xff09; C&#x…