承接上一章节的基础定位知识,相信大家已经能轻松搞定那些 "标签清晰、属性唯一" 的简单元素啦!但实际测试中,总会遇到元素没 ID、Name 重复、链接文本超长的 "调皮情况",基础定位就像 "常规武器",这时就需要 CSS Selector 这种 "精准狙击枪" 登场~ 这节课咱们就手把手教你玩转 CSS Selector,解锁复杂页面的定位技能!
为什么需要 CSS Selector?------ 基础定位的 "补位高手"
上一章学的 ID、Name 定位虽然简单,但实际项目中会遇到这些坑:
- 元素没有 ID/Name 属性,只有 class 且不唯一;
- 多个元素标签、class 完全一致,只能靠层级或属性区分;
- 动态元素(属性值会变),无法用固定属性定位。
而 CSS Selector 的优势堪称 "全能":语法简洁、定位速度快、支持多条件组合、兼容所有浏览器,既能搞定简单元素,更能轻松破解复杂场景,是自动化测试工程师的 "必备杀手锏"!
CSS Selector 核心语法实战 ------ 手把手教你写表达式
1) ID选择器
原理: 通过元素的"id"属性定位
语法示例:
python
#选择id="username"的元素
driver.find_element(By.CSS_SELECTOR,"#username")
案例实操:
我们还是以百度为例,进行实战操作,我们回到最初打开百度网页的代码,用CSS选择器定位进行重新实现。我们通过ID选择器定位到登录按钮,并点击。

找到登录按钮的id属性以后,我们通过以下代码进行定位并点击,如下:
python
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
def test_baidu():
# 初始化浏览器驱动(chrome浏览器)
driver = webdriver.Chrome()
# 打开百度
driver.get("https://www.baidu.com/")
# 定位到登录按钮,并点击
driver.find_element(By.CSS_SELECTOR,"#s-top-loginbtn").click()
# 等待3秒
sleep(3)
# 关闭浏览器
driver.quit()
if __name__ == "__main__":
test_baidu()
运行以上代码,发现程序帮我们实现了登录按钮的定位和点击,如下:

2) 类选择器
原理: 通过元素的"class"属性定位
语法示例:
python
# 选择class="btn-primary"的所有元素
driver.find_elements(By.CSS_SELECTOR,".btn-primary")
案例实操:
接着上一步的实操,我们使用类选择器定位到手机号输入框,并输入手机号码。

找到手机号输入框的class属性以后,通过以下代码进行定位:
python
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
def test_baidu():
# 初始化浏览器驱动(chrome浏览器)
driver = webdriver.Chrome()
# 打开百度
driver.get("https://www.baidu.com/")
# 定位到登录按钮,并点击
driver.find_element(By.CSS_SELECTOR,"#s-top-loginbtn").click()
sleep(3)
# 定位到手机号码输入框,并输入
driver.find_element(By.CSS_SELECTOR,".pass-text-input-userName").send_keys("15111111111")
# 等待3秒
sleep(3)
# 关闭浏览器
driver.quit()
if __name__ == "__main__":
test_baidu()
运行代码后,程序自动完成手机号输入框的定位,并自动完成手机号码的输入,如下:

3) 标签名选择器
原理: 通过元素的标签名进行定位
语法示例:
python
# 选择所有<input>标签的元素
driver.find_elements(By.CSS_SELECTOR,"input")
案例实操:
我们还是以input标签为例,获取页面中所有的input标签,并打印其数量。代码如下:
python
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
def test_baidu():
# 初始化浏览器驱动(chrome浏览器)
driver = webdriver.Chrome()
# 打开百度
driver.get("https://www.baidu.com/")
# 定位到登录按钮,并点击
driver.find_element(By.CSS_SELECTOR,"#s-top-loginbtn").click()
sleep(3)
# 定位到手机号码输入框,并输入
driver.find_element(By.CSS_SELECTOR,".pass-text-input-userName").send_keys("15111111111")
# 找到所有的输入框,并统计数量
allinputs = driver.find_elements(By.CSS_SELECTOR, "input")
print(f"输入框的数量:{len(allinputs)}")
# 等待3秒
sleep(3)
# 关闭浏览器
driver.quit()
if __name__ == "__main__":
test_baidu()
测试运行效果如下:

4) 属性选择器
原理: 通过元素标签的具体属性值进行定位
语法示例:
python
# 选择所有的type="text"的<input>元素
driver.find_elements(By.CSS_SELECTOR,"input[type="text"]")
案例实操:
接着上面的代码,我们通过属性选择器找到密码输入框,并输入密码。我们来看看密码输入框有哪些属性,如下:

