自动化框架
Selenium
环境准备 1.包 # 下载包 pip install -i https://pypi.douban.com/simple selenium
2.驱动 Chrome for Testing availability
Chrome测试版(防自动更新)
ChormeDriver
元素定位
find_element_by_id
<input id ="toolbar-search-input" autocomplete="off" type ="text" value="" placeholder="C++难在哪里?" > driver.find_element_by_id("toolbar-search-input" )
find_element_by_name
<meta name="keywords" content="论坛" > driver.find_element_by_name("keywords" )
find_element_by_class_name
<div class ="toolbar-search-container" > driver.find_element_by_class_name("toolbar-search-container" )
xpath 定位 xpath 是一种在 XML 文档中定位元素的语言,拥有多种定位方式
<head > ...<head /> <body > <div id ="csdn-toolbar" > <div class ="toolbar-inside" > <div class ="toolbar-container" > <div class ="toolbar-container-left" > ...</div > <div class ="toolbar-container-middle" > <div class ="toolbar-search onlySearch" > <div class ="toolbar-search-container" > <input id ="toolbar-search-input" autocomplete ="off" type ="text" value ="" placeholder ="C++难在哪里?" >
driver.find_element_by_xpath( "/html/body/div/div/div/div[2]/div/div/input[1]" ) driver.find_element_by_xpath( "//*[@id='toolbar-search-input']" )) driver.find_element_by_xpath( "//div[@id='csdn-toolbar']/div/div/div[2]/div/div/input[1]" ) driver.find_element_by_xpath( "//*[@id='toolbar-search-input' and @autocomplete='off']" )
CSS
使用选择器来为页面元素绑定属性,它可以较为灵活的选择控件的任意属性,一般定位速度比 xpath
要快
方法
例子
描述
.class
.toolbar-search-container
选择 class = 'toolbar-search-container'
的所有元素
#id
#toolbar-search-input
选择 id = 'toolbar-search-input'
的元素
*****
*
选择所有元素
element
input
选择所有 <input\>
元素
element>element
div>input
选择父元素为 <div\>
的所有 <input\>
元素
element+element
div+input
选择同一级中在 <div\>
之后的所有 <input\>
元素
[attribute=value]
type='text'
选择 type = 'text'
的所有元素
driver.find_element_by_css_selector('#toolbar-search-input' ) driver.find_element_by_css_selector('html>body>div>div>div>div>div>div>input' )
简单使用 1.启动调试浏览器
推荐使用bat脚本实现
创建文件夹 【selenium\AutomationProfile】
自定义调试端口 9527
@echo off start "" " chrome.exe位置 " --remote-debugging-port=9527 --user-data-dir ="D:\selenium\AutomationProfile" --new-window " 接手的网站 "
from selenium import webdriverfrom selenium.webdriver.chrome.options import Optionsfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.common.exceptions import NoAlertPresentException, TimeoutException,ElementNotInteractableException
2.加载驱动连接浏览器 chrome_options = webdriver.ChromeOptions() chrome_options.add_experimental_option("debuggerAddress" , "127.0.0.1:端口" ) driver = webdriver.Chrome(options=chrome_options) driver.switch_to.default_content() driver.get(url)
3.等待元素显示 WebDriverWait(driver, 50 ).until( EC.visibility_of_element_located((By.XPATH, '//*[@id="menu"]' ))) WebDriverWait(self.driver, 20 ).until( EC.visibility_of_element_located((By.XPATH, f'//*[@id="*"]/div[contains(text(),"{name} ")]' )))
timeout :最长等待时间,单位为秒。
until(method, message=’’) :
method:返回值为True或False的可调用方法(如lambda函数或自定义函数)。
message:可选参数,如果等待超时,将抛出TimeoutException异常时显示的消息。
locator :元组,包含两个元素,第一个是定位方法( 如By.XPATH),第二个是定位表达式 。
4.点击 driver.find_element(by=By.XPATH, value='//*[@id="menu"]' ).click() driver.find_element(by=By.XPATH, value='//*[contains(@id,"*")]' ).click() driver.find_element(by=By.XPATH, value='//*[contains(@class,"*")]' ).click() driver.find_element(by=By.XPATH, value='//*[contains(@name,"*")]' ).click()
**contains()**:函数用来判断属性值是否包含特定的字符串,这里用于模糊匹配ID。
5.切换 iframe 框架 使用WebDriverWait 配合预期条件(Expected Condition)来智能等待并切换到指定的iframe框架
WebDriverWait(driver, 50 ).until(EC.frame_to_be_available_and_switch_to_it((By.ID, "iframebox1" )))
driver :当前WebDriver实例,用于与浏览器交互。
50 :等待的最长时间(单位为秒),在此期间会不断检查条件是否满足。
frame_to_be_available_and_switch_to_it :
指定的iframe是否存在并且可交互(即“可用”)
当iframe可用时,自动切换到该iframe,使得后续的定位操作在该iframe的上下文中执行
(By.ID, “iframebox1”):这是定位iframe的方式和参数,通过ID定位,参数为”iframebox1”
6.获取标签的属性/文本 driver.find_element(by=By.XPATH, value='//*[@id="*' ).text driver.find_element(by=By.XPATH,value='//*[@id="*"][@dicttype="*"]' ).get_attribute('reltext' ) driver.find_element(by=By.XPATH,value='//div[contains(@class,"*")]/span[contains(text(),"*)"]' ).get_attribute('id' ) tr.get_attribute("class" )
浏览器控制
# 当前页面跳转到新的窗口 # 先获取当前各个窗口的句柄,这些信息的保存顺序是按照时间来的,最新打开的窗口放在数组的末尾,这时就可以定位到最新打开的那个窗口了 # 获取打开的多个窗口句柄 windows = driver.window_handles # 切换到当前最新打开的窗口 driver.switch_to.window(windows[-1]) # 在点击前跳转的时候保存当前窗口句柄 original_window = driver.current_window_handle # 点击操作后 WebDriverWait(driver, 10).until(EC.number_of_windows_to_be(2)) # 动态获取新窗口句柄 all_windows_after_click = driver.window_handles if len(all_windows_after_click) > 1: new_window = [window for window in all_windows_after_click if window != original_window][0] driver.switch_to.window(new_window) print('进入新窗口') else: print("新窗口未按预期打开") # 操作完成后关闭当前窗口(B系统) driver.close() # 切换回A系统的窗口 driver.switch_to.window(original_window)
方法
描述
send_keys()
模拟输入指定内容
clear()
清除文本内容
is_displayed()
判断该元素是否可见
get_attribute()
获取标签属性值
size
返回元素的尺寸
text
返回元素文本
鼠标控制
方法
描述
click()
单击左键
context_click()
单击右键
double_click()
双击
drag_and_drop()
拖动
move_to_element()
鼠标悬停
perform()
执行所有ActionChains中存储的动作
button = driver.find_element_by_xpath('//*[@id="toolbar-search-button"]/span' ) button.click() from selenium.webdriver.common.action_chains import ActionChainsbutton = driver.find_element_by_xpath('//*[@id="toolbar-search-button"]/span' ) ActionChains(driver).context_click(button).perform() button = driver.find_element_by_xpath('//*[@id="toolbar-search-button"]/span' ) ActionChains(driver).double_click(button).perform() collect = driver.find_element_by_xpath('//*[@id="csdn-toolbar"]/div/div/div[3]/div/div[3]/a' ) ActionChains(driver).move_to_element(collect).perform()
键盘控制 from selenium.webdriver.common.keys import Keysdriver.find_element_by_id('xxx' ).send_keys('Dream丶killer' ) driver.find_element_by_id('xxx' ).send_keys(Keys.ENTER) driver.find_element_by_id('xxx' ).send_keys(Keys.BACK_SPACE) driver.find_element_by_id('xxx' ).send_keys(Keys.CONTROL, 'a' ) driver.find_element_by_id('xxx' ).send_keys(Keys.CONTROL, 'c' ) driver.find_element_by_id('xxx' ).send_keys(Keys.CONTROL, 'v' )
操作
描述
Keys.F1
F1键
Keys.SPACE
空格
Keys.TAB
Tab键
Keys.ESCAPE
ESC键
Keys.ALT
Alt键
Keys.SHIFT
Shift键
Keys.ARROW_DOWN
向下箭头
Keys.ARROW_LEFT
向左箭头
Keys.ARROW_RIGHT
向右箭头
工作技巧
WebDriverWait(self.driver, 10 ).until( EC.visibility_of_element_located( (By.XPATH, '//*[@id="*"]/div/table' ))) print ('--table装载完成--' )table = WebDriverWait(self.driver, 20 ).until(EC.presence_of_element_located((By.XPATH, '//*[@id=" * "]/div/table' ))) tbody = table.find_element(By.TAG_NAME,"tbody" ) for tr in tbody.find_elements(By.TAG_NAME,"tr" ): if 'ant-table-row ant-table-row-level-0' in tr.get_attribute("class" ): td_check = tr.find_elements(By.TAG_NAME, "td" )[5 ] if td_check.text.strip() == "定位数据" : td_data = tr.find_elements(By.TAG_NAME, "td" )[6 ].text name = td_data.split('/' ,1 )[0 ] bu_men = tr.find_elements(By.TAG_NAME, "td" )[7 ].text
sl_id = self.driver.find_element(by=By.XPATH, value='//div[contains(@class,"*")]/span[contains(text(),"*")]/..//input[@class="*"]' ).get_attribute('id' )
PyWinAuto
控制软件的第一件事就是启动一个Windows软件,每一个软件(进程)都是一个Application对象
实例化Application对象的时候可以传入一个backend参数,可选值为win32(默认)和 uia ,
win32对应的框架:MFC、VB6、VCL、简单的 WinForms 控件和大多数旧的遗留应用程序
uia对应的框架:WinForms、WPF、商店应用程序、Qt5、浏览器
如果无法知道要测试的软件是属于哪种框架,可以使用 Inspect(对应uia) 和 Spy++(对应win32) 看看,哪个显示得更全就选哪个
环境准备
pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
from pywinauto.application import Application
简单使用
方法
常用参数
说明
start()
cmd_line、timeout、retry_interval
通过cmd命令启动一个软件(进程)
connect()
process、handle、path、timeout
连接一个进程,一般是使用进程号(任务管理器可以看到)
top_window()
/
获取应用的顶层窗口
window()
title、title_re、class_name、best_match
获取单个窗口(WindowSpecification)
windows()
title、title_re、class_name
获取多个窗口(UIAWrapper)
active()()
/
搜索返回一个激活的窗口
kill()
soft
结束进程
wait_for_process_exit()
timeout、retry_interval
等待进程结束
1.启动应用
app = Application(backend="uia" ).start("notepad.exe" ) app = Application(backend="uia" ).start(r"D:\Wechat\WeChat.exe" )
2.连接应用
app = Application("uia" ).connect(process=40936 ) app = Application("uia" ).connect(handle=197736 ) app = Application(backend="uia" ).connect(title="微信" ) app = Application(backend="uia" ).start(r"D:\Wechat\WeChat.exe" ) app.connect(title="微信" )
3.选择窗口
dlg = app["WeChatMainWndForPC" ] dlg.print_control_identifiers() dlg = app["微信" ] dlg.print_control_identifiers() dlg = app.WeChatMainWndForPC dlg.print_control_identifiers()
4.操作窗口 dlg.maximize() dlg.minimize() dlg.restore() status = dlg.get_show_state() xy = dlg.rectangle() print (xy)dlg.close()
5.定位元素
dlg.child_window(title="进入微信" , control_type="Button" ).click_input() dlg.child_window(title="小伙伴" , control_type="Button" ).click_input() btn = dlg['设置及其他' ] btn.draw_outline(colour="red" ) btn.click_input()
6.等待元素
login_button = dlg.child_window(title='进入微信' , control_type='Button' ) has_btn = login_button.exists(timeout=20 ) dlg.child_window(title='进入微信' , control_type='Button' ).exists(timeout=20 )
微信单发送案例 from pywinauto.application import Applicationfrom pyautogui import hotkeyapp = Application(backend="uia" ).start(r"D:\Wechat\WeChat.exe" ) app.connect(title="微信" ) dlg = app["微信" ] fileSend = dlg.child_window(title="文件传输助手" , control_type="ListItem" ) fileSend.click_input() dlg.child_window(title="文件传输助手" , control_type="Edit" ).click_input() dlg.child_window(title="文件传输助手" , control_type="Edit" ).type_keys('测试' ) hotkey('enter' )
微信完整流程案例
1.判断是否登录
2.未登录进行点击登录
3.进入界面列表查询聊天对象是否存在
4.聊天对象不存在则搜索
5.进入聊天对象界面
6.输入内容
7.发送
login_button = dlg.child_window(title='进入微信' , control_type='Button' ) has_btn = login_button.exists(timeout=20 ) if has_btn: login_button.click_input() if dlg.child_window(title='小伙伴' , control_type='Text' ).exists(timeout=2 ): dlg.child_window(title='小伙伴' , control_type='Text' ).click_input() else : dlg.child_window(title="搜索" , control_type="Edit" ).click_input() dlg.child_window(title="搜索" , control_type="Edit" ).type_keys("RPA运维小伙伴" ) dlg.child_window(best_match="小伙伴" , control_type="ListItem" ).click_input() dlg.child_window(title="小伙伴" , control_type="Edit" ).type_keys( "======== PyWinAuto正在操控微信======== " ) hotkey('enter' )
本地文件复制到剪切板 def send_image_to_user (image_path ): img = Image.open (image_path) output = BytesIO() img.convert("RGB" ).save(output, "BMP" ) data = output.getvalue()[14 :] print (f'事件: 当前读取的图片资源 - {len (data)} 字节' ) output.close() clip.OpenClipboard() clip.EmptyClipboard() clip.SetClipboardData(CF_DIB, data) time.sleep(7 ) clip.CloseClipboard() hotkey('ctrl' , 'v' ) hotkey('enter' )
Excel转图片
# excel转图片-截图方式 def excel_to_image_excel2img(excel_path): # 要截图的工作表名称 sheet_name = 'Sheet1' # 保存图片到本地 img_filename = f"{os.path.splitext(os.path.basename(excel_path))[0]}" if '.' in img_filename: img_filename = f"{os.path.splitext(img_filename)[0]}" img_filename += '.png' img_path = os.path.join(CHROME_DOWNLOADS, img_filename) save_log("开始截图保存") excel2img.export_img(excel_path, img_path, sheet_name, None)