使用selenium操作页面时有时需根据同级元素的属性来定位某元素,并进行操作。
如一个列表,内多个表项,第一列为名称,最后一列为一个按钮,需根据该行第一列内容判断是否点击后面的按钮。
以下以一个具体场景为例展示如何实现:
#!/usr/bin/python
# -*- coding:utf-8 -*-
# @author : lxy
# @time : 2024/8/3
"""selenium中的元素查找
本脚本实现批量分离和重连openstack中实例的网络接口(未完成)。
批量操作流程:
1. 创建webdriver
2. 进入登录界面,输入登录信息
3. 进入需要操作的界面
经验:
1. webdriver和webelements都有find_elements()等方法
2. 通过直接复制元素的Xpath或full Xpath来定位元素
3. 通过先定位父元素,来解决同级元素属性作为判断条件的问题
Typical usage example:
horizon = OpenStackHorizon(login_url, instance_url, username, password)
horizon.login()
horizon.reconnect_port()
"""
import time
from selenium import webdriver
from selenium.common import ElementClickInterceptedException
from selenium.common.exceptions import NoSuchElementException, TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
class OpenStackHorizon:
def __init__(self, login_url, instance_url, username, password, domain="default"):
# 初始化webdriver
options = webdriver.ChromeOptions()
# options.add_argument('--headless') # 无头模式,不打开实际的浏览器窗口
options.add_argument('--ignore-certificate-errors') # 忽略证书错误
self.driver = webdriver.Chrome(options=options)
self.login_url = login_url
self.instance_url = instance_url
self.username = username
self.password = password
self.domain = domain
def login(self):
try:
# 打开登录页面
self.driver.get(self.login_url)
# 等待并找到用户名输入框
WebDriverWait(self.driver, 10).until(
EC.presence_of_element_located((By.NAME, "username"))
)
# 输入用户名和密码
self.driver.find_element(By.NAME, "domain").send_keys(self.domain)
self.driver.find_element(By.NAME, "username").send_keys(self.username)
self.driver.find_element(By.NAME, "password").send_keys(self.password)
# 提交表单
self.driver.find_element(By.XPATH, '//button[@type="submit"]').click()
# 确认登录成功,等待某个已知会出现在登录成功后的元素,例如导航栏中的一个元素
WebDriverWait(self.driver, 10).until(
EC.presence_of_element_located((By.ID, "main_content"))
)
print("登录成功")
except (ElementClickInterceptedException, TimeoutException) as e:
print(f"登录失败: {e}")
def reconnect_port(self):
try:
# 访问实例页面
self.driver.get(self.instance_url)
# 等待页面加载,并确保特定数据行存在
WebDriverWait(self.driver, 20).until(
EC.presence_of_element_located((By.TAG_NAME, "tbody"))
)
for i in range(1, 61):
# 确保表格内容已加载,等待特定行内容出现
while True:
rows = self.driver.find_elements(By.XPATH, "//tbody/tr")
if len(rows) >= 3:
break
time.sleep(1)
# 过滤实例列表
self.driver.find_element(By.XPATH, '//*[@id="instances"]/caption/div/div[1]/div/button').click()
# 对于固定的元素,直接在浏览器中复制其Xpath即可。
WebDriverWait(self.driver, 20).until(
EC.presence_of_element_located((By.XPATH, '//*[@id="instances"]/caption/div/div[1]/div/ul/li[2]/a'))
)
self.driver.find_element(By.XPATH, '//*[@id="instances"]/caption/div/div[1]/div/ul/li[2]/a').click()
self.driver.find_element(By.XPATH, '//*[@id="instances"]/caption/div/div[1]/input').send_keys(
"scada-{}".format(i))
self.driver.find_element(By.XPATH, '//*[@id="instances__action_filter"]').click()
# 等待筛选结果
WebDriverWait(self.driver, 10).until(
EC.presence_of_element_located((By.XPATH, "//tbody/tr"))
)
# 查找匹配的行
# 这种不固定的元素,可以复制其full Xpath,观察需要的内容。
# 在此例情况中,按条件筛选后获得一个列表,列表第一列中包含实例名,最后一列中包括一个可互动的按钮(链接)
# 现要根据实例名选取对应行,点击对应按钮
# 可先根据列表Xpath,find_elements获取所有表项,然后逐行处理
rows = self.driver.find_elements(By.XPATH, "//tbody/tr")
for row in rows:
try:
# 找到第一列的链接内容
instance_name = row.find_element(By.XPATH, "./td[1]/a").text
if instance_name == "scada-{}".format(i):
# 找到并点击目标元素
target_element = row.find_element(By.XPATH, "./td[13]/div/a[2]")
target_element.click()
"""
未完待续,继续需要的操作即可
"""
break
except NoSuchElementException:
continue
except (ElementClickInterceptedException, TimeoutException) as e:
print(f"操作失败: {e}")
def __del__(self):
# 退出webdriver
self.driver.quit()
def main():
# 使用示例
login_url = "http://10.254.0.100/horizon/auth/login/"
instance_url = "http://10.254.0.100/horizon/project/instances/"
username = "admin"
password = "adminpasswd"
horizon = OpenStackHorizon(login_url, instance_url, username, password)
horizon.login()
horizon.reconnect_port()
if __name__ == "__main__":
main()