Python自动化测试系列[v1.0.0][常见页面操作处理]

[智能等待]

# 用于实现智能等待页面元素的出现
# encoding = utf-8
"""
__title__ = ''
__author__ = 'davieyang'
__mtime__ = '2018/4/21'
"""
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import difflibclass Smart_Waiting:def __init__(self, driver):self.locationTypeDict = {"xpath": By.XPATH,"id": By.ID,"name": By.NAME,"css_selector": By.CSS_SELECTOR,"class_name": By.CLASS_NAME,"tag_name": By.TAG_NAME,"link_text": By.LINK_TEXT,"partial_link_text": By.PARTIAL_LINK_TEXT}self.driver = driverself.wait = WebDriverWait(self.driver, 30)def presence_of_element_located(self, location_type, locator_expression, *args):"""显示等待页面元素出现在DOM中,但并不一定可见,存在则返回该页面元素对象:param location_type::param locator_expression::param args::return:"""try:if location_type.lower() in self.locationTypeDict:# if self.locationTypeDict.has_key(locatorMethod.lower()):self.wait.until(EC.presence_of_element_located((self.locationTypeDict[location_type.lower()], locator_expression)))else:raise TypeError(u"未找到定位方式,请确认定位方法是否正确")except Exception as e:raise edef frame_to_be_available_and_switch_to_it(self, location_type, locator_expression, *args):"""判断该frame是否可以switch进去,如果可以的话,返回True并且switch进去,否则返回False:param location_type::param locator_expression::param args::return:"""try:self.wait.until(EC.frame_to_be_available_and_switch_to_it((self.locationTypeDict[location_type.lower()], locator_expression)))except Exception as e:# 抛出异常信息给上层调用者raise edef visibility_element_located(self, location_type, locator_expression, *args):"""判断某个元素是否被添加到了dom里并且可见,可见代表元素可显示且宽和高都大于0:param location_type::param locator_expression::param args::return:"""try:element = self.wait.until(EC.visibility_of_element_located((self.locationTypeDict[location_type.lower()], locator_expression)))return elementexcept Exception as e:raise edef wait_title_is(self, string, location_type, locator_expression, *args):"""当页面标题是string时对页面元素进行定位,并返回页面元素对象:param string::param location_type::param locator_expression::param args::return:"""try:if self.driver.title == string:element = self.wait.until(EC.visibility_of_element_located((self.locationTypeDict[location_type.lower()], locator_expression)))return elementexcept Exception as e:raise edef wait_title_contain(self, string, location_type, locator_expression, *args):"""当页面标题与string进行比较相似度大于0.8时对页面元素进行定位,并返回页面元素对象:param string::param location_type::param locator_expression::param args::return:"""page_tile = driver.titlesimilarity = difflib.SequenceMatcher(None, page_tile, string).quick_ratio()try:if similarity >= 0.8:element = self.wait.until(EC.visibility_of_element_located((self.locationTypeDict[location_type.lower()], locator_expression)))return elementexcept Exception as e:raise edef visibility_of_element(self, location_type, locator_expression, *args):"""判断元素是否可见,如果可见就返回这个元素:param location_type::param locator_expression::param args::return:"""try:element = self.wait.until(EC.visibility_of((self.locationTypeDict[location_type.lower()], locator_expression)))return elementexcept Exception as e:raise edef presence_of_all_elements_located(self, location_type, locator_expression, *args):"""判断是否至少有1个元素存在于dom树中,如果定位到就返回列表:param location_type::param locator_expression::param args::return:"""try:element_list = self.wait.until(EC.presence_of_all_elements_located((self.locationTypeDict[location_type.lower()], locator_expression)))return element_listexcept Exception as e:raise edef visibility_of_any_elements_located(self, location_type, locator_expression, *args):"""判断是否至少有一个元素在页面中可见,如果定位到就返回列表:param location_type::param locator_expression::param args::return:"""try:element_list = self.wait.until(EC.visibility_of_any_elements_located((self.locationTypeDict[location_type.lower()], locator_expression)))return element_listexcept Exception as e:raise edef text_to_be_present_in_element(self, string, location_type, locator_expression, *args):"""判断指定的元素中是否包含了预期的字符串,返回布尔值:param string::param location_type::param locator_expression::param args::return:"""try:self.wait.until(EC.text_to_be_present_in_element((self.locationTypeDict[location_type.lower()], locator_expression), string))except Exception as e:raise edef text_to_be_present_in_element_value(self, string, location_type, locator_expression, *args):"""判断指定元素的属性值中是否包含了预期的字符串,返回布尔值:param string::param location_type::param locator_expression::param args::return:"""try:self.wait.until(EC.text_to_be_present_in_element_value((self.locationTypeDict[location_type.lower()], locator_expression), string))except Exception as e:raise edef invisibility_of_element_located(self, location_type, locator_expression, *args):"""判断某个元素在是否存在于dom或不可见,如果可见返回False,不可见返回这个元素:param location_type::param locator_expression::param args::return:"""try:element = self.wait.until(EC.invisibility_of_element_located((self.locationTypeDict[location_type.lower()], locator_expression)))return elementexcept Exception as e:raise edef element_to_be_clickable(self, location_type, locator_expression, *args):"""判断某个元素中是否可见并且是enable的,代表可点击:param location_type::param locator_expression::param args::return:"""try:element = self.wait.until(EC.element_to_be_clickable((self.locationTypeDict[location_type.lower()], locator_expression)))return elementexcept Exception as e:raise edef element_to_be_selected(self, location_type, locator_expression, *args):"""判断某个元素是否被选中了,一般用在下拉列表:param location_type::param locator_expression::param args::return:"""try:element = self.wait.until(EC.element_to_be_selected((self.locationTypeDict[location_type.lower()], locator_expression)))return elementexcept Exception as e:raise edef element_selection_state_to_be(self, location_type, locator_expression, *args):"""判断某个元素的选中状态是否符合预期:param location_type::param locator_expression::param args::return:"""try:element = self.wait.until(EC.element_selection_state_to_be((self.locationTypeDict[location_type.lower()], locator_expression), True))return elementexcept Exception as e:raise edef element_located_selection_state_to_be(self, location_type, locator_expression, *args):"""判断某个元素的定位状态是否符合预期:param location_type::param locator_expression::param args::return:"""try:element = self.wait.until(EC.element_located_selection_state_to_be((self.locationTypeDict[location_type.lower()], locator_expression), True))return elementexcept Exception as e:raise edef alert_is_present(self):"""判断页面上是否存在alert,如果有就切换到alert并返回alert的内容"""try:alert_instance = self.wait.until(EC.alert_is_present())print(alert_instance.text)alert_instance.accept()except Exception as e:raise eif __name__ == "__main__":from selenium import webdriverdriver = webdriver.Chrome()driver.get("http://mail.126.com")# 实例化WaitUtil类waitUtil = Smart_Waiting(driver)# 判断如果id = x-URS-iframe的iframe存在则切换进去wf = waitUtil.frame_to_be_available_and_switch_to_it("id", "x-URS-iframe")# 等待页面元素xpath = //input[@name='email']的出现wv = waitUtil.visibility_element_located("xpath", "//input[@name='email']")# 显示等待页面元素出现在DOM中,但并不一定可见,存在则返回该页面元素对象wp = waitUtil.presence_of_element_located("xpath", "//input[@name='email']")driver.quit()