我们可以选择name属性进行定位,寻找name="password"的input标签,代码如下:
python
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
def test_baidu():
# 初始化浏览器驱动(chrome浏览器)
driver = webdriver.Chrome()
# 打开百度
driver.get("https://www.baidu.com/")
# 定位到登录按钮,并点击
driver.find_element(By.CSS_SELECTOR,"#s-top-loginbtn").click()
sleep(3)
# 定位到手机号码输入框,并输入
driver.find_element(By.CSS_SELECTOR,".pass-text-input-userName").send_keys("15111111111")
# 找到所有的输入框,并统计数量
allinputs = driver.find_elements(By.CSS_SELECTOR, "input")
print(f"输入框的数量:{len(allinputs)}")
# 找到密码输入框,并输入密码
driver.find_element(By.CSS_SELECTOR,"input[name='password']").send_keys("79&abe$")
# 等待3秒
sleep(3)
# 关闭浏览器
driver.quit()
if __name__ == "__main__":
test_baidu()
代码中注意"input[name='password']" 当使用双引号包裹属性值时,内部字符串应使用单引号以避免冲突。反之如果外部使用单引号包裹属性值,则内部字符串要使用双引号。运行效果如下:

5) 后代选择器
原理: 通过空格实现定位一个元素的指定后代元素定位
语法示例:
python
# 选择div元素内所有class属性为content的后代元素
driver.find_element(By.CSS_SELECTOR,"div .content")
# 选择 id="csdn-toolbar"元素的后代中type="search"的元素
driver.find_element(By.CSS_SELECTOR," #csdn-toolbar [type='search']")
案例实操:
前面的例子中,我们在定位密码输入框的时候,使用了属性选择器来定位name="password"的input标签,以此完成了密码的自动输入。但是细心的同学们可能会发现一个问题,就是当前页面中短信登录方式中验证码的输入框其实也符合name="password",如下:

所以在这种属性重复的情况下,单纯靠单一的属性定位是不严谨的,特别是如果我们想要定位的是验证码输入框,而不是密码输入框时,这会导致我们定位不到预期的元素。接下来我们尝试用本节学习到后代选择器来优化一下。首先顺着账号登录的密码输入框和短信登录中的验证码输入框分别逐级向上查找,发现账户登录的内容是在id="TANGRAM__PSP_11__form"的form节点下,而短信登录的内容是在id="TANGRAM__PSP_11__sms"的div节点下,如下:

所以如果我们要定位到账户登录的密码输入框,只需要先定位到id="TANGRAM__PSP_11__form"元素,然后查找其后代中name="password"的元素,就可以唯一定位到密码输入框了,代码如下:
python
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
def test_baidu():
# 初始化浏览器驱动(chrome浏览器)
driver = webdriver.Chrome()
# 打开百度
driver.get("https://www.baidu.com/")
# 定位到登录按钮,并点击
driver.find_element(By.CSS_SELECTOR,"#s-top-loginbtn").click()
sleep(3)
# 定位到手机号码输入框,并输入
driver.find_element(By.CSS_SELECTOR,".pass-text-input-userName").send_keys("15111111111")
# 找到所有的输入框,并统计数量
allinputs = driver.find_elements(By.CSS_SELECTOR, "input")
print(f"输入框的数量:{len(allinputs)}")
# 找到密码输入框,并输入密码
driver.find_element(By.CSS_SELECTOR,"#TANGRAM__PSP_11__form input[name='password']").send_keys("79&abe$")
# 等待3秒
sleep(3)
# 关闭浏览器
driver.quit()
if __name__ == "__main__":
test_baidu()
运行程序,查看效果。

6) 子元素选择器
原理: 通过> 符号实现定位一个元素的直接子元素
语法示例:
python
# 选择ul元素下所有直接的li子元素
driver.find_element(By.CSS_SELECTOR,"ul>li")
# 选择 id="csdn-toolbar"元素的直接子元素中id="search_box"的元素
driver.find_element(By.CSS_SELECTOR,"#csdn-toolbar>#search_box")
案例实操:
本节的实操案例我们再回到前面登录按钮的定位,尝试修改之前的定位方式,换成利用子元素选择器进行登录按钮的定位。需要注意的是实际项目中对于同一个元素的定位有很多种定位方式,结合自己的习惯选择合理的定位方式即可。

我们还是先查看一下登录按钮的属性,我们发现name属性为tj_login,我们想通过这个name属性进行定位,但是通过搜索发现name="tj_login"的元素有两个,所以不满足唯一性,但是这两个节点的父节点属性却不同,如下:

因为我们要找的第二个tj_login,所以我们修改代码中登录按钮的定位,如下:
python
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
def test_baidu():
# 初始化浏览器驱动(chrome浏览器)
driver = webdriver.Chrome()
# 打开百度
driver.get("https://www.baidu.com/")
# 定位到登录按钮,并点击
driver.find_element(By.CSS_SELECTOR,"#u1>[name='tj_login']").click()
sleep(3)
# 定位到手机号码输入框,并输入
driver.find_element(By.CSS_SELECTOR,".pass-text-input-userName").send_keys("15111111111")
# 找到所有的输入框,并统计数量
allinputs = driver.find_elements(By.CSS_SELECTOR, "input")
print(f"输入框的数量:{len(allinputs)}")
# 找到密码输入框,并输入密码
driver.find_element(By.CSS_SELECTOR,"#TANGRAM__PSP_11__form input[name='password']").send_keys("79&abe$")
# 等待3秒
sleep(3)
# 关闭浏览器
driver.quit()
if __name__ == "__main__":
test_baidu()
运行测试效果,依然可以定位到登录按钮并完成点击。

