在玩PNETLab模拟器实验遇到的小问题,从登陆PNETLab到能登陆拓扑里的设备这一段时间是很无聊且每次都是重复动作
以前做测试就点点点没觉得是什么问题,但是设备数量多的大型拓扑,那是一个超长等待时间
想过的方法是查查API,无功而返,而且也不熟
最后使用Selenium实现功能
环境说明
- Selenium==4.22.0
- Python==Python 3.10.13
- PNETLab== 5.3.13
- ChromeDriver==126.0.6478.126
- PNETLab关闭验证码验证
效果展示
显示窗口执行(控制台同时输出)
无显示窗口执行(控制台输出)
脚本代码
就不过多文字描述了,代码里的注释有说明
Note1:PNETLab的打开速度和拓扑文件的载入速度有很多影响因素(电脑硬件,拓扑文件大小,镜像等)
Note2:如果你在本地运行代码后提示错误,大概率可以通过调整显式等待时间和隐式等待时间解决(PNETLab的版本也要考虑,有可能定位元素有变动)
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
import time
import random
# 创建 ChromeOptions 对象
options = webdriver.ChromeOptions()
# 添加参数headless后不显示窗口
options.add_argument('--headless')
# 控制台不显示INFO:CONSOLE,默认0,显示与服务端的的交互内容
options.add_argument('--log-level=3')
# 4.6版本前需要指定路径,使用本地驱动
# 其实前后版本都可以使用本地驱动(本地好使,加载快,无须联网下载ChromeDriver)
# 指定浏览器驱动路径,以Chrome为例(与电脑安装的谷歌浏览器版本大版本号一致即可或添加参数忽略(自己查))
# 当前日期(20240704)使用最新版本selenium==4.22.0
# chromedriver_bin = 'D:/Plugs/ChromeDriver/126.0.6478.126/chrome-win64/chromedriver.exe'
chromedriver_bin = r'D:\Plugs\ChromeDriver\126.0.6478.126\chrome-win64\chromedriver.exe'
service = webdriver.ChromeService(executable_path=chromedriver_bin)
driver = webdriver.Chrome(service=service, options=options)
# 4.6版本之后使用默认的实例,使用如下代码(自动联网下载ChromeDriver)
# 当前日期(20240704)使用最新版本selenium==4.22.0
# service = Service()
# driver = webdriver.Chrome(options=options, service=service)
# selenium参数定义
# 显式等待设置,等待最多10秒钟,每0.5秒检查一次条件是否满足
# 该参数对需要控制的元素定位起作用
wait = WebDriverWait(driver, 10)
# 设置一个隐式等待时间,因为点击open后的loading时间不可预计
# 可能的因素有拓扑unl文件大小,电脑硬件配置
# 设置隐式等待时间为6秒,该参数全局作用,按需调整数值
driver.implicitly_wait(6)
# 本地PNETLab登陆参数定义
login_url = 'http://x.x.x.x/'
username = 'admin'
password = 'pnet'
# 自动登陆操作
def pnetlab_login():
# 打开PNETLab登陆页面登陆
driver.get(login_url)
# 定位用户名输入框并输入用户名
username_input = wait.until(EC.visibility_of_element_located((By.XPATH, '//input[@placeholder="Username"]')))
username_input.clear()
username_input.send_keys(username)
# 定位密码输入框并输入密码
password_input = wait.until(EC.visibility_of_element_located((By.XPATH, '//input[@placeholder="Password"]')))
password_input.clear()
password_input.send_keys(password)
# 定位登录按钮并点击
login_button = driver.find_element(By.XPATH, '//div[@class="button btn btn-info" and text()="Login"]')
login_button.click()
try:
# 使用显式等待来等待搜索框元素出现
search_element = wait.until(EC.visibility_of_element_located((By.XPATH, '//input[@placeholder="Search Labs"]')))
print("登录成功!")
return True
except Exception as e:
print("登录失败或者无法找到搜索框元素.\n", str(e))
return False
# 随机抽一个lab文件
def random_lab():
lab_names = ('H1 CFG SPOTO Ver_1.unl',
'H1 Plus CFG SPOTO Ver_1.unl',
'H2 CFG SPOTO CCIE RS Ver_1.unl',
'H2 Plus CFG SPOTO Ver_1.unl',
"H3 SPOTO CCIE RS Ver_1.unl")
chosen_lab = random.choice(lab_names)
return chosen_lab
def main():
try:
# 登陆
sign_in = pnetlab_login()
if not sign_in:
print('>>>登陆有问题')
return
# 随机抽
chosen_lab = random_lab()
print('-' * 50 + f'\n本次抽中的lab是:{chosen_lab[:-4]}')
# 搜索框输入lab文件名
search_element = wait.until(EC.visibility_of_element_located((By.XPATH, '//input[@placeholder="Search Labs"]')))
search_element.clear()
search_element.send_keys(chosen_lab)
# 等待第一个搜索结果可见并点击
time.sleep(1)
first_button = wait.until(
EC.visibility_of_element_located((By.XPATH, "//div[@id='search_lab_suggest']/div[@class='button'][1]")))
first_button.click()
# 等待第二个搜索结果可见并点击
# second_button = wait.until(
# EC.visibility_of_element_located((By.XPATH, "//div[@id='search_lab_suggest']/div[@class='button'][2]")))
# second_button.click()
# 点击open按钮
open_button = driver.find_element(By.CSS_SELECTOR, "div.box_flex > div.btn-info:nth-child(1)")
open_button.click()
# 等不到加载完成页面报错,调大隐式等待时间
# 触发左侧菜单的元素,左下角log图片class类
leftmenu_trigger = driver.find_element(By.CLASS_NAME, "logo_img")
# 创建 ActionChains 对象
actions = ActionChains(driver)
# 移动鼠标到左侧菜单触发元素上,触发菜单显示
actions.move_to_element(leftmenu_trigger).perform()
# 点击Setup Nodes
setup_nodes = driver.find_element(By.XPATH, '//span[text()="Setup Nodes"]')
setup_nodes.click()
# Start All Nodes
start_all = driver.find_element(By.CLASS_NAME,"action-nodesstart")
start_all.click()
# 查看已启动的IOL Nodes 的数值
# 移动鼠标到左侧菜单触发元素上,触发菜单显示
actions.move_to_element(leftmenu_trigger).perform()
# 点击System status
system_status = driver.find_element(By.XPATH, '//span[text()="System Status"]')
system_status.click()
print('-' * 50 + '\n正在启动设备\n')
# 30秒后获取已启动设备数值
# time.sleep(30)
# 循环获取启动的设备数值,间隔10秒获取一次,共5分钟
x = 1
while x < 30:
time.sleep(10)
started = wait.until(EC.presence_of_element_located((By.XPATH, "//td/strong[@style='color: red;']")))
# 打印已启动设备数值
print("已启动设备数:", started.text)
if int(started.text) >= 40:
break
x += 1
time.sleep(5)
print(f'设备启动完成,打开链接{login_url},开始实验\n' * 3 + '-' * 50)
# //////////////////////////////////////////////////////////////////////////////////
# # destroy lab 退出
# # 测试用,随后可能用不到
# # time.sleep(10)
# print('正在关闭设备\n')
# # 移动鼠标到左侧菜单触发元素上,触发菜单显示
# actions.move_to_element(leftmenu_trigger).perform()
# # 点击Destroy Lab
# destroy_lab = driver.find_element(By.XPATH, '//span[text()="Destroy Lab"]')
# destroy_lab.click()
#
# # 定位弹窗的 Destroy 按钮
# destroy_button = driver.find_element(By.CLASS_NAME, "swal2-confirm")
#
# # 定位弹窗的 Cancel 按钮
# # cancel_button = driver.find_element(By.CLASS_NAME, "swal2-cancel")
#
# # 二次确认点击
# destroy_button.click()
#
# # 循环获取启用的设备数值,间隔10秒获取一次,共5分钟
# x = 600
# while x > 0:
# time.sleep(10)
# started = WebDriverWait(driver, 10).until(
# EC.presence_of_element_located((By.XPATH, "//td/strong[@style='color: red;']"))
# )
#
# # 打印剩余启动设备数值
# print("剩余启动设备数:", started.text)
# if int(started.text) < 10:
# break
# x -= 1
#
# print('设备已关闭\n' * 3 + '-' * 50)
# time.sleep(10)
# //////////////////////////////////////////////////////////////////////////////////
except Exception as e:
print(e)
print('>>>OPS,FUCKED UP')
finally:
# 关闭浏览器驱动
driver.quit()
if __name__ == '__main__':
main()
最后
- 忽略代码中变量名的命名拼写错误,词汇量紧张
- 部分代码来自GPT,官网示例(注意看版本对应的示例)
- 大部分时间都耗在定位元素了,pnet为了玩花样或是抄来不想改这html前端也是无限套壳
- 有些是前端渲染代码是通过java函数生成的,这就有点不好玩了
- 欢迎“
来电”来函探讨。