[Win32模拟键盘]

在实际的自动化代码调试过程中,往往Selenium提供的方法不能满足于自动化任务,例如定位某个按钮要完成点击操作,定位正确但就是无法完成点击,此时如果掌握模拟键盘的方法便可以为我们的自动化提供很大的帮助。

安装Pywin32

要模拟键盘操作,需要先为Python安装相关模块,启动命令行,然后在命令行执行命令“pip install –U pywin32”,执行结果如下

C:\Users\davieyang>pip install pywin32
Collecting pywin32Using cached https://files.pythonhosted.org/packages/a3/8a/eada1e7990202cd27e58eca2a278c344fef190759bbdc8f8f0eb6abeca9c/pywin32-224-cp37-cp37m-win_amd64.whl
Installing collected packages: pywin32
Successfully installed pywin32-224

看到Successfully installed pywin32-xxx则表示安装成功。

无法引入模块

如果使用pip安装后,出现无法将win32apiwin32con引入项目的情况,还可以直接下载安装文件,下载地址为
pywin32,必须指出的是在这个地址只能找到Build 222之前的版本,新的版本移步到了pywin32新,文件是.exe的,直接双击安装即可

方法封装

在PO项目中的Util路径下新建一个Python文件并命名为Keyboard_Simulation,然后在文件中写入如下代码。

# 用于实现模拟键盘单个或多个组合键操作
# encoding = utf-8
import win32api
import win32con
class Simulate_Keyboard:"""定义字典,字典内容为键盘上的按键与VkCode的键值对"""VK_CODE = {'backspace': 0x08,'tab': 0x09,'clear': 0x0C,'enter': 0x0D,'shift': 0x10,'ctrl': 0x11,'alt': 0x12,'pause': 0x13,'caps_lock': 0x14,'esc': 0x1B,'spacebar': 0x20,'page_up': 0x21,'page_down': 0x22,'end': 0x23,'home': 0x24,'left_arrow': 0x25,'up_arrow': 0x26,'right_arrow': 0x27,'down_arrow': 0x28,'select': 0x29,'print': 0x2A,'execute': 0x2B,'print_screen': 0x2C,'ins': 0x2D,'del': 0x2E,'help': 0x2F,'0': 0x30,'1': 0x31,'2': 0x32,'3': 0x33,'4': 0x34,'5': 0x35,'6': 0x36,'7': 0x37,'8': 0x38,'9': 0x39,'a': 0x41,'b': 0x42,'c': 0x43,'d': 0x44,'e': 0x45,'f': 0x46,'g': 0x47,'h': 0x48,'i': 0x49,'j': 0x4A,'k': 0x4B,'l': 0x4C,'m': 0x4D,'n': 0x4E,'o': 0x4F,'p': 0x50,'q': 0x51,'r': 0x52,'s': 0x53,'t': 0x54,'u': 0x55,'v': 0x56,'w': 0x57,'x': 0x58,'y': 0x59,'z': 0x5A,'numpad_0': 0x60,'numpad_1': 0x61,'numpad_2': 0x62,'numpad_3': 0x63,'numpad_4': 0x64,'numpad_5': 0x65,'numpad_6': 0x66,'numpad_7': 0x67,'numpad_8': 0x68,'numpad_9': 0x69,'multiply_key': 0x6A,'add_key': 0x6B,'separator_key': 0x6C,'subtract_key': 0x6D,'decimal_key': 0x6E,'divide_key': 0x6F,'F1': 0x70,'F2': 0x71,'F3': 0x72,'F4': 0x73,'F5': 0x74,'F6': 0x75,'F7': 0x76,'F8': 0x77,'F9': 0x78,'F10': 0x79,'F11': 0x7A,'F12': 0x7B,'num_lock': 0x90,'scroll_lock': 0x91,'left_shift': 0xA0,'right_shift ': 0xA1,'left_control': 0xA2,'right_control': 0xA3,'left_menu': 0xA4,'right_menu': 0xA5,'browser_back': 0xA6,'browser_forward': 0xA7,'browser_refresh': 0xA8,'browser_stop': 0xA9,'browser_search': 0xAA,'browser_favorites': 0xAB,'browser_start_and_home': 0xAC,'volume_mute': 0xAD,'volume_Down': 0xAE,'volume_up': 0xAF,'next_track': 0xB0,'previous_track': 0xB1,'stop_media': 0xB2,'play/pause_media': 0xB3,'start_mail': 0xB4,'select_media': 0xB5,'start_application_1': 0xB6,'start_application_2': 0xB7,'attn_key': 0xF6,'crsel_key': 0xF7,'exsel_key': 0xF8,'play_key': 0xFA,'zoom_key': 0xFB,'clear_key': 0xFE,'+': 0xBB,',': 0xBC,'-': 0xBD,'.': 0xBE,'/': 0xBF,'`': 0xC0,';': 0xBA,'[': 0xDB,'\\': 0xDC,']': 0xDD,"'": 0xDE,}@staticmethoddef press_key (keyName):# 按下按键win32api.keybd_event(Simulate_Keyboard.VK_CODE[keyName], 0, 0, 0)@staticmethoddef release_key (keyName):# 释放按键win32api.keybd_event(Simulate_Keyboard
.VK_CODE[keyName], 0, win32con.KEYEVENTF_KEYUP, 0)@staticmethoddef click_onekey(key):# 模拟单个按键Simulate_Keyboard.press_key(key)Simulate_Keyboard.release_key(key)@staticmethoddef click_twokey(first_key, second_key):# 模拟两个组合键Simulate_Keyboard.press_key(first_key)Simulate_Keyboard.press_key(second_key)Simulate_Keyboard.release_key(second_key)Simulate_Keyboard.release_key(first_key)

方法调用