7)相邻兄弟选择器
原理: 通过+ 符号实现紧接在一个元素之后的第一个兄弟元素
语法示例:
python
# 选择紧跟在h2元素之后的第一个p元素
driver.find_element(By.CSS_SELECTOR,"h2+p")
案例实操:
这个案例我们采用相邻兄弟选择器来进行短信登录标签的定位并模拟点击,需要注意本实操案例只是为了讲解兄弟选择器的使用,这个案例中的短信登录标签实际可以直接通过Id来定位。本案例的思路是先定位到账户登录,然后找到其相邻的span元素,即为短信登录。

基于以上的思路,代码实现如下:
python
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
def test_baidu():
# 初始化浏览器驱动(chrome浏览器)
driver = webdriver.Chrome()
# 打开百度
driver.get("https://www.baidu.com/")
# 定位到登录按钮,并点击
driver.find_element(By.CSS_SELECTOR,"#u1>[name='tj_login']").click()
sleep(3)
# 定位到手机号码输入框,并输入
driver.find_element(By.CSS_SELECTOR,".pass-text-input-userName").send_keys("15111111111")
# 找到所有的输入框,并统计数量
allinputs = driver.find_elements(By.CSS_SELECTOR, "input")
print(f"输入框的数量:{len(allinputs)}")
# 找到密码输入框,并输入密码
driver.find_element(By.CSS_SELECTOR,"#TANGRAM__PSP_11__form input[name='password']").send_keys("79&abe$")
# 切换登录方式
driver.find_element(By.CSS_SELECTOR,"#TANGRAM__PSP_11__changePwdCodeItem+span").click()
# 等待3秒
sleep(3)
# 关闭浏览器
driver.quit()
if __name__ == "__main__":
test_baidu()
运行效果如下,实现了短信登录的切换。

8)通用兄弟选择器
原理: 通过~ 符号实现选择一个元素之后的所有兄弟元素
语法示例:
python
# 选择在h2元素之后的所有兄弟p元素
driver.find_element(By.CSS_SELECTOR,"h2~p")
案例实操:
该案例,我们定位到扫码登录,然后查找另外的登录方式还有几种,即查找扫描登录元素的所有兄弟span元素,并打印出数量。

代码实现逻辑如下:
python
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
def test_baidu():
# 初始化浏览器驱动(chrome浏览器)
driver = webdriver.Chrome()
# 打开百度
driver.get("https://www.baidu.com/")
# 定位到登录按钮,并点击
driver.find_element(By.CSS_SELECTOR,"#u1>[name='tj_login']").click()
sleep(3)
# 定位到手机号码输入框,并输入
driver.find_element(By.CSS_SELECTOR,".pass-text-input-userName").send_keys("15111111111")
# 找到所有的输入框,并统计数量
allinputs = driver.find_elements(By.CSS_SELECTOR, "input")
print(f"输入框的数量:{len(allinputs)}")
# 找到密码输入框,并输入密码
driver.find_element(By.CSS_SELECTOR,"#TANGRAM__PSP_11__form input[name='password']").send_keys("79&abe$")
# 切换登录方式
driver.find_element(By.CSS_SELECTOR,"#TANGRAM__PSP_11__changePwdCodeItem+span").click()
# 获取除了扫码登录之外,其余的登录方式
login_types = driver.find_elements(By.CSS_SELECTOR,"#TANGRAM__PSP_11__changeQrCodeItem~span")
print(f"除了扫码登录之外的登录方式数量:{len(login_types)}")
# 等待3秒
sleep(3)
# 关闭浏览器
driver.quit()
if __name__ == "__main__":
test_baidu()
测试运行效果,如下:

本章小结
除了以上介绍的内容,更多的CSS 选择器可以参考官方文档的介绍。CSS Selector 就像 "定位界的瑞士军刀",通过 ID、Class、属性、层级的灵活组合,几乎能搞定所有复杂元素!核心是掌握 "属性组合" 和 "模糊匹配",这两个技巧能解决 80% 的实战问题~ 大家课后可以拿自己常用的网站练手,比如定位电商页的商品、论坛的评论框,多写多试就能熟练掌握!
下一节课咱们要讲另一个 "复杂定位神器"------XPath,它的功能更强大,甚至能直接定位文本内容,敬请期待哦~ 如果你在 CSS Selector 练习中遇到具体问题,比如某个元素定位失败,欢迎随时留言交流!