一、前置知识
1、find_element()和find_elements()的区别
find_element():
- 目标:查找并返回第一个匹配定位策略的元素
- 返回对象:一个
WebElement对象 - 未找到对象时:抛出异常
NoSuchElementException,程序中断 - 主要用途:与查找的唯一、特定元素进行交互,比如:点击登录按钮,唯一的搜索框中输入文本搜索
- 后续操作:直接对返回的
WebElement对象进行操作(send.keys(),click())
find_elements()
- 目标:查找并返回所有定位策略的元素
- 返回对象:一个或多个
WebElement对象的列表(list) - 未找到对象时:返回空列表[],程序继续执行
- 主要用途:获取一组元素进行遍历、检查符合条件的元素是否存在(返回列表的长度大于0)、获取页面元素的总数
- 后续操作:先遍历列表,再对列表中每个
WebElement对象进行操作
2、driver和drivers的区别
driver:一个单一的、控制一个浏览器实例的WebElement 对象
drivers:
一个或包含多个driver对象的集合(列表或者字典),同时管理多个浏览器实例
应用场景:
并发测试:同时在不同浏览器运行不同测试用例,缩短总的测试时间
跨浏览器测试:验证网页兼容性
多用户模拟:模拟多个用户同时登录或操作一个网站
3、实例脚本
from selenuim import webdriver
import time
import threading
from selenuim.wendriver.support import excepted_conditions as EC
browser_types= ['chome' ,'firefox' ]
drivers = []
for browser_type in browser_types:
if browser_type == "chome":
driver = webdriver.Chome()
elsif browser_type == "firefox"
driver = webdriver.Firefox()
drivers.append(driver)
print(f"成功初始化了 {len(drivers)} 个浏览器实例。")
# 定义一个函数,让每个浏览器执行相同的操作
def perform_search(driver_instance, keyword):
try:
driver_instance.get("https://www.google.com" )
search_box = driver_instance.find_element("name", "q")
search_box.send_keys(keyword)
search_box.submit()
print(f"[{driver_instance.name}] 正在搜索: {keyword}")
time.sleep(5) # 等待搜索结果
except Exception as e:
print(f"在 {driver_instance.name} 中发生错误: {e}")
# 创建并启动线程,让每个浏览器并行执行搜索任务
threads = []
for i, driver_item in enumerate(drivers):
# 为每个浏览器创建一个线程
thread = threading.Thread(target=perform_search, args=(driver_item, f"并行测试 {i+1}"))
threads.append(thread)
thread.start()
# 等待所有线程执行完毕
for thread in threads:
thread.join()
# 所有任务完成后,遍历 drivers 列表,关闭所有浏览器
print("\n所有任务完成,正在关闭所有浏览器...")
for driver_item in drivers:
driver_item.quit()
print("所有浏览器已关闭。")
二、selenium元素定位的八种方法
1、ID定位
优点:在一个页面中id是唯一的,定位是最快的
缺点:不是所有元素都有id
适用场景:当前元素有id属性时,优先使用
html页面示例:
<input id="loginName" type="text" placeholder="请输入用户名">
定位脚本:
username = driver.find_element(By.ID, "loginName")
2、Name定位
优点:表单通常使用name属性
缺点:定位不唯一,可能有多个
适用场景:表单元素(下拉框、输入框、单选框)
html页面示例:
<input name="password" type="password">
定位脚本:
pwd = driver.find_element(By.NAME, "password")
pwd.send_keys("123456")
3、Class_Name定位
优点:元素基本都会使用class
缺点:定位不唯一,可能会定位到多个元素
适用场景:元素有独特的class名称
html示例:
<button class="login-btn">登录</button>
<span class="t-checkbox__label">运营角色</span>
定位脚本:
login_button = driver.find_element(By.CLASS_NAME, "login-btn")
login_button.click()
checkbox = driver.find_element(By.CLASS_NAME, "t-checkbox__label")
4、Tag_Name定位
优点:通过html标签名定位
缺点:同类型标签很多,定位不唯一
适用场景:标签少,或配合其他定位方式一起使用
html示例:
<ul>
<li><a href="https://www.google.com">谷歌</a></li>
<li><a href="https://www.bing.com">必应</a></li>
<li><a href="https://www.yahoo.com">雅虎</a></li>
</ul>
定位上面的所有的链接:
links = driver.find_elemets(By.TAG_NAME, "a")
5、Link Text定位
优点:通过链接的完整内容文本定位,直观,利于理解和维护
缺点:只能用于链接元素,且文本需完全匹配
适用场景:专门用于定位超链接(<a>标签)
html示例:
<a href="/home">首页</a>
定位脚本:
home_link = driver.find_element(By.LINK_TEXT, "首页")
home_link.click()
6、Partial Link Text定位
优点:链接文本部分内容定位,更灵活不需要完整匹配
缺点:只能用于链接,可能会匹配到多个
适用场景:当链接文本过长或需要变化时
html示例:
<a href="/download">下载最新版本软件</a>
定位脚本:
find_link = driver.find_elemet(By.PARTIAL_LINK_TEXT, "下载")
find_link.click()
7、CSS选择器定位
优点:适用CSS语法选择器定位,灵活性高,可组合多种条件查找
缺点:语法复杂
适用场景:复杂定位需求,其他方法需组合使用时
html示例:
<span CLASS=t-checkbox__label>运营角色</span>
定位脚本:
label = driver.find_element(By.CSS_SELECTOR, "span.t-checkbox__label")
8、XPath定位
优点:适用xpath语法定位元素,是最强大的定位方法,可以实现任何复杂的定位需求
缺点:语法复杂,性能相对较慢
适用场景:当其他定位方法都不可用时
定位脚本:
# 绝对路径(不推荐,太脆弱)
element = driver.find_element(By.XPATH, "//html/body/div[1]/form/input[1]")
# 相对路径(推荐)
element = driver.find_element(By.XPATH, "//input[@id='username']")
# 通过文本内容定位
button = driver.find_element(By.XPATH, "//button[text()='登录']")
# 通过属性定位
element = driver.find_element(By.XPATH, "//input[@placeholder='请输入用户名']")
# 通过父子关系定位
element = driver.find_element(By.XPATH, "//div[@class='form-group']//input")
parent::找父元素
child::找子元素
# 通过兄弟关系定位
element = driver.find_element(By.XPATH, "//label[text()='用户名']/following-sibling::input")
preceding-sibling::向前找兄弟元素
following-sibling::向后找兄弟元素
三、定位示例
# 假设HTML结构:
html_example = """
<div class="parent">
<input type="text" /> <!-- 前面兄弟 -->
<input type="checkbox" /> <!-- 前面兄弟 -->
<span>运营角色</span> <!-- 当前元素 -->
<button>确定</button> <!-- 后面兄弟 -->
</div>
"""
# 各种轴的使用:
axis_examples = {
"前面的兄弟": "//span[text()='运营角色']/preceding-sibling::input",
"后面的兄弟": "//span[text()='运营角色']/following-sibling::button",
"父元素": "//span[text()='运营角色']/parent::div",
"子元素": "//div[@class='parent']/child::span",
"所有前面兄弟": "//span[text()='运营角色']/preceding-sibling::*"
}
复杂的XPath详细解析
html示例:
<!-- 实际的HTML结构可能是这样的: -->
<div class="t-popup__content">
<div class="popup-header">...</div>
<div class="popup-body">
<ul class="option-list">
<li class="option-item">
<input type="checkbox" tabindex="-1" /> <!-- 这是目标 -->
<span>运营角色</span>
</li>
<li class="option-item">
<input type="checkbox" tabindex="0" /> <!-- 不是目标 -->
<span>管理角色</span>
</li>
</ul>
</div>
</div>
定位脚本:
"//div[@class='t-popup__content']//input[@type='checkbox' and @tabindex='-1']"
//div[@class='t-popup__content']:整个页面中查找class为't-popup__content'的div元素,通常是一个弹出框的内容区域
//input[@type='checkbox' and @tabindex='-1']:在前面找到的div的任意层级中查找type为'checkbox‘并且tabindex为'-1'的input元素
//= 任意层级(不管隔了多少层)/= 直接子层级(只能是直接子元素)and= 同时满足多个条件
四、踩过的坑
原始HTML
<button
class="t-button t-button--variant-base t-button--theme-primary t-size-l !mt-[60px] !w-full"
type="submit"
tabindex="0">
<span class="t-button__text">登录</span>
</button>
定位脚本1(未找到该元素):
login_button = (By.XPATH, "//button[@type='submit' and contains(@class,'t-button')]")
定位脚本2(未找到该元素):
login_button = (By.XPATH, "//button[contains(@class,'t-button') and .//span[normalize-space()='登录']]")
定位失败原因:
- 依赖文字
- 语法层级太深
- XPATH易读性差
最终换成了如下脚本(定位到了该元素):
login_button = (By.CSS_SELECTOR, "button.t-button[type='submit']")
其他情况:如果HTML页面上有多个submit按钮
driver.find_elements(By.CSS_SELECTOR, "button.t-button[type='submit']")[0]
定位成功原因:
基于标准属性 (type=submit) + 稳定 class (t-button) + 明确标签 (button),不依赖会频繁变动的 UI 文本或层级,能保证定位长期可用
7253

被折叠的 条评论
为什么被折叠?