当需要调用其中的方法的时候,将其引入到测试代码中,调用类中的方法时,将想要的按键传给相应方法即可,在test_advanced_application文件中新增如下测试方法,验证封装的方法是否可用,代码如下所示。

from Util.Keyboard_Simulation import Simulate_Keyboard
def test_simulate_keyboard(self):Simulate_Keyboard.oneKey('enter')Simulate_Keyboard.oneKey('ctrl', 'v')Simulate_Keyboard.oneKey('enter')

[PyUserInput模拟键盘]

PyUserInput安装

在Python3.7版本下安装PyUserInput需要先安装PyHook,用浏览器打开链接PyHook,这个页面里能找到很多Python的第三方扩展,读者朋友不妨保存起来。我们找到pyHook兼容Python3.7版本的链接,直接点击链接即可下载
然后启动命令行并将命令行引导到下的文件所在路径下,执行命令pip install pyHook-1.5.1-cp37-cp37m-win_amd64.whl,如下执行过程则表示安装成功。

C:\Users\Administrator\Downloads>pip install pyHook-1.5.1-cp37-cp37m-win_amd64.whl
Processing c:\users\administrator\downloads\pyhook-1.5.1-cp37-cp37m-win_amd64.whl
Installing collected packages: pyHook
Successfully installed pyHook-1.5.1

安装完pyHook后,便可以安装PyUserInput模块,继续在命令行执行pip install PyUserInput,如下所示则表示PyUserInput安装成功。

C:\Users\Administrator\Downloads>pip install PyUserInput
Collecting PyUserInput
Usingcachedhttps://files.pythonhosted.org/packages/d0/09/17fe0b16c7eeb52d6c14e904596ddde82503aeee268330120b595bf22d7b/PyUserInput-0.1.11.tar.gz
Requirement already satisfied: pyHook in c:\python37\lib\site-packages (from PyUserInput) (1.5.1)
Requirement already satisfied: pywin32 in c:\python37\lib\site-packages (from PyUserInput) (223)
Installing collected packages: PyUserInput
Running setup.py install for PyUserInput ... done
Successfully installed PyUserInput-0.1.11

模拟键盘

启动命令行工具,并进入到Python命令行,将pykeyboard类引入到环境中,然后调用PyKeyboard()函数,它返回键盘对象,将其赋值给pk

>>> import pykeyboard
>>> pk = pykeyboard.PyKeyboard()

当我们有了鼠标和键盘对象后,就可以模拟一些实际的鼠标键盘操作了,在键盘上敲字母”D”

pk.press_key('D')
pk.release_key('D')

我们看到敲击一个字母需要使用两个方法一个是press_key()另一个是release_key(),正如我们键盘上的操作按下按钮和松开按钮是一样,我们还可以使用一个方法tap_key()代替press_key()和release_key()

pk.tap_key('D')

同时tap_key()还支持在指定间隔时间情况的多次敲击,如下命令所示,其中10表示敲击键盘的次数,而1表示敲击间隔

pk.tap_key('D', 10, 1) 

并且还可以使用方法type_string()模拟敲击整个字符串

pk.type_string('__davieyang__')

接下来看一下如何完成组合键和功能键的模拟,如下命令行所示,敲击Ctrl+A。

pk.press_key(pk.control_key)
pk.tap_key(‘a’)
pk.release_key(pk.control_key)

敲击功能键F5

pk.tap_key(pk.function_keys[5])

敲击小键盘上的Home键

pk.tap_key(pk.numpad_keys['Home'])

敲击小键盘上的3,敲8次

pk.tap_key(pk.numpad_keys[3], n=8)

我们还可以使用press_keys()方法,然后传给他一个列表,完成组合键的敲击,如下代码所示,敲击键盘的Ctrl+A

pk.press_keys([pk.control_key,'a'])

[PyUserInput模拟鼠标]

PyUserInput安装

在Python3.7版本下安装PyUserInput需要先安装PyHook,用浏览器打开链接:PyHook,这个页面里能找到很多Python的第三方扩展,读者朋友不妨保存起来。我们找到pyHook兼容Python3.7版本的链接,直接点击链接即可下载
然后启动命令行并将命令行引导到下的文件所在路径下,执行命令pip install pyHook-1.5.1-cp37-cp37m-win_amd64.whl,如下执行过程则表示安装成功。

C:\Users\Administrator\Downloads>pip install pyHook-1.5.1-cp37-cp37m-win_amd64.whl
Processing c:\users\administrator\downloads\pyhook-1.5.1-cp37-cp37m-win_amd64.whl
Installing collected packages: pyHook
Successfully installed pyHook-1.5.1

安装完pyHook后,便可以安装PyUserInput模块,继续在命令行执行pip install PyUserInput,如下所示则表示PyUserInput安装成功。

C:\Users\Administrator\Downloads>pip install PyUserInput
Collecting PyUserInput
Usingcachedhttps://files.pythonhosted.org/packages/d0/09/17fe0b16c7eeb52d6c14e904596ddde82503aeee268330120b595bf22d7b/PyUserInput-0.1.11.tar.gz
Requirement already satisfied: pyHook in c:\python37\lib\site-packages (from PyUserInput) (1.5.1)
Requirement already satisfied: pywin32 in c:\python37\lib\site-packages (from PyUserInput) (223)
Installing collected packages: PyUserInput
Running setup.py install for PyUserInput ... done
Successfully installed PyUserInput-0.1.11

模拟鼠标

启动命令行工具,并进入到Python命令行,将pymouse类引入到环境中,然后调用PyMouse()函数,它返回鼠标对象,我们将其赋值给pm

>>> import pymouse
>>> pm = pymouse.PyMouse()

获取鼠标指针当前所在位置的坐标

>>> mouse_position = pm.position()
>>> print(mouse_position)
(849, 589)

获取了当前位置,模拟鼠标从当前所在位置按住鼠标左键滑动到坐标(300,400)

>>> pm.drag(300,400)

模拟鼠标移动到坐标(300,500)

>>> pm.move(300,500)

模拟鼠标在坐标(300,500)点住左键不放,其中1表示左键,2表示右键,3表中中间键

>>> pm.press(300,500,1)

模拟了按住不放,就要有释放按键的方法

>>> pm.release(300,500,1)

模拟鼠标滚轮滚动,如下命令行所示,其中参数vertical为负数表示向下滚动反之正数表示向上滚动,而horizontal为负数表示向左滚动反之为向右。

pm.scroll(vertical = -30, horizontal = -40)

模拟鼠标在坐标为(300,500)点击鼠标右键5次,如下命令行所示,2表示鼠标右键,左键用1表示中间键用3表示,命令行中的5表示点击次数,默认为1。

