超详细的selenium使用指南
🍅 视频学习:文末有免费的配套视频可观看
🍅 点击文末小卡片 ,免费获取软件测试全套资料,资料在手,涨薪更快
概述
selenium是网页应用中最流行的自动化测试工具,可以用来做自动化测试或者浏览器爬虫等。官网地址为:相对于另外一款web自动化测试工具QTP来说有如下优点:
- 免费开源轻量级,不同语言只需要一个体积很小的依赖包
- 支持多种系统,包括Windows,Mac,Linux
- 支持多种浏览器,包括Chrome,FireFox,IE,safari,opera等
- 支持多语言,包括Java,C,python,c#等主流语言
- 支持分布式测试用例执行
python+selenium环境安装
首先需要安装python(推荐3.7+)环境,然后直接用pip install selenium安装依赖包即可。
另外还需要下载浏览器相应的webdriver驱动程序,注意下载的驱动版本一定要匹配浏览器版本。
- Firefox浏览器驱动:
- Chrome浏览器驱动:
- IE浏览器驱动:
- Edge浏览器驱动:
- Opera浏览器驱动:
下载以后可以把驱动程序加到环境变量,这样使用时就不用手动指定驱动程序路径。
使用selenium启动浏览器
可以在python中使用下面的代码启动一个Chrome浏览器,然后控制这个浏览器的行为或者读取数据。
from selenium import webdriver# 启动Chrome浏览器,要求chromedriver驱动程序已经配置到环境变量
# 将驱动程序和当前脚本放在同一个文件夹也可以
driver = webdriver.Chrome()# 手动指定驱动程序路径
driver = webdriver.Chrome(r'D:/uusama/tools/chromedriver.exe')driver = webdriver.Ie() # Internet Explorer浏览器
driver = webdriver.Edge() # Edge浏览器
driver = webdriver.Opera() # Opera浏览器
driver = webdriver.PhantomJS() # PhantomJSdriver.get('http://uusama.com') # 打开指定路径的页面
启动的时候还可以设置启动参数,比如下面的代码实现启动时添加代理,并且忽略https证书校验。
from selenium import webdriver# 创建chrome启动选项对象
options = webdriver.ChromeOptions()options.add_argument("--proxy-server=127.0.0.1:16666") # 设置代理
options.add_argument("---ignore-certificate-errors") # 设置忽略https证书校验
options.add_experimental_option("excludeSwitches", ["enable-logging"]) # 启用日志# 设置浏览器下载文件时保存的默认路径
prefs = {"download.default_directory": get_download_dir()}
options.add_experimental_option("prefs", prefs)
driver = webdriver.Chrome(options=options)
启动的时候还可以设置启动参数,比如下面的代码实现启动时添加代理,并且忽略https证书校验。
from selenium import webdriver# 创建chrome启动选项对象
options = webdriver.ChromeOptions()options.add_argument("--proxy-server=127.0.0.1:16666") # 设置代理
options.add_argument("---ignore-certificate-errors") # 设置忽略https证书校验
options.add_experimental_option("excludeSwitches", ["enable-logging"]) # 启用日志# 设置浏览器下载文件时保存的默认路径
prefs = {"download.default_directory": get_download_dir()}
options.add_experimental_option("prefs", prefs)
driver = webdriver.Chrome(options=options)
一些非常有用的启动选项,下面使用的options = webdriver.ChromeOptions():
- options.add_argument("--proxy-server=127.0.0.1:16666"): 设置代理,可以结合mitmproxy进行抓包等
- option.add_experimental_option('excludeSwitches', ['enable-automation']): 设置绕过selenium检测
- options.add_argument("---ignore-certificate-errors"): 设置忽略https证书校验
- options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2}): 设置不请求图片模式加快页面加载速度
- chrome_options.add_argument('--headless'): 设置无头浏览器
selenium页面加载等待和检测
使用selenium打开页面以后,还不能立刻操作,需要等到待处理页面元素加载完成,这时就需要检测和等待页面元素加载。
使用time.sleep()等待
最简单的方法就是打开页面以后,使用time.sleep()强制等待一定时间,该方法只能设置一个固定时间等待,如果页面提前加载完成,则会空等阻塞。
from time import sleep
from selenium import webdriverdriver = webdriver.Chrome()
driver.get('http://uusama.con')
time.sleep(10)
print('load finish')
使用implicitly_wait设置最长等待时间
另外还可以使用implicitly_wait设置最长等待时间,如果在给定时间内页面加载完成或者已经超时,才会执行下一步。该方法会等到所有资源全部加载完成,也就是浏览器标签栏的loading图表不再转才会执行下一步。有可能页面元素已经加载完成,但是js或者图片等资源还未加载完成,此时还需要等待。
另需注意使用implicitly_wait只需设置一次,且对整个driver生命周期都起作用,凡是遇到页面正在加载都会阻塞。
示例如下:
from selenium import webdriverdriver = webdriver.Chrome()
driver.implicitly_wait(30) # 设置最长等30秒
driver.get('http://uusama.com')
print(driver.current_url)driver.get('http://baidu.com')
print(driver.current_url)
使用WebDriverWait设置等待条件
使用WebDriverWait(selenium.webdriver.support.wait.WebDriverWait)能够更加精确灵活地设置等待时间,WebDriverWait可在设定时间内每隔一段时间检测是否满足某个条件,如果满足条件则进行下一步操作,如果超过设置时间还不满足,则抛出TimeoutException异常,其方法声明如下:
WebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None)
其中各参数含义如下:
- driver:浏览器驱动
- timeout:最长超时时间,默认以秒为单位
- poll_frequency:检测的间隔(步长)时间,默认为0.5秒
- ignored_exceptions:忽略的异常,即使在调用until()或until_not()的过程中抛出给定异常也不中断
WebDriverWait()一般配合until()或until_not()方法使用,表示等待阻塞直到返回值为True或者False,需要注意这两个方法的参数都需是可调用对象,即方法名称,可以使用expected_conditions模块中的方法或者自己封装的方法。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditionsdriver = webdriver.Chrome()
driver.get("http://baidu.com")# 判断id为`input`的元素是否被加到了dom树里,并不代表该元素一定可见,如果定位到就返回WebElement
element = WebDriverWait(driver, 5, 0.5).until(expected_conditions.presence_of_element_located((By.ID, "s_btn_wr")))# implicitly_wait和WebDriverWait都设置时,取二者中最大的等待时间
driver.implicitly_wait(5)# 判断某个元素是否被添加到了dom里并且可见,可见代表元素可显示且宽和高都大于0
WebDriverWait(driver,10).until(EC.visibility_of_element_located((By.ID, 'su')))# 判断元素是否可见,如果可见就返回这个元素
WebDriverWait(driver,10).until(EC.visibility_of(driver.find_element(by=By.ID, value='kw')))
下面列出expected_conditions常用的一些方法:
- title_is: 判断当前页面title是否精确等于预期
- title_contains: 判断当前页面title是否包含预期字符串
- presence_of_element_located: 判断某个元素是否被加到了dom树里,并不代表该元素一定可见
- visibility_of_element_located: 判断某个元素是否可见(元素非隐藏,并且元素的宽和高都不等于0)
- visibility_of: 跟上面的方法做一样的事情,只是上面的方法要传入locator,这个方法直接传定位到的element就好了
- presence_of_all_elements_located: 判断是否至少有1个元素存在于dom树中。举个例子,如果页面上有n个元素的class都是'column-md-3',那么只要有1个元素存在,这个方法就返回True
- text_to_be_present_in_element: 判断某个元素中的text是否包含了预期的字符串
- text_to_be_present_in_element_value: 判断某个元素中的value属性是否包含了预期的字符串
- frame_to_be_available_and_switch_to_it: 判断该frame是否可以switch进去,如果可以的话,返回True并且switch进去,否则返回False
- invisibility_of_element_located: 判断某个元素中是否不存在于dom树或不可见
- element_to_be_clickable: 判断某个元素中是否可见并且是enable的,这样的话才叫clickable
- staleness_of: 等某个元素从dom树中移除,注意,这个方法也是返回True或False
- element_to_be_selected: 判断某个元素是否被选中了,一般用在下拉列表
- element_selection_state_to_be: 判断某个元素的选中状态是否符合预期
- element_located_selection_state_to_be: 跟上面的方法作用一样,只是上面的方法传入定位到的element,而这个方法传入locator
检测document是否加载完成
另外还可以使用driver.execute_script('return document.readyState;') == 'complete'来检测document是否加载完成。
注意document加载完成,是不包括那种异步加载ajax请求动态渲染dom的,这种需要使用WebDriverWait检测某个元素是否渲染完成。
selenium元素定位和读取
查找元素
selenium提供了一系列api方便获取chrome中的元素,这些API都返回WebElement对象或其列表,如:
- find_element_by_id(id): 查找匹配id的第一个元素
- find_element_by_class_name(): 查找匹配class的第一个元素
- find_elements_by_xpath(): 查找匹配xpath的所有元素
- find_elements_by_css_selector(): 查找匹配css选择器的所有元素
其实可以看WebDriver类里面的实现源码,其核心实现都是调用两个基本函数:
- find_element(self, by=By.ID, value=None): 查找匹配策略的第一个元素
- find_elements(self, by=By.ID, value=None): 查找匹配策略的所有元素
其中by参数可以是ID, CSS_SELECTOR, CLASS_NAME, XPATH等。下面举几个简单的例子:
- 通过xpath查询包含文本登录的第一个元素: find_element_by_xpath("//*[contains(text(),'登录')]")
- 查询包含类名refresh的所有元素: find_elements_by_class_name('refresh')
- 查询table表格的第二行: find_element_by_css_selector('table tbody > tr:nth(2)')
dom元素交互
上面介绍的元素查找结果WebElement对象,常用的api有:
- element.text: 返回元素的文本内容(包括所有后代节点的内容),注意如果元素display=none则返回为空字符串
- element.screenshot_as_png: 元素的截图
- element.send_keys("input"): 元素输入框输入input字符串
- element.get_attribute('data-v'): 获取data-v名称属性值,除了自定义节点属性,还可以获取如textContent等属性
- element.is_displayed(): 判断元素是否用户可见
- element.clear(): 清除元素文本
- element.click(): 点击元素,如果元素不可点击会抛出ElementNotInteractableException异常
- element.submit(): 模拟表单提交
查找元素失败处理
如果找不到指定元素,则会抛出NoSuchElementException异常,而且需要注意,display=none的元素是可以获取到的,凡是在dom节点中的元素都可以获取到。
而且实际使用的时候要注意一些js代码动态创建的元素,可能需要轮询获取或者监控。
一个检查是否存在指定元素的方法如下:
def check_element_exists(xpath):try:driver.find_element_by_xpath(xpath)except NoSuchElementException:return Falsereturn True
selenium交互控制
ActionChains动作链
webdriver通过ActionChains对象来模拟用户操作,该对象表示一个动作链路队列,所有操作会依次进入队列但不会立即执行,直到调用perform()方法时才会执行。其常用方法如下:
- click(on_element=None): 单击鼠标左键
- click_and_hold(on_element=None): 点击鼠标左键,不松开
- context_click(on_element=None): 点击鼠标右键
- double_click(on_element=None): 双击鼠标左键
- send_keys(*keys_to_send): 发送某个键到当前焦点的元素
- send_keys_to_element(element, *keys_to_send): 发送某个键到指定元素
- key_down(value, element=None): 按下某个键盘上的键
- key_up(value, element=None): 松开某个键
- drag_and_drop(source, target): 拖拽到某个元素然后松开
- drag_and_drop_by_offset(source, xoffset, yoffset): 拖拽到某个坐标然后松开
- move_by_offset(xoffset, yoffset): 鼠标从当前位置移动到某个坐标
- move_to_element(to_element): 鼠标移动到某个元素
- move_to_element_with_offset(to_element, xoffset, yoffset): 移动到距某个元素(左上角坐标)多少距离的位置
- perform(): 执行链中的所有动作
- release(on_element=None): 在某个元素位置松开鼠标左键
模拟鼠标事件
下面代码模拟鼠标移动,点击,拖拽等操作,注意操作时需要等待一定时间,否则页面还来不及渲染。
from time import sleep
from selenium import webdriver
# 引入 ActionChains 类
from selenium.webdriver.common.action_chains import ActionChainsdriver = webdriver.Chrome()
driver.get("https://www.baidu.cn")
action_chains = ActionChains(driver)target = driver.find_element_by_link_text("搜索")
# 移动鼠标到指定元素然后点击
action_chains.move_to_element(target).click(target).perform()
time.sleep(2)# 也可以直接调用元素的点击方法
target.click()
time.sleep(2)# 鼠标移动到(10, 50)坐标处
action_chains.move_by_offset(10, 50).perform()
time.sleep(2)# 鼠标移动到距离元素target(10, 50)处
action_chains.move_to_element_with_offset(target, 10, 50).perform()
time.sleep(2)# 鼠标拖拽,将一个元素拖动到另一个元素
dragger = driver.find_element_by_id('dragger')
action.drag_and_drop(dragger, target).perform()
time.sleep(2)# 也可以使用点击 -> 移动来实现拖拽
action.click_and_hold(dragger).release(target).perform()
time.sleep(2)
action.click_and_hold(dragger).move_to_element(target).release().perform()
模拟键盘输入事件
通过send_keys模拟键盘事件,常用有:
- 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.F1): 键盘 F1
- send_keys(Keys.CONTROL,'a'): 全选(Ctrl+A)
- send_keys(Keys.CONTROL,'c'): 复制(Ctrl+C)
- send_keys(Keys.CONTROL,'x'): 剪切(Ctrl+X)
- send_keys(Keys.CONTROL,'v'): 粘贴(Ctrl+V)
示例:定位到输入框,然后输入内容
# 输入框输入内容
driver.find_element_by_id("kw").send_keys("uusamaa")# 模拟回车删除多输入的一个字符a
driver.find_element_by_id("kw").send_keys(Keys.BACK_SPACE)
警告框处理
用于处理调用alert弹出的警告框。
- driver.switch_to_alert(): 切换到警告框
- text:返回alert/confirm/prompt中的文字信息,比如js调用alert('failed')则会获取failed字符串
- accept():接受现有警告框
- dismiss():关闭现有警告框
- send_keys(keysToSend):将文本发送至警告框
selenium浏览器控制
基本常用api
下面列出一些非常实用的浏览器控制api:
- driver.current_url: 获取当前活动窗口的url
- driver.switch_to_window("windowName"): 移动到指定的标签窗口
- driver.switch_to_frame("frameName"): 移动到指定名称的iframe
- driver.switch_to_default_content(): 移动到默认文本内容区
- driver.maximize_window(): 将浏览器最大化显示
- driver.set_window_size(480, 800): 设置浏览器宽480、高800显示
- driver.forword(), driver.back(): 浏览器前进和后退
- driver.refresh(): 刷新页面
- driver.close(): 关闭当前标签页
- driver.quiit(): 关闭整个浏览器
- driver.save_screenshot('screen.png'): 保存页面截图
- driver.maximize_window(): 将浏览器最大化显示
- browser.execute_script('return document.readyState;'): 执行js脚本
selenium读取和加载cookie
使用get_cookies和add_cookie可以实现将cookie缓存到本地,然后启动时加载,这样可以保留登录态。实现如下
import os
import json
from selenium import webdriverdriver = webdriver.Chrome()
driver.get("https://www.baidu.cn")# 读取所有cookie并保存到文件
cookies = driver.get_cookies()
cookie_save_path = 'cookie.json'
with open(cookie_save_path, 'w', encoding='utf-8') as file_handle:json.dump(cookies, file_handle, ensure_ascii=False, indent=4)# 从文件读取cookie并加载到浏览器
with open(cookie_save_path, 'r', encoding='utf-8') as file_handle:cookies = json.load(file_handle)for cookie in cookies:driver.add_cookie(cookie)
selenium打开新的标签页窗口
使用driver.get(url)会默认在第一个标签窗口打开指定连接,点击页面中的_blank的链接时也会打开一个新的标签窗口。
还可以用下面的方式手动打开一个指定页面的标签窗口,需要注意打开新窗口或者关闭以后,还需要手动调用switch_to.window切换当前活动的标签窗口,否则会抛出NoSuchWindowException异常。
from selenium import webdriverdriver = webdriver.Chrome()
driver.get("https://www.baidu.cn")new_tab_url = 'http://uusama.com'
driver.execute_script(f'window.open("{new_tab_url}", "_blank");')
time.sleep(1)# 注意:必须调用switch_to.window手动切换window,否则会找不到tab view
# 聚焦到新打开的tab页面,然后关闭
driver.switch_to.window(driver.window_handles[1])
time.sleep(2)
driver.close() # 关闭当前窗口# 手动回到原来的tab页面
driver.switch_to.window(driver.window_handles[0])
time.sleep(1)
除了使用execute_script外,还可以使用模拟打开新tab页按键的方式新建一个标签页窗口:
- driver.find_element_by_tag_name('body').send_keys(Keys.CONTROL + 't')
- ActionChains(driver).key_down(Keys.CONTROL).send_keys('t').key_up(Keys.CONTROL).perform()
selenium一些问题记录
获取隐藏元素的文本内容
如果一个元素是隐藏的,即display=none,虽然可以通过find_element查找到该元素,但是用element.text属性是获取不到该元素文本内容的,其值是空字符串,这时可以用下面的方式获取:
element = driver.find_element_by_id('uusama')
driver.execute_script("return arguments[0].textContent", element)
driver.execute_script("return arguments[0].innerHTML", element)# 相应的也可以把隐藏的元素设置为非隐藏
driver.execute_script("arguments[0].style.display = 'block';", element)
浏览器崩溃WebDriverException异常处理
比如在Chrome中长时间运行一个页面会出现Out Of Memory内存不足的错误,此时WebDriver会抛出WebDriverException异常,基本所有api都会抛出这个异常,这个时候需要捕获并进行特殊处理。
我的处理方式是记录页面的一些基本信息,比如url,cookie等,并定期写入到文件中,如果检测到该异常,则重启浏览器并且加载url和cookie等数据。
selenium抓取页面请求数据
网上有通过driver.requests或者通过解析日志来获取页面请求的方式,但是我感觉都不是很好使。最后使用mitmproxy代理进行抓包处理,然后启动selenium时填入代理来实现。
proxy.py为在mitmproxy基础上封装的自定义代理请求处理,其代码如下:
import os
import gzip
from mitmproxy.options import Options
from mitmproxy.proxy.config import ProxyConfig
from mitmproxy.proxy.server import ProxyServer
from mitmproxy.tools.dump import DumpMaster
from mitmproxy.http import HTTPFlow
from mitmproxy.websocket import WebSocketFlowclass ProxyMaster(DumpMaster):def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)def run(self, func=None):try:DumpMaster.run(self, func)except KeyboardInterrupt:self.shutdown()def process(url: str, request_body: str, response_content: str):# 抓包请求处理,可以在这儿转存和解析数据passclass Addon(object):def websocket_message(self, flow: WebSocketFlow):# 监听websockt请求passdef response(self, flow: HTTPFlow):# 避免一直保存flow流,导致内存占用飙升# flow.request.headers["Connection"] = "close"# 监听http请求响应,并获取请求体和响应内容url = flow.request.urlrequest_body = flow.requestresponse_content = flow.response# 如果返回值是压缩的内容需要进行解压缩if response_content.data.content.startswith(b'\x1f\x8b\x08'):response_content = gzip.decompress(response_content.data.content).decode('utf-8')Addon.EXECUTOR.submit(process, url, request_body, response_content)def run_proxy_server():options = Options(listen_host='0.0.0.0', listen_port=16666)config = ProxyConfig(options)master = ProxyMaster(options, with_termlog=False, with_dumper=False)master.server = ProxyServer(config)master.addons.add(Addon())master.run()if __name__ == '__main__':with open('proxy.pid', mode='w') as fin:fin.write(os.getpid().__str__())run_proxy_server()
在使用mitmproxy过程中,随着时间推移proxy.py会出现占用内存飙升的问题,在github的issue区有人也遇到过,有说是因为http连接keep-alive=true请求会一直保存不会释放,导致请求越多越占用内存,然后通过添加flow.request.headers["Connection"] = "close"来手动关闭连接,我加了以后有一定缓解,但还是不能从根本上解决。
最后通过写入proxy.pid记录代理程序进程,然后用另外一个程序定时重启proxy.py来解决内存泄漏的问题。
同时,在这我为大家准备了一份软件测试视频教程(含面试、接口、自动化、性能测试等),就在下方,需要的可以直接去观看。
【2024最新版】Python自动化测试15天从入门到精通,10个项目实战,允许白嫖。。。
相关文章:
![](https://www.ngui.cc/images/no-images.jpg)
超详细的selenium使用指南
🍅 视频学习:文末有免费的配套视频可观看 🍅 点击文末小卡片 ,免费获取软件测试全套资料,资料在手,涨薪更快 概述 selenium是网页应用中最流行的自动化测试工具,可以用来做自动化测试或者浏览器…...
![](https://img-blog.csdnimg.cn/direct/a0b3651b19ab4ead929201d0023e30f1.png)
LogicFlow 学习笔记——1. 初步使用 LogicFlow
什么是 LogicFlow LogicFlow 是一个开源的前端流程图编辑器和工作流引擎,旨在帮助开发者和业务人员在网页端创建、编辑和管理复杂的业务流程和工作流。它提供了一个直观的界面和强大的功能,使得设计和管理工作流变得更加高效和便捷。 官网地址ÿ…...
![](https://img-blog.csdnimg.cn/img_convert/9ea475f13417fe481bf2de6f0df3a9b9.webp?x-oss-process=image/format,png)
场外个股期权通道业务是什么意思?
今天带你了解场外个股期权通道业务是什么意思?场外个股期权业务是指在沪深交易所之外进行的个股期权交易。它是一种非标准化的合约,不在交易所内进行交割。 场外个股期权通道业务,是指投资者通过与场外个股期权机构通道签订合约,购…...
![](https://img-blog.csdnimg.cn/direct/7ac21a1c9c944a91aa7e6f01c232cd80.png)
分页插件结合collection标签后分页数量不准确的问题
问题1:不使用collection 聚合分页正确 简单列子 T_ATOM_DICT表有 idname1原子12原子23原子34原子45原子56原子6 T_ATOM_DICT_AUDIT_ROUTE表审核记录表有 idaudit1拒绝1通过4拒绝 我要显示那些原子审核了,我把两个表inner join 就是那些原子审核过了 idnameaudit1原子1拒绝…...
![](https://www.ngui.cc/images/no-images.jpg)
git diff 命令
目录标题 [Q&A] git diff 作用常见用法比较工作目录与暂存区比较暂存区与最近一次提交比较工作目录与最近一次提交比较两个具体的提交之间差异 [Q&A] git diff 作用 git diff 用于展示不同版本之间文件内容的变化。 常见用法 比较工作目录与暂存区 显示工作目录中尚…...
![](https://www.ngui.cc/images/no-images.jpg)
Code Review常用术语
CR: Code Review. 请求代码审查。PR: pull request. 拉取请求,给其他项目提交代码。MR: merge request. 合并请求。LGTM: Looks Good To Me.对我来说,还不错。表示认可这次PR,同意merge合并代码到远程仓库。…...
![](https://www.ngui.cc/images/no-images.jpg)
HashMap 源码中的巧妙小技巧
根据容量计算大于容量的最小的哈希表的大小(table的length),这里的length需要满足length2^n,也就是我们需要根据容量算出最小的n的值 static final int tableSizeFor(int cap) {int n cap - 1;n | n >>> 1;n | n >>> 2;n | n >&g…...
![](https://img-blog.csdnimg.cn/direct/26017dcf8f7b46f7aaa483d3120bcf47.jpeg)
极具吸引力的小程序 UI 风格
极具吸引力的小程序 UI 风格...
![](https://img-blog.csdnimg.cn/direct/d2940db6102e4f36868e660b4be6aec5.png)
数据库 | 试卷五试卷六试卷七
1. 主码不相同!相同的话就不能唯一标识非主属性了 2.从关系规范化理论的角度讲,一个只满足 1NF 的关系可能存在的四方面问题 是: 数据冗余度大,插入异常,修改异常,删除异常 3.数据模型的三大要素是什么&…...
![](https://img-blog.csdnimg.cn/direct/9cff0ab71325458591db2224e57151a3.png)
网页五子棋对战项目测试(selenium+Junit5)
目录 网页五子棋对战项目介绍 网页五子棋对战测试的思维导图 网页五子棋对战的UI自动化测试 测试一:测试注册界面 测试二:测试登陆界面 测试三:测试游戏大厅界面 测试四:测试游戏房间界面以及观战房间界面 测试五&#…...
![](https://img-blog.csdnimg.cn/direct/c28abbf190484955a028aec3d4f2d08e.png)
stable diffusion 局部重绘 reference-only api 接口调试
webUI api payload 插件生成的接口参数不准确,reference-only 的image不是对象,就是不同字符串字段,直接传,不是套image。 综上,那个插件参数不确定,应直接看插件的源码,看它接受什么参数 错误…...
![](https://img-blog.csdnimg.cn/direct/59e3408b23a54dfdbbae4849bc6e0ad2.png#pic_center)
浪潮信息内存故障预警技术再升级 服务器稳定性再获提升
浪潮信息近日对其内存故障智能预警修复技术进行了全面升级,再次取得技术突破。此次升级后,公司服务器的宕机率实现了80%锐降,再次彰显了浪潮信息在服务器技术领域的卓越能力。 浪潮信息全新升级服务器内存故障智能预警修复技术MUPR (Memory …...
![](https://img-blog.csdnimg.cn/direct/4829d5675ba5480aaf7fbc29afd81a9d.png)
JWT整合Gateway实现鉴权(RSA与公私密钥工具类)
一.业务流程 1.使用RSA生成公钥和私钥。私钥保存在授权中心,公钥保存在网关(gateway)和各个信任微服务中。 2.用户请求登录。 3.授权中心进行校验,通过后使用私钥对JWT进行签名加密。并将JWT返回给用户 4.用户携带JWT访问 5.gateway直接通过公钥解密JWT进…...
![](https://www.ngui.cc/images/no-images.jpg)
vue实现全屏screenfull-封装组件
1. 安装依赖 npm install --save screenfull 2. 引用 import screenfull from "screenfull" 3.封装fullScreen/index: <template><div><el-tooltip v-if"!content" effect"dark" :content"fullscreenTips" placement&…...
![](https://img-blog.csdnimg.cn/direct/ccd62007d4f94ee0bf0ed97d0585a68f.png)
【LinkedList与链表】
目录 1,ArrayList的缺陷 2,链表 2.1 链表的概念及结构 2.2 链表的实现 2.2.1 无头单向非循环链表实现 3,LinkedList的模拟实现 3.1 无头双向链表实现 4,LinkedList的使用 4.1 什么是LinkedList 4.2 LinkedList的使用 5…...
![](https://img-blog.csdnimg.cn/e6349c2b37c74f028ee1c1b844541c04.png)
为数据安全护航,袋鼠云在数据分类分级上的探索实践
在大数据时代,数据具有多源异构的特性,且价值各异,企业需依据数据的重要性、价值指数等予以区分,以利采取不同的数据保护举措,避免数据泄露。故而,数据分类分级管理属于数据安全保护中极为重要的环节之一。…...
![](https://www.ngui.cc/images/no-images.jpg)
Spring 循环依赖详解
Spring 循环依赖详解 1. 引言 在Spring框架中,依赖注入(Dependency Injection, DI)是其核心功能之一,它通过配置来管理对象的创建和它们之间的依赖关系。然而,在复杂的应用程序中,开发人员有时会遇到循环…...
![](https://www.ngui.cc/images/no-images.jpg)
项目经理真的不能太“拧巴”
前期的项目经理经常是“拧巴”的,就是心里纠结、思路混乱、行动迟缓。对于每天需要面对各种挑战、协调各方资源、确保项目顺利进行的项目经理来说,这种“拧巴”不仅会让自己陷入内耗中,还会让项目出大问题。 项目计划总是改来改去࿰…...
![](https://img-blog.csdnimg.cn/img_convert/eade77740abb9a94d87449817c8d8577.png)
企业如何选择合适的CRM工具?除Salesforce之外的10大主流选择
对比salesforce,其他10款优秀CRM:纷享销客CRM、Zoho CRM、腾讯企点、销售易、企业微信 (WeCom)、Odoo CR、OroCRM、金蝶、用友CRM、EspoCRM 虽然Salesforce以其全面的功能和强大的市场占有率在海外收获了许多客户,但Salesforce在国内市场的接…...
![](https://img-blog.csdnimg.cn/img_convert/3ad834f6aa4cb6262ee0cc7b878a4684.png)
每年1-1.2万人毕业,男女比例约3:1,测绘工程的就业率如何
测绘工程,一个让人闻风丧胆的理科专业,虎扑评分4.2: 干过测绘的,苦不苦只有大家心里知道,带大家来感受一下,兄弟们的精神状态都十分美妙: 测绘专业到底是什么情况? PS.测绘分为本科…...
![](https://img-blog.csdnimg.cn/img_convert/249b1678454e42d83f5d1b00f6c9fa3a.png)
JimuReport 积木报表 v1.7.6 版本发布,免费的低代码报表
项目介绍 一款免费的数据可视化报表工具,含报表和大屏设计,像搭建积木一样在线设计报表!功能涵盖,数据报表、打印设计、图表报表、大屏设计等! Web 版报表设计器,类似于excel操作风格,通过拖拽完…...
![](https://www.ngui.cc/images/no-images.jpg)
“灵活就业者“超两亿人 游戏开发者如何破局?
随着“灵活就业”者数量突破两亿,我相信“寒气”已经传递到每一位普通人!对于游戏行业的“灵活就业”者,应当如何破局? 首先应该恭喜大家,选择了一个相对“稳健”的行业,无论大环境如何,游戏/软…...
![](https://img-blog.csdnimg.cn/direct/6958308f180845938027ec3d37b680d8.png)
MySQL事务与存储引擎
一、事务的概念 是一种机制、一个操作序列,包含了一组数据库操作命令,并且把所有的命令作为一个整体一起向系统提交或撤销操作请求,即这一组数据库命令要么都执行,要么都不执行是一个不可分割的工作逻辑单元,在数据库…...
![](https://www.ngui.cc/images/no-images.jpg)
总是给数据库表字段设置默认值的好处
1、NOT NULL DEFAULT 的好处 在设计数据库表结构时,将字段设置为不能为空并设置默认值有以下几种好处: 1.1、数据完整性 通过设置字段不能为空,可以确保每条记录都包含必要的数据,从而保证了数据的完整性。例如,在用…...
![](https://img-blog.csdnimg.cn/direct/603b3459b0d549eca40c0fbc82d4e45a.png)
11.2 Go 常用包介绍
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…...
![](https://img-blog.csdnimg.cn/direct/4edc25bcaa6e4ee8aaaaca29907c3701.png)
Sqlite3数据库基本使用
一、基本概念 数据:能够输入计算机并能被计算机程序识别和处理的信息集合 数据库:长期存储在计算机内、有组织的、可共享的大量数据的集合 DBMS:位于用户与操作系统之间的一层数据管理软件,用于操纵和管理数据库 二、安装 在线…...
![](https://img-blog.csdnimg.cn/direct/70b015c5f44b4d74967b95a33ba281af.png)
实现贪吃蛇小游戏【简单版】
1. 贪吃蛇游戏设计与分析 1.1 地图 我们最终的贪吃蛇大纲要是这个样子,那我们的地图如何布置呢? 这里不得不讲⼀下控制台窗口的⼀些知识,如果想在控制台的窗口中指定位置输出信息,我们得知道该位置的坐标,所以首先介…...
![](https://www.ngui.cc/images/no-images.jpg)
uniapp实现内嵌其他网页的功能
一、用到的知识点 页面跳转页面间跳转,参数传递web-view使用 二、使用navigator 页面跳转。 navigator 组件类似HTML中的<a>组件,但只能跳转本地页面。目标页面必须在pages.json中注册。所以这么写是不行的: <navigator url&quo…...
![](https://www.ngui.cc/images/no-images.jpg)
【Ruby简单脚本01】查看wifi密码
脚本 # 使用io库 def get_cmd_result(cmd) IO.popen(cmd,:external_encoding>GBK).read.encode("utf-8") end def list_wifi wifi_pwds Hash.new # 获取所有wifi文件 o1 get_cmd_result("netsh wlan show profiles") # 获取所有匹配结果 …...
![](https://img-blog.csdnimg.cn/img_convert/047976793ef516c92fdb18a10c2beed9.png)
VSG/VSA 矢量信号模拟/分析软件
_Ceyear思仪 _ VSG/VSA 矢量信号模拟/分析软件 苏州新利通仪器仪表 在现代无线通信中,IQ调制属于标准配置,经常应用于通信系统的信号调制和解调环节。IQ调制的应用简化了通信设备的硬件结构,同时提高了频谱资源的利用效率,提…...
![](/images/no-images.jpg)
长春火车站最新通知/手机百度网盘登录入口
-------装箱和拆箱--------- 数据类型按照存储 方式 可以分为值类型和引用类型,两者仍然可以相互转换,将值类型转换为引用类型的过程称为装箱。反之则为拆箱。 static void Main(string[] args){int i123;object oi ; //装箱int i (int)0 ;/、拆箱}--…...
中铁建设集团官方网站/seo服务顾问
问题描述:要在有限的时间内安排尽量多的会议。 贪心策略(前提是会议不冲突,也就是两个会议不同时进行): 1. 每次选择持续时间最短的安排。这样如果开始时间很迟,安排的会议也很少,所以策略不是…...
![](/images/no-images.jpg)
网站吸流量/站长工具网站测速
在当前的大环境里,实体店产品同质化非常严重。经过一条商业街,我们会明显发现,同行业的几家店铺不管是产品类型、装修风格还是产品价格都十分相似,给人的感觉就是把相同的产品放在不同的店里。 这个时候,优秀的营销策…...
![](https://img-blog.csdnimg.cn/img_convert/4b151825acea825319cce4803335086b.png)
深圳龙华汽车站附近有做网站建设的/站长之家工具高清
展示页面效果展示list.wxml设置开头以及背景样式,设置固定发布按钮上传人:{{item.name}}上传时间:{{item.time}}list.wxsspage {background: #2db7f5;}/* 卡片 */.item-container {box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);transition: 0…...
![](/images/no-images.jpg)
郑州网站建设方案报价/网上做广告推广
在phpmyadmin中显示上传文件最大问8M,可我的单个sql文件超过50M,尝试上传,上传依然可以进行,但上传到8M即操作终止,phpmyadmin提示上传失败。该问题实际上是php的配置问题,在php.ini中有两个参数需要关注&a…...
![](https://img-blog.csdnimg.cn/e01f608394564e2f8c5067209cb50a9f.jpeg#pic_center)
实事热点新闻事件/网站建设优化
DAMA认证为数据管理专业人士提供职业目标晋升规划,彰显了职业发展里程碑及发展阶梯定义,帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力,促进开展工作实践应用及实际问题解决,形成企业所需的新数字经济下的核心职业…...