>>> pm.click(300,500,2,5)

获取屏幕尺寸

>>> screen_x, screen_y = pm.screen_size()
>>> print(screen_x, screen_y)
3360 1080

模拟鼠标在坐标(300,500)点击左右键或者点击滚轮,如下两条命令等效

>>> pm.click(300,500, 1|2)
>>> pm.click(300,500, 3)

[ActionChains模拟鼠标]

在实际的测试中鼠标的操作也是频繁发生的,与封装控制浏览器相关方法是相同的思想,本节笔者将详细介绍如何封装模拟鼠标操作的方法以及如何调用我们封装好的方法。

方法封装

在实际的自动化测试中往往需要模拟一些鼠标的操作来辅助我们来完成页面上一些特殊的操作,例如有些需要鼠标拖拽页面元素,挪动页面元素,鼠标悬停在页面元素上等等, 因此我们封装一些工具类以便于我们在写测试代码中直接调用。

# encoding = utf-8
from selenium.webdriver.common.action_chains import ActionChains
class Simulate_Mouse:def __init__(self, driver):self.driver = driverself.actions = ActionChains(self.driver)# 单击鼠标左键def left_click(self, element):self.actions.click(element).perform()# 双击鼠标左键def double_left_click(self, element):self.actions.double_click(element).perform()#  单击鼠标右键def right_click(self, element):self.actions.context_click(element).perform()#  移动鼠标到elementdef move_mouse(self, element):self.actions.move_to_element(element).perform()#  从source移动鼠标到targetdef move_mouse_source_target(self, source, target):self.actions.drag_and_drop(source, target).perform()#  从source移动鼠标到targetdef move_source_target(self, source, target):self.actions.click_and_hold(source).release(target).perform()#  拖拽元素到坐标xydef drag_element(self, element, x, y):self.actions.click_and_hold(element).move_by_offset(x, y).release().perform()#  点击并且不释放def click_hold(self, element):self.actions.click_and_hold(element)

方法调用

当需要调用其中的方法的时候,将其引入到测试代码中,调用类中的方法时,根据方法所需参数传参即可。
在test_advanced_application文件中新增如下测试方法,验证封装的方法是否可用,代码如下所示。

from Util.Mouse_Simulation import Simulate_Mouse  # 引入我们封装好的工具类
import time # 引入time模块用于等待
def test_simulate_mouse(self):  # 定义测试方法chr_driver = webdriver.Chrome()  # 启动谷歌浏览器 chr_driver.get(self.url)  # 打开urlelement = chr_driver.find_element_by_link_text("设置") # 获取页面元素Simulate_Mouse(chr_driver).move_mouse(element)  # 鼠标悬停动作time.sleep(5)  # 强制等待5秒

[兼容性测试]

在实际的自动化测试过程中,有些产品必须进行兼容性测试,那就意味着在不同的环境中执行相同的测试用例,而这应该是发挥自动化测试优势的非常重要的战地。
自动化在编写兼容性测试用例的时候,稍微有所不同,需要我们定义好一个测试方法,然后执行不同环境时调用该方法,从而实现在不同的环境中执行相同的测试,如下代码所示。

# -*- coding: utf-8 -*-
from selenium import webdriver
from time import sleep
import unittest
class Compatibility_Test(unittest.TestCase):def setUp(self):self.base_url = "https://admin.leadscloud.com/Front-breeze/#/home"def login_leadscloud(self, driver):'''定义测试方法:param driver::return:'''driver.get(self.base_url)sleep(5)driver.find_element_by_xpath("//*[@id='main']/div/div[1]/div/div[2]/form/div[1]/div/div/input").send_keys('xxxxxx')driver.find_element_by_xpath("//*[@id='main']/div/div[1]/div/div[2]/form/div[2]/div/div/input").send_keys('xxxxxx')driver.find_element_by_xpath("//*[@id='main']/div/div[1]/div/div[2]/form/div[3]/div/button").click()driver.quit()def test_chrome(self):'''启动chrome浏览器执行测试用例:return:'''chrome_driver = webdriver.Chrome()self.login_leadscloud(chrome_driver)def test_firefox(self):'''启动firefox执行测试用例:return:'''firefox_driver = webdriver.Firefox()self.login_leadscloud(firefox_driver)def test_ie(self):'''启动IE执行测试用例:return:'''ie_driver = webdriver.Ie()self.login_leadscloud(ie_driver)
if __name__ == '__main__':unittest.main(verbosity=2)

[杀浏览器进程]

Webdriver虽然有quit()方法和close()可以关闭浏览器,但有些时候浏览器进程并不能彻底关闭,我们需要掌握杀进程的方法,代码实例如下:

# encoding = utf-8
from selenium import webdriver
import unittest
import os
from time import sleep
class Test_Kill_Browser(unittest.TestCase):def test_kill_browser_process(self):# 启动浏览器chrome_driver = webdriver.Chrome()sleep(5)firefox_driver = webdriver.Firefox()sleep(5)ie_driver = webdriver.Ie()sleep(5)# 杀chrome浏览器进程code = os.system("taskkill /F /iM chrome.exe")if code ==0:print(u"Kill Firefox Successfully")else:print(u"Kill Firefox Failed")# 杀firefox浏览器进程code = os.system("taskkill /F /iM firefox.exe")if code ==0:print(u"Kill Firefox Successfully")else:print(u"Kill Firefox Failed")# 杀ie浏览器进程code = os.system("taskkill /F /iM ie.exe")if code ==0:print(u"Kill Firefox Successfully")else:print(u"Kill Firefox Failed")if __name__ == '__main__':unittest.main(verbosity=2)

执行结果

在这里插入图片描述
在执行结果中出现乱码,是因为我们的代码文件的File Encodings设置是默认UTF-8的,想解决这乱码问题,我们可以修改Pycharm的设置,找到Settings,或者直接使用快捷键Ctrl+Alt+S打开Settings
在这里插入图片描述
然后在检索栏中检索encoding,便可以找到File Encodings的选项
在这里插入图片描述
将Global Encoding修改为GBK后,在此执行代码,执行结果如图
在这里插入图片描述

[浏览器静默模式启动]

在实际的自动化测试中,为了不让浏览器频繁起动关闭,可以采用静默模式执行,代码示例如下。

# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time
# 创建chrome的Option对象
chrome_options = Options()
# 添加静默参数
chrome_options.add_argument('--headless')
for i in range(100000):# 静默模式启动浏览器chrome_driver = webdriver.Chrome(options=chrome_options)# 打开页面 chrome_driver.get("http://www.yialife.co.za/contact.html")chrome_driver.maximize_window()chrome_driver.find_element_by_class_name("xhl-button-text").click()# 获取当前时间current_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))chrome_driver.find_element_by_id("messageText").send_keys("audio notification testing at " + current_time)time.sleep(1)chrome_driver.find_element_by_id("sendBtn").click()print(current_time)time.sleep(5)# 删掉所有cookiechrome_driver.delete_all_cookies()
chrome_driver.quit()

[处理Cookies]

在某些场景下是需要处理浏览器cookie的,比如经常能看到有些网站都提供了页面内部的咨询窗口,点开窗口可以和客服对话,然而当第一次跟客服对话的时候,客服方显示对话名称假设为访客A,当半小时后再次打开该网站继续聊天的话客服方依然还会显示是访客A,但如果清理了cookie,在打开网站去和客服对话,客服方显示我们可能就是一个新的访客。
笔者只是举一个互联网产品系统常见的一个场景,而这种场景如果需要自动化测试的话,无疑我们要掌握处理cookie的方法,本节笔者将介绍封装操作cookie的方法,以及如何使用封装好的方法来处理cookie。

方法封装

def delete_current_cookie(self):  # 封装删除当前所有cookie的方法"""删除所有cookie:return:"""self.driver.delete_all_cookies()
def get_current_cookies(self):  # 封装获取当前cookies的方法"""获取当前cookies:return:"""current_cookie = self.driver.get_cookies()return current_cookie
def get_current_cookie_value(self, key):  # 获取当前name为key的cookie信息"""获取key为key的cookie信息:param key::return:"""key_cookie = self.driver.get_cookie(key)return key_cookie
def add_key_value_to_cookie(self, cookie_dict):  # 添加cookie,参数为字典"""添加cookie:return:"""self.driver.add_cookie(cookie_dict)

方法调用

然后看如何调用它,在test_advanced_application文件中新增如下测试方法,验证封装的方法是否可用,代码如下所示。

def test_cookies(self):  # 定义新的测试方法cookie_dict = {'name': 'name_yang', 'value': 'cookie_yang'}  # 定义字典 chrome_driver = webdriver.Chrome()  # 启动浏览器chrome_driver.get("https://www.baidu.com")time.sleep(10)# 获取当前所有cookiecurrent_cookie = Browser_Controller(chrome_driver).get_current_cookies()# 打印当前cookieprint(current_cookie)# 将之前定义的字典添加到cookie中去Browser_Controller(chrome_driver).add_key_value_to_cookie(cookie_dict)# 获取name为name_yang的cookie信息key_cookie = Browser_Controller(chrome_driver).get_current_cookie_value('name_yang')# 打印cookie信息print(key_cookie)# 删除当前cookieBrowser_Controller(chrome_driver).delete_current_cookie()# 删除后再次获取cookiecurrent_cookie_2 = Browser_Controller(chrome_driver).get_current_cookies()# 将当前cookie转换成字符串打印到控制台print(str(current_cookie_2) + "只有这几个字没有cookie了")

[处理iframe]

如果页面存在iframe,那么我们是不能直接定位到iframe节点下的页面元素的,需要先切换到iframe里边去,然后再对iframe中的页面元素进行定位,而如果切换进iframe中后也是定位不到iframe外的元素的,还需要切换出去才能进行iframe外的元素的定位。
在经历过上前边多种操作的封装后,iframe的封装就简单了很多,接下来笔者将介绍封装后的方法以及如何调用。

方法封装

def switch_to_iframe(self, frame):"""用于切换进页面的iframe控件:param iframe::return:"""self.driver.switch_to.frame(frame)
def switch_to_default_content(self):"""从iframe中切换回主页页面:return:"""self.driver.switch_to.default_content()

方法调用

def test_switch_iframe(self):  # 定义测试方法chrome_driver = webdriver.Chrome()chrome_driver.get("https://mail.163.com")time.sleep(10)frame = chrome_driver.find_element_by_xpath("//*[@id='loginDiv']/iframe")# 调用封装好的方法切换进iframe控件Browser_Controller(chrome_driver).switch_to_iframe(frame)  time.sleep(5)chrome_driver.find_element_by_name("email").send_keys("邮箱账号")chrome_driver.find_element_by_name("password").send_keys("邮箱密码")chrome_driver.find_element_by_id("dologin").click()

[处理弹窗]

我们常见的弹窗一般分为3个样式,分别成为alert/prompt/confirm,同样的要定位弹窗控件中的元素或者操作控件都必须先切换进控件内

被测页面

<html>  <head>  <title>For Test Alert</title>  </head>  <body>  <input id = "alert" value = "alert" type = "button" onclick = "alert('您点击了alert按钮');"/>  <input id = "confirm" value = "confirm" type = "button" onclick = "confirm('您点击了confirm按钮');"/>  <input id = "prompt" value = "prompt" type = "button" onclick = "var name = prompt('您点击了prompt按钮:','Prompt'); document.write(name) "/>    </body>   
</html>  

方法封装

def switch_to_alert(self):"""切换进alert控件:return:"""pop_dailog = self.driver.switch_to.alertreturn pop_dailog

方法调用

def test_switch_to_alert(self):chrome_driver = webdriver.Chrome()# 浏览器打开我们刚才新建的html文件chrome_driver.get("file:///C:/Users/davieyang/Desktop/test_alert.html")time.sleep(3)#  点击alert按钮chrome_driver.find_element_by_id("alert").click()time.sleep(3)#  调用我们封装好的方法al = Browser_Controller(chrome_driver).switch_to_alert()print(al.text)  #  打印弹窗中的文本# 相当于点击弹窗中的确定按钮,但实际并不是点击只是弹窗对象提供的方法,效果一样al.accept()
def test_switch_to_confirm(self):chrome_driver = webdriver.Chrome()# 浏览器打开我们刚才新建的html文件chrome_driver.get("file:///C:/Users/davieyang/Desktop/test_alert.html")time.sleep(3)#  点击alert按钮chrome_driver.find_element_by_id("confirm").click()time.sleep(3)#  调用我们封装好的方法al = Browser_Controller(chrome_driver).switch_to_alert()print(al.text)  #  打印弹窗中的文本# 相当于点击弹窗中的取消按钮,但实际并不是点击只是弹窗对象提供的方法,效果一样al.dismiss()
def test_switch_to_prompt(self):chrome_driver = webdriver.Chrome()# 浏览器打开我们刚才新建的html文件chrome_driver.get("file:///C:/Users/davieyang/Desktop/test_alert.html")time.sleep(3)#  点击alert按钮chrome_driver.find_element_by_id("prompt").click()time.sleep(3)#  调用我们封装好的方法al = Browser_Controller(chrome_driver).switch_to_alert()print(al.text)  #  打印弹窗中的文本# 相当于点击弹窗中的确定按钮,但实际并不是点击只是弹窗对象提供的方法,效果一样al.accept()

[处理下拉菜单]

Selenium为选择下拉菜单中的选项提供了3中方法,接下来分别将这三种方法进行封装然后调用。

方法封装

from selenium.webdriver.support.select import Select
def select_by_index(self, element, index):"""通过下拉菜单的索引,完成对选项的选择:param element::param value::return:"""Select(element).select_by_index(index)
def select_by_value(self, element, value):"""通过选项值,完成对选项的选择:param element::param value::return:"""Select(element).select_by_value(value)
def select_by_text(self, element, text):"""通过选项的文本,完成对选项的选择:param element::param text::return:"""Select(element).select_by_visible_text(text)

方法调用

def test_select(self):chrome_driver = webdriver.Chrome()chrome_driver.get("http://www.baidu.com")chrome_driver.implicitly_wait(30)mouse = chrome_driver.find_element_by_link_text("设置")ActionChains(chrome_driver).move_to_element(mouse).perform()chrome_driver.find_element_by_link_text("搜索设置").click()time.sleep(5)chrome_driver.find_element_by_name("NR").click()time.sleep(5)select = chrome_driver.find_element_by_name("NR")Browser_Controller(chrome_driver).select_by_value(select, "20")time.sleep(5)Browser_Controller(chrome_driver).select_by_index(select, 1)time.sleep(5)Browser_Controller(chrome_driver).select_by_text(select, "每页显示50条")time.sleep(5)

方法扩展

实际上Selenium提供的处理下拉菜单选项的不止我们封装的三种方法,还有如下所示取消选项选择的方法

deselect_by_index(index)  # 根据索引取消选择
deselect_by_value(value)  # 根据value取消选择 
deselect_by_visible_text(text)  # 根据文本取消选择 
deselect_all()  # 取消所有选择

[上传文件]

上传附件是我们在测试BS系统的时候经常遇到的功能,然而在处理上传附件的自动化代码并不总是有效的,因此需要掌握多种上传附件的手段,本节笔者将介绍几种上传附件的方法,应该可以满足绝大多数的情况。

被测页面

<html><head><meta http-equiv="content-type" content="text/html;charset=utf-8" /><title>上传文件</title></head><body><div class="row-fluid"><div class="span6 well"><h3>选择文件</h3><input type="file" name="fileupload" /></div></div></body>
</html>

测试代码

def test_upload_by_sendkeys(self):chrome_driver = webdriver.Chrome()chrome_driver.get("file:///C:/Users/Administrator/Desktop/fileupload.html")chrome_driver.find_element_by_name("fileupload").send_keys("E:\\test_upload_file.txt")time.sleep(10)chrome_driver.quit()

借助AutoIt实现上传

如果页面标签非input类型,可以通过第三方工具来完成上传操作。

  • 首先第一步下载AutoIt工具,浏览器访问https://www.autoitscript.com/files/autoit3/autoit-v3-setup.exe,即可直接下载,下载完成后双击autoit-v3-setup.exe文件,默认选项安装即可,安装完成后,在操作系统的开始菜单中能看到相关菜单项
    在这里插入图片描述
  • 用浏览器打开上一小节创建的fileupload.html文件,然后在打开的页面中点击“选择文件”此时选择文件的窗口将弹出
    在这里插入图片描述
  • 在开始菜单中点击AutoIt Window Info,改程序存在两个版本(x86)表示32位版本,(x64)表示64位版本,读者朋友根据自己的操作系统版本启动相应的AutoIt版本即可,启动成功后
    在这里插入图片描述
  • 在AutoIt Window Info窗口中间部分有几个标签,然后拖拽Finder Tool到“打开”按钮上,便可获取该控件的窗口信息
    在这里插入图片描述
  • 启动SciTE Script Editor,在开始菜单AutoIt v3路径里的可以找到他
    在这里插入图片描述
  • 编写脚本,在SciTE Script Editor中写入如下内容,然后在选择文件的窗口打开的情况下,在SciTE Script Editor窗口按键盘上F5键,执行脚本,脚本运行正常,即可保存到我们PO项目下的Util路径中,命名为upload_file,保存成功后,会生成一个upload_file.au3的文件
; ControlFocus("title", "text", "ClassnameNN") ControlFocus函数的用法
ControlFocus("打开", "", "Edit1")
; 等待10秒WinWait("[CLASS:#32770]", "", 10)
; 在文件名控件里设置要上传的文件全路径ControlSetText("打开", "", "Edit1", "E:\test_upload_file.txt")Sleep(2000)
; 点击打开按钮ControlClick("打开", "", "Button1")
  • 然而这个upload_file.au3文件并不能被Python执行,需要将其编译成.exe文件以供Python调用,启动Compile Script to .exe,在开始菜单Auto v3路径里可以找到它,启动(x86)或者(x64)根据自己的操作系统版本对应选择即可
    在这里插入图片描述
  • 选择之前保存的au3文件,点击Convert按钮,将其转换为.exe文件
    在这里插入图片描述
  • Python脚本调用该.exe完成文件的上传
import os  # 引入os模块用于调用.exe文件执行
def test_upload_by_autoit(self):  # 定义测试方法chrome_driver = webdriver.Chrome()  # 启动浏览器#打开我们的html文件chrome_driver.get("file:///C:/Users/Administrator/Desktop/fileupload.html")chrome_driver.find_element_by_name("fileupload").click()os.system("E:\\PO\\Util\\upload_file.exe")  # 调用我们编译好的.exe文件time.sleep(10)  # 强制等待10秒  chrome_driver.quit()

模拟键盘实现上传

封装操作剪切板方法
# encoding = utf-8
import win32clipboard as wc
import win32con
class Simulate_Clipboard:# 读取剪切板@staticmethoddef get_clipboard():# 打开剪切板wc.OpenClipboard()# 获取剪切板中的数据data = wc.GetClipboardData(win32con.CF_TEXT)# 关闭剪切板wc.CloseClipboard()# 返回剪切板数据给调用者return data# 设置剪切板内容@staticmethoddef set_clipboard(content):# 打开剪切板wc.OpenClipboard()# 清空剪切板wc.EmptyClipboard()# 将数据astring写入剪切板wc.SetClipboardData(win32con.CF_UNICODETEXT, content)# 关闭剪切板wc.CloseClipboard()
方法调用
# 将模拟剪切板的类引入到测试代码文件中
from Util.Clipboard_Simulation import Simulate_Clipboard  
def test_simulate_clipboard(self):  # 定义测试方法Simulate_Clipboard.set_clipboard("set clipboard")  # 设置剪切板内容str = Simulate_Clipboard.get_clipboard()  # 获取剪切板内容并赋给strprint(str)  # 将剪切板内容打印到控制台
截切板配合键盘实现上传
def test_upload_by_simulation(self):  # 定义测试方法 # 设置剪切板内容,将文件全路径放到剪切板中Simulate_Clipboard.set_clipboard("E:\\test_upload_file.txt")      chrome_driver = webdriver.Chrome()  # 启动浏览器# 打开我们的html文件chrome_driver.get("file:///C:/Users/Administrator/Desktop/fileupload.html")chrome_driver.find_element_by_name("fileupload").click()time.sleep(5)Simulate_Keyboard.click_twokey('ctrl', 'v')  # 模拟键盘Ctrl+V组合键,黏贴剪切板内容time.sleep(5)Simulate_Keyboard.click_onekey('enter')  # 模拟键盘回车键time.sleep(20)

[JS完成任务]

有些时候Selenium并不能帮我们完成页面上的所有操作,例如滚动条的控制就比较难处理,而且有些时候click()方法也会失灵即便我们定位按钮没问题也有点击不了的情况,这些情况下我们就可以借助Python可以执行JS的机制,借助JS来辅助我们完成一些任务。

方法封装

class JS_Assistance:  # 定义类
def __init__(self, driver):self.driver = driver
def single_click(self, element):try:#  判断页面元素状态if element.is_enabled() and element.is_displayed():#  调用js单击元素self.driver.execute_script("arguments[0].click();", element)else:print("该元素不可点击")except Exception as e:raise edef scroll_to_bottom(self):"""滚动条滚动到页面底部:return:"""self.driver.execute_script("document.documentElement.scrollTop=10000")def scroll_to_top(self):"""滚动条滚动到页面顶部:return:"""self.driver.execute_script("document.documentElement.scrollTop=0")def scrolltobottom(self):"""滚动条滚动到页面底部:return:"""self.driver.execute_script("window.scrollTo(0,100000)")
def scrolltotop(self):"""滚动条滚动到页面顶部:return:"""self.driver.execute_script("window.scrollTo(0,1)")
def vertical_to_middle(self):"""纵向滚动条滚动到页面中部:return:"""self.driver.execute_script("window.scrollBy(0, 0-document.body.scrollHeight *1/2)")
def horizontal_to_middle(self):"""滚动水平滚动条到页面中部:return:"""self.driver.execute_script("window.scrollBy(0, 0-document.body.scrollWidht *1/2)")def scroll_to_element(self, element):"""滚动到具体页面元素可见位置:param element::return:"""self.driver.execute_script("arguments[0].scrollIntoView(true);", element)def scroll_to_bottom_page(self):"""滚动条滚动到页面底部:return:"""self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")

方法调用

def test_js(self):  # 定义测试方法
chrome_driver = webdriver.Chrome()
chrome_driver.get("http://www.baidu.com")
chrome_driver.find_element_by_id("kw").send_keys("davieyang")
chrome_driver.find_element_by_id("su").click()JS_Assistance(chrome_driver).scroll_to_bottom()  #滚动页面到底部 time.sleep(3)JS_Assistance(chrome_driver).scroll_to_top() #滚动页面到顶部time.sleep(3)JS_Assistance(chrome_driver).scroll_to_bottom_page()  #滚动页面到底部time.sleep(3)JS_Assistance(chrome_driver).scrolltotop()  #滚动页面到顶部time.sleep(3)JS_Assistance(chrome_driver).scrolltobottom()  #滚动页面到底部time.sleep(3)element = chrome_driver.find_element_by_xpath("//*[@id='help']/a[3]")JS_Assistance(chrome_driver).single_click(element)  # 单击该页面元素time.sleep(3)

[句柄]

在实际的自动化测试过程中往往会遇到我们的产品点击页面中的元素后,会启动浏览器新的页签,注意此处说的浏览器页签并不是我们系统内的标签,而启动了浏览器第二个页签后,就意味着我们的自动化程序要在两个页签内切换完成一些交互,因此切换页签便成了一个课题。

def test_switch_window_handle(self):  # 定义测试方法chrome_driver = webdriver.Chrome()  #启动浏览器chrome_driver.get("http://www.baidu.com")  #打开百度首页baidu_main_handle = chrome_driver.current_window_handle  # 获取当前浏览器句柄 print(baidu_main_handle)  # 为方便调试,将句柄打印到控制台 time.sleep(5) # 等待5秒chrome_driver.find_element_by_link_text("登录").click()  # 点击登录按钮time.sleep(5)  # 等待5秒chrome_driver.find_element_by_link_text("立即注册").click()  # 在弹出窗口中点击立即注册all_handles = chrome_driver.window_handles  # 获取所有句柄print(all_handles)  # 打印所有句柄到控制台for handle in all_handles: # 在所有句柄中进行循环try:if handle != baidu_main_handle:  # 判断是否句柄不等于百度首页的句柄,如不等于chrome_driver.switch_to.window(handle)  # 则切换句柄 print("进入新窗口....")chrome_driver.switch_to.window(baidu_main_handle)  #再切换回百度首页句柄chrome_driver.refresh() # 刷新页面# 输入检索内容到输入框chrome_driver.find_element_by_id("kw").send_keys("__davieyang__") time.sleep(5)# 点击百度一下按钮chrome_driver.find_element_by_id("su").click()time.sleep(5)except Exception as e:raise echrome_driver.quit()  # 关闭浏览器

[日志]

在实际的自动化测试代码调试过程中往往我们需要记录一些日志,一方面是打印到控制台便于我们调试代码,如果是持续集成的环境无人值守的话也是对测试执行过程的一个记录过程。

方法封装

新建一个Python文件, 并命名为ConstantConfig,然后在该文件中写入如下代码。

# 用于定义整个框架中所需要的全局常量值
# encoding = utf-8
import os
# 获取当前文件所在目录的父目录的绝对路径
parent_directory_path = os.path.abspath('..')
print(parent_directory_path)
# encoding = utf-8
import time
import logging
from Configuration.ConstantConfig import parent_directory_path
class Logger(object):def __init__(self, logger):"""指定保存日志的文件路径,日志级别,以及调用文件将日志存入到指定的文件中:param logger:"""# 创建一个loggerself.logger = logging.getLogger(logger)self.logger.setLevel(logging.DEBUG)# 创建一个handler,用于写入日志文件rq = time.strftime('%Y-%m-%d-%H-%M-%S', time.localtime(time.time()))log_path = parent_directory_path + '/TestResult/TestLog/'log_name = log_path + rq + '.log'filehandler = logging.FileHandler(log_name)filehandler.setLevel(logging.INFO)# 再创建一个handler,用于输出到控制台consolehandler = logging.StreamHandler()consolehandler.setLevel(logging.INFO)# 定义handler的输出格式formatter = logging.Formatter('%(asctime)s-%(name)s-%(levelname)s-%(message)s')filehandler.setFormatter(formatter)consolehandler.setFormatter(formatter)# 给logger添加handlerself.logger.addHandler(filehandler)self.logger.addHandler(consolehandler)def getlog(self):return self.logger

新建一个名为TestLog的文件夹,用于存储生成的log文件
在这里插入图片描述

方法调用

testlogger = GetLog.Logger('Test_Advanced_Application').getlog()
class Test_Advanced_Application(unittest.TestCase): def test_get_log(self):testlogger.info("打开浏览器")driver = webdriver.Chrome()driver.maximize_window()testlogger.info("最大化浏览器窗口。")driver.implicitly_wait(10)testlogger.info("打开百度首页。")driver.get("https://www.baidu.com")testlogger.info("暂停3秒。")time.sleep(3)testlogger.info("关闭并退出浏览器")driver.quit()with self.assertLogs(testlogger, level=20) as log:testlogger.error("打开浏览器")testlogger.info('关闭并退出浏览器')self.assertEqual(log.output,['ERROR:Test_Advanced_Application:打开浏览器','INFO:Test_Advanced_Application:关闭并退出浏览器'])

执行结果

在这里插入图片描述
能看到执行过程中将我们的日志逐一打印到控制台的过程
在这里插入图片描述
然后再到我们创建的TestLog文件夹下查看日志文件,如果遇到乱码,如图11.13所示,不必慌张,是因为编码不匹配导致的,点击Reload in ‘GBK’,将编码格式转换成GBK即可正常显示
在这里插入图片描述
在这里插入图片描述
产生乱码的原因是因为项目设置中的File Encodings里,Global Encoding是UTF-8
在这里插入图片描述

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

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

相关文章

代码签名证书是什么?

代码签名证书就像是软件世界的身份证&#xff0c;它为开发者提供了一种在分发软件时验证软件真实性和完整性的方式。通过使用数字签名技术&#xff0c;开发者可以将自己的“签名”嵌入软件中&#xff0c;确保在软件传播的过程中不被篡改。 代码签名证书的作用 确保软件完整性&…

异步Buck升级为同步Buck注意点总结(摘自芯洲)

1 介绍图 2所示&#xff0c;异步Buck变换器采用肖特基二极管作为续流管&#xff0c;而同步Buck变换器用MOSFET替代肖特基二极管进行续流&#xff0c;由于MOSFET的导通电阻很低&#xff0c;所以导通损耗较低&#xff0c;而肖特基二极管的损耗为其正向导通压降乘以电流&#xff0…

kali-钓鱼网站远程代码漏洞分析

文章目录 一、靶场搭建二、开始信息收集&#xff0c;寻找漏洞三、使用蚁剑连接后门程序四、使用webshell查看数据库信息五、进入网站后台 实验环境 Kali CentOs 一、靶场搭建 CentOsIP地址192.168.64.159 #关闭centos防火墙 [rootlocalhost ~]# systemctl disable --now fi…

抖音网红罗盘时钟改良版

文章目录 💕效果展示💕代码展示HTML💕效果展示 💕代码展示 HTML <!DOCTYPE html> <html lang=

pr插件|特殊编码.mkv/mov/flv/webm/avi/wmv/vob等多种格式视频素材直接导入pr的插件 Influx v1.2.5

适用于Adobe的一体式原生导入器插件&#xff08;Premiere Pro、After Effects和Media Encoder&#xff09;。支持多种格式和编解码器。 主要特点 直接在Adobe CC Video中进行本机导入 不再需要通过外部转码软件&#xff01;节省时间、磁盘空间和麻烦 在Premiere Pro中导入和编辑…

开源元数据治理平台Datahub部署指南(小白版)

1.引言 datahub是做什么的&#xff0c;这里就不展开描述了&#xff0c; 如果想了解更多请自行阅读DataHub官网文档&#xff0c; 这里主要教大家如何一步一步安装然后100%部署完成。一般开源产品的文档都是被大家吐槽的最多的&#xff0c;部署步骤写的非常简单&#xff0c;重要…

每日一题——链表的回文结构

链表的回文结构 1. 题目描述 对于一个链表&#xff0c;请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法&#xff0c;判断其是否为回文结构。 给定一个链表的头指针A&#xff0c;请返回一个bool值&#xff0c;代表其是否为回文结构。保证链表长度小于等于900。 测试…

mysql空间查询之搜索在不规则图形范围内点的数据

前言 这次的需求是在地图上画一个不规则图形,查询这个范围内的数据,类似下图: 前端会把每个折现点的经纬度传过来,比如:0 0, 0 10, 10 10, 10 0, 0 0,注意要首尾相连,这样才是一个完整的图形。 数据准备:有一个包含点的数据表&#xff0c;并且该表具有一个名为point的列来存…

【游戏篇】Scratch之安全上升的气球

【作品展示】安全上升的气球 操作&#xff1a;点击小绿旗&#xff0c;按下键盘方向键控制气球躲避障碍物同时还要拿到金币。

MySQL增删改查(增加)

White graces&#xff1a;个人主页 &#x1f649;专栏推荐:Java入门知识&#x1f649; &#x1f649; 内容推荐:MySQL数据库和表操作&#x1f649; &#x1f439;今日诗词:父兵诛卓起长沙,直取江东作帝家&#x1f439; ⛳️点赞 ☀️收藏⭐️关注&#x1f4ac;卑微小博主&…

VM Group

在复杂方案中模块过多可能造成查看或修改方案时存在视觉混乱&#xff0c;不够直观。此时可利用Group模块进行模块整合&#xff0c;同时Group模式也兼容循环的功能&#xff0c;如下图所示。 双击Group模块可进入Group内部&#xff0c;如下图所示。 在Group模块单击 可设置输入、…

ai学习笔记-入门

目录 一、人工智能是什么&#xff1f;可以做什么&#xff1f; 人工智能(Artificial Intelligence): 人工智能的技术发展路线&#xff1a; 产业发展驱动因素&#xff1a;数据、算力、算法 二、人工智能这个工具的使用原理入门 神经网络⭕数学基础 1.神经网络的生物表示 …