Selenium

hkt 发布于 2025-08-15 27 次阅读


前言

Selenium 是一个用于Web应用程序测试的工具(是一款免费的、开源的、基于web页面的UI自动化测试工具)。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7, 8, 9, 10, 11),Firefox,Safari,Google Chrome,Opera等。这个工具的主要功能包括:测试与浏览器的兼容性——测试你的应用程序看是否能够很好得工作在不同浏览器和操作系统之上。测试系统功能——创建回归测试检验软件功能和用户需求是否一致。支持自动录制动作和自动生成 .Net、Java、Perl、Python等不同语言的测试脚本。它提供了一套完善的测试函数,功能非常灵活,能够完成界面元素的定位、窗口的跳转、结果的比较(引入unitest进行断言)等

官网和发展

  • 官网: https://www.selenium.dev
  • 支持的语言:Python、Java、Ruby、PHP等等
  • selenium1.0:selenium IDE(录制和回放脚本)、selenium Grid(多浏览器运行) 、selenium Remote Control(RC)(核心,主要是用来跟浏览器做交互)
  • selenium2.0 = selenium1.0 + WebDriver
  • selenium3.0是在selenium2.0的基础上改进,去掉了RC,全面拥抱了webdriver,从此webdriver一统江湖

Selenium的核心组件

组件说明
Selenium WebDriver核心驱动,直接控制浏览器进行自动化操作(如点击、输入、获取元素)
Selenium IDE浏览器插件,支持录制和回放测试用例(适合简单脚本生成)
Selenium Grid分布式测试,可在多台机器上并行运行测试用例
Selenium Client Libraries不同语言的客户端库(如 Python 的 selenium 包)

Selenium原理

Selenium 主要由 客户端库(Client Libraries) 和 浏览器驱动(WebDriver) 组成,并通过 JSON Wire Protocol(旧版) 或 W3C WebDriver 协议(新版) 进行通信。客户端使用python的第三方模块Selenium来进行发送http请求,Browser Drivers:作为中间代理,接收客户端请求翻译并以指令的方式转发给浏览器。例如,Chrome → chromedriver

关键流程(以chorme为例):

  1. 测试脚本调用 webdriver.Chrome()
  2. Python 客户端库向 chromedriver(默认端口 9515)发送 HTTP 请求:
  3. chromedriver 启动 Chrome 浏览器实例,并返回一个 会话 ID(Session ID)。
  4. 脚本调用 driver.get("https://www.baidu.com")。客户端发送请求:
  5. 脚本调用 driver.find_element("id", "kw")。客户端发送 HTTP 请求:

元素定位

元素定位就是根据元素的某个特征在网页中找到对应的元素,定位元素的目的是为了操作元素,在Web自动化中,定位元素是后续一切操作的前提条件。在selenium中有8大基本方法定位元素

find_element(By.ID,属性值)
find_element(By.NAME,属性值)
find_element(By.CLASS_NAME,属性值)
find_element(By.TAG_NAME,属性值)
find_element(By.LINK_TEXT,属性值)
find_element(By.PARTIAL_LINK_TEXT,属性值)
find_element(By.XPATH,属性值)
find_element(By.CSS_SELECTOR,属性值)

以上8个方法,会根据传入的条件,在整个页面中查找对应的元素,找到第一个元素(对应元素)后就返回,不再往下面继续找。所以在指定条件时要准确(确保唯一性),否则会定位不到元素。

根据id定位元素

方法名:find_element(By.ID,"属性值")

作用:通过元素的id值来定位元素,一般情况下元素的id是唯一的,所以通过这个方法可以很轻松地找到元素。find_element(By.ID)方法的返回值是一个WebElement对象,这个对象对应的就是Web页面上对应的元素。

有些特性情况,不能通过id来定位元素,比如:

  • 有的元素的id属性是动态变化的,动态id属性值一般由固定部分+动态部分组成,一般情况下元素的id是一个有具体含义的,看到带数字的id就要怀疑可能是动态的。针对这种情况那就换一种定位方式 动态id举例:163邮箱登录页面的输入框的id,https://mail.163.com/
  • 有些Web元素也可能没有id,这种情况就要换成其它方法来定位。
  • 有可能id的属性值不唯一(重复)

根据name定位元素

方法名:find_element(By.NAME,'属性值')

作用:通过元素的名称来定位元素,一般情况下元素的名称也是唯一的,所以通过名称可以找到元素。find_element(By.NMAE,属性值)方法的返回值是一个WebElement对象。

根据class定位元素

方法:find_element(By.CLASS_NAME,"属性值")

class 属性表示元素的类名,指向样式表中的类,一个元素可以有一个或多个类名,一个类名也可能对应多个元素。当元素有多个类名时,类名与类名之间用间隔符隔开(但是定位时需要使用点号代替间隔符)。

作用:根据元素的类名定位元素,需要注意的是作为定位条件的类名必须是唯一的,否则会定位不到想找的元素,当元素有多个类名时, 选择其中一个唯一的类名来定位。find_element(By.CLASS_NAME,属性值)方法的返回值是一个WebElement对象。

根据tag_name定位元素

方法:find_element(By.TAG_NAME,属性值)

作用:根据元素的标签名称来定位元素,一般用来定位一组元素,因为一个页面上往往有多个同名的标签,所以这种方法的局限性较大。find_element(By.TAG_NAME,属性值)方法的返回值是一个WebElement对象。

根据link_text定位元素

方法:find_element(By.LINK_TEXT,属性值)

作用:根据元素的链接文本来定位元素,适用于超链接元素,超链接元素的标签是a,在一个页面上,超链接文本一般是唯一的。find_element_by_link_text方法的返回值是一个WebElement对象。

根据partial_link_text定位元素(模糊文本定位)

方法:driver.find_element(By.PARTIAL_LINK_NAME,"部分链接文本")

作用:根据元素的链接文本的一部分来定位元素,要注意选取的部分文本要唯一,否则定位不到需要找的元素。driver.find_element(By.PARTIAL_LINK_NAME,"部分链接文本")方法的返回值是一个WebElement对象。

根据xpath定位元素

XPath 是一门在 XML文档中查找信息的语言,XPath 使用路径表达式来选取XML文档中的节点或节点集。我们可有通过XPath路径表达式在HTML中定位元素。**通俗一点讲就是通过元素的路径来查找到这个标签元素
格式:与Linux的绝对路径相似,XPath绝对路径以/(根目录)开始写,第一层是/html,依次往下写,直到要定位的标签,比如/html/body/form/div/div/div/div/input

注意:当父标签下有多个相同的子标签,可通过索引进行选择,索引值是从1开始,与python中的索引值从0开始不一样。

Xpath表达式

  • /: 子节点
  • //:任意节点
  • @: 标签当前节点对象
  • *:通配符
  • []: 过滤器

相对路径+属性

格式://标签名[@属性名=属性值];其中,//表示当前页面的任意目录下,因为同一个页面,往往同名的标签很多,所以经常把相对路径+属性组合起来使用

举例:

#定位百度首页输入框,输入Python
driver.find_element(By.XPATH,'//input[@id="kw"]').send_keys("Python")
#定位百度首页输入框,输入Selenium
driver.find_element(By.XPATH,'//input[@name="wd"]').send_keys("Selenium")
#定位百度一下按钮,点击
driver.find_element(By.XPATH,'//input[@id="su"]').click()

相对路径+层级

格式:绝对路径是完全依靠层级定位,相对路径是标签+属性定位,我们也可以把路径+属性+层级组合来定位,比如说有的元素它本身不好定位,但它的上一级或者上上一级定位比较方便,这种情况下就可以通过先定位上一级元素再通过上一级来定位你想定位的元素。

举例:

#定位百度输入框,输入Python
driver.find_element(By.XPATH,"//span[@id='s_kw_wrap']/input[1]").send_keys("Python")
#定位百度一下按钮,点击
driver.find_element(By.XPATH,"//form[@id='form']/span[2]/input[1]").click()

使用逻辑运算符

格式://标签名[@属性名1=属性值1 and @属性名2=属性值2];如果通过一个属性不能准确地定位到元素,可以使用逻辑运算符and来指定多个属性定位元素。

举例:

#定位百度输入框,输入Python
driver.find_element(By.XPATH,"//input[@id='kw' and @name='wd']").send_keys("Python")
#定位百度一下按钮,点击
driver.find_element(By.XPATH,"//input[@id='su' and @value='百度一下']").click()

文本内容

格式://标签名[text()=文本],如果一个标签有文本,并且文本在页面中是唯一的,可以通过指定文本来定位元素,比如//a[text()="新闻"]

举例:

#定位文本是新闻的标签
driver.find_element_by_xpath("//a[text()='新闻']").click()

模糊定位

格式:

- `//标签名[contains(@属性名,属性对应的部分值)]`
- `//标签名[starts-with(@属性名,属性对应的部分值)]`
- `//标签名[ends-with(@属性名,属性对应的部分值)]————已被移除`

注意:分析属性值是否是动态的,是否由动态+固定值所构成的;如果分析出动态值中的部分固定值则可以使用contains完成。ends-with是xml2.0的语法,有的浏览器不支持。

举例://input[contains(@id,'kw')]`

通过浏览器生成XPath表达式

可以在浏览器开发者工具界面,选择元素,右键选择Copy,选择Copy XPath来自动生成XPath路径表达式。这可以作为一种辅助手段,有时复制出来的可能也不对,所以最终必须掌握手写XPath表达式的能力。

通过CSS选择器定位

CSS选择器表达式可以实现对HTML页面中的元素实现一对一、一对多或者多对一的控制。
方法:find_element(By.CSS_SELECTOR,属性值)

css样式表达式

  • #id:以id属性值定位
  • .class.class:以class属性值定位
  • 标签名:以标签名称定位
  • 标签名#id.class.class:以标签名、id属性及class属性值定位
  • [属性名="值"]:根据属性的值进行定位
  • [属性名1="值1"][属性名2="值2"]:根据多个属性的值进行定位
  • 标签名[属性名="值"]:根据标签名和属性的值进行定位
  • [属性名^="值"]: 根据属性的开始值定位
  • [属性名$="值"]: 根据属性的结束值定位
  • [属性名*="值"]: 根据属性的包含值定位
  • >:子标签
    • > 子标签名:first-child: 定位第一个子标签
    • > 子标签名:last-child: 定位最后一个子标签,注意标签名一定要给正确
    • > 子标签:nth-child(n): 定位第n个子标签。
  • ~/+: 根据当前标签定位兄弟标签。

通过CSS id属性值定位

语法:#代表id,比如#kw
举例:

#定位百度一下输入框,输入Python
driver.find_element(By.CSS_SELECTOR, "#kw").send_keys("python") 

通过CSS class属性值定位

语法:.类名,如果一个元素有多个类名,可以用.类名1.类名2.类名n来定位,也可以选择其中一个唯一的类名来定位
举例:

#定位百度输入框,输入Python
driver.find_element(By.CSS_SELECTOR, ".s_ipt").send_keys("python")
#定位百度一下按钮,这个元素有多个class值,可以指定多个类名定位,也可以选择其中一个唯一的类名来定位
driver.find_element(By.CSS_SELECTOR, ".bg.s_btn").click()
driver.find_element(By.CSS_SELECTOR, ".s_btn").click()

通过其它属性值定位

语法:[属性名=属性值]
举例:

#定位百度输入框,输入Python
driver.find_element(By.CSS_SELECTOR, "[name='wd']").send_keys("python")

通过标签名定位

语法:直接写标签名,前面不需要加任何符号(会存在很多同名的标签,不建议单独使用,一般结合其他属性进行定位)
举例:

#定位百度输入框,输入Python
driver.find_element(By.CSS_SELECTOR, "input").send_keys("python")

通过标签名+id组合定位

语法:标签名#id属性值
举例:

#定位百度输入框,输入Python
driver.find_element(By.CSS_SELECTOR, "input#kw").send_keys("python")

通过标签名+class组合定位

语法:标签名.类名
举例:

#定位百度输入框,输入Python
driver.find_element(By.CSS_SELECTOR, "input.s_ipt").send_keys("python")

使用标签名+其它属性组合定位

语法:标签名[属性名=属性值],id和class也可以用这种形式写
举例:

#定位百度输入框,输入Python
driver.find_element(By.CSS_SELECTOR, "input[name='wd']").send_keys("python")

使用多个属性组合定位

语法:标签名[属性名1=属性值1][属性名2=属性值2];如果通过一个属性不好准确定位到元素,也可以通过多个属性值定位;
举例:

#定位百度输入框,输入Python
driver.find_element(By.CSS_SELECTOR, "input[name='wd']").send_keys("python")

通过页面元素的属性值的一部分定位

语法:

  • [属性名^='XX']:表示以XX开头
  • [属性名$='XX']:表示以XX结尾
  • [属性名*='XX']:表示包含XX;

举例:

#定位百度一下按钮,点击
driver.find_element(By.CSS_SELECTOR, "input[value*='百度']").click()
driver.find_element(By.CSS_SELECTOR, "input[value^='百度']").click()
driver.find_element(By.CSS_SELECTOR, "input[value$='一下']").click()

通过页面元素定位子元素

语法:如果直接定位某个元素不好定位,可以尝试先找它的上级元素或上上级元素,再找这个元素,在CSS中用>表示层级。css的绝对路径是从html标签开始的,比如html>body>form>div>div>div>div>input,这种方式类似于XPath的绝对路径,在实际使用中不可取。

表示某个元素下的第x个子元素的方法:
nth-child(x):A>B:nth-child(x)——表示A标签下面的所有子标签中的第x个并且标签名是B的子元素,可以用,第一个也可以用first-child表示,最后一个也可以用last-child表示

举例:

#定位百度输入框,输入Python
#使用nth-child(x)
driver.find_element(By.CSS_SELECTOR, "span#s_kw_wrap>input:nth-child(2)").send_keys("Python")
#使用nth-of-type(x)
driver.find_element(By.CSS_SELECTOR, "span#s_kw_wrap>input:nth-of-type(1)").send_keys("Python")

通过页面元素定位后代元素

语法:A B:表示B是A的后代元素,后代包含儿子、孙子、曾孙等。这种方式往往再结合属性来定位B元素。
举例:

#定位百度输入框,输入Python,表达式form#form input[type='text']表示找id等于form的form标签的后代元素中type等于text的input标签
driver.find_element(By.CSS_SELECTOR, "form#form input[type='text']").send_keys("Python")

通过页面元素定位同级兄弟元素

语法:

  • #hotsearch-content-wrapper>li:nth-of-type(2表达式1:element+element,比如div#div1 > input + a,表示定位与id为div1的div标签的子元素input同级的后面紧挨着的元素a——哥哥定位弟弟
  • 表达式2:element1~element2,比如div#div1 > input ~ a,表示定位与id为div1的div标签的子元素input同级的后面所有元素a,若满足条件的元素不止一个,需要使用find_elements_by_css_selector()方法——哥哥定位弟弟们

举例:

#定位百度输入框,输入Python,表达式form#form span.soutu-btn+input表示找id等于form的form标签的后代元素中class等于soutu-btn的span标签后紧邻的input标签
driver.find_element(By.CSS_SELECTOR, "form#form span.soutu-btn+input").send_keys("Python")

XPath表达式和CSS选择器的语法对比

功能xpathcss选择器
id定位//*[@id=值1]#id
css//*[@class=值].class1.class2.class3
子节点/>
根据标签名定位//标签名标签名
根据属性定位//标签名[@属性名1=值1 and @属性名2=值2]标签名[属性名1=值1][属性名2=值2]
定位子节点的第几个//标签名[@属性名1=值1]/标签名[n]标签名[属性名1=值1]>标签名:first-child
标签名[属性名1=值1]>标签名:last-child
标签名[属性名1=值1]>标签名:nth-child(n)
模糊匹配//标签名[contains(属性名,值)]
//标签名[starts-with(属性名, 值)]
//标签名[ends-with(属性名, 值)]
标签名[属性名*=值]
标签名[属性名^=值]
标签名[属性名$=值]

八大定位方法的选择

  1. 如果有固定且唯一的属性(id/name/class等),直接通过属性定位;
  2. 如果是超链接,优先选择link_text()方法;
  3. 如果上面几种简单的方法解决不了,选择By.CSS_SELECTOR方法,CSS的功能强大、速度快、语法稍微复杂;
  4. 如果以上方法都解决不了,选择By.XPATH方法,通常XPath定位的速度会比CSS慢(特别是在IE上);

浏览器对象和元素对象的操作

    浏览器对象的操作:
get("url"): 访问url
find_element(By.定位方式, "定位的值"):查询网页中的元素,返回值类型为WebElement类型
find_elements(By.定位方式, "定位的值"): 查询网页中有相同定位值的一组元素,返回值为列表,列表中的元素为WebElement类型
maximize_window(): 浏览器窗口最大化
minimize_window(): 浏览器窗口最小化
get_window_size(): 获取浏览器的窗口大小

back(): 后退
forward(): 前进
refresh(): 刷新

close(): 关闭网页
quit(): 退出浏览器

title:获取网页的title
current_url: 获取网页的url

page_source: 获取网页的全部数据,返回值类型为字符串
get_screenshot_as_file("png图片路径"):网页截图
元素对象(WebElement类型)的操作:
send_keys("值"):向可输入的元素发送一个值
click(): 点击元素
text: 获取元素的文本
get_attribute("属性名"):获取元素的属性值
find_element(By.定位方式, "定位的值"):查询元素中的元素,返回值类型为WebElement类型
find_elements(By.定位方式, "定位的值"): 查询元素中有相同定位值的一组元素,返回值为列表,列表中的元素为WebElement类型
clear(): 清空元素的文本。

元素定位不到的原因

  1. 网页中是否有iframe标签。iframe标签是在网页中再嵌套网页。
    • 进入嵌套的网页:浏览器对象.switch_to.frame("iframe标签的id/iframe标签的WebElement类型")
    • 回到父级页面: 浏览器对象.switch_to.parent_frame()
    • 回到主页面: 浏览器对象.switch_to.default_content()
  2. 元素已加载,但是未显示。将鼠标移动到元素的周围,让元素显示出来再定位。
    • 创建鼠标对象:action = ActionChains(浏览器对象)
    • 鼠标对象的常用操作:
      • move_to_element(WebElement类型):将鼠标移动到指定的元素位置
      • move_by_offset(x, y):将鼠标移动到指定的坐标位置
      • click(): 点击鼠标左键
      • perform(): 释放鼠标存储的动作
      • reset_actions(): 重置鼠标的事件。
    • Keys: 为selenium的键盘操作。
  3. 元素未加载也未显示, 操作浏览器的滚动条,滚动到元素周围,让元素加载出来,再定位做操作。
    • js代码1:window.scrollTo(x, y)
      • 浏览器对象.execute_script("js代码"): 整个网页滚动
    • js代码2:arguments[0].scrollIntoView()
      • 浏览器对象.execute_script("js代码", 元素的WebElement类型):滚动到指定元素位置
    • 键盘的滚动:
      • Keys.DOWN/Keys.UP/Keys.PAGE_UP
  4. 是否有窗口(句柄)的切换
    • 获取浏览器所有野望的句柄:浏览器对象.window_handles
    • 获取当前浏览器对象操作网页的句柄:浏览器对象.current_window_handle
    • 如果要操作其他网页,需要进行句柄切换:浏览器对象.switch_to.window(“句柄值”)
  5. 是否有alert弹窗
    • alert,confirm,prompt弹窗
    • 点击弹窗确定:浏览器对象.switch_to.alert.accept()
    • 点击弹窗取消:浏览器对象.switch_to.alert.dismiss()
    • 获取弹窗的文本:浏览器.switch_to.alert.text
    • 向弹窗发送值:浏览器对象.switch_to.alert.send_keys("值")
  6. 其他元素的处理。
    • 上传文件:如果上传文件的元素为input元素,类型为file,直接使用send_keys发送一个文件路径,就会实现文件的上传。
    • 下拉框的处理:
      • Select(WebElement).select_by_visible_text('可见文本')
        Select(WebElement).select_by_index(option的索引值')
        Select(WebElement).select_by_value('option的value')

上传文件举例:

driver.find_element(By.CSS_SELECTOR, '[type="file"]').send_keys(r"D:\Project\test68\2025-8-04_selenium\code\day3\1.png")  #注意:只能支持input标签下上传文集。

网页嵌套举例:

driver = webdriver.Chrome(service=webdriver.ChromeService("../chromedriver.exe"))
driver.maximize_window()

driver.get("http://47.109.99.224:10198/korei/login.jsp")

driver.find_element(By.NAME, "username").send_keys("admin")
driver.find_element(By.NAME, "password").send_keys("1")
driver.find_element(By.TAG_NAME, 'a').click()
time.sleep(3)
driver.find_element(By.XPATH, '//*[text()="系统管理"]').click()
time.sleep(1)
driver.find_element(By.XPATH, '//*[text()="门店管理"]').click()
time.sleep(2)

data = Faker(locale="zh_cn")
# 进入嵌套的网页
for i in range(10):
driver.switch_to.frame("mdgl")
driver.find_element(By.XPATH, '//*[text()="添加"]').click()
time.sleep(2)
element = driver.find_element(By.CSS_SELECTOR, '[id^="ligerwindow"]')
# 再次进入嵌套的网页
driver.switch_to.frame(element)

driver.find_element(By.ID, 'name').send_keys(data.name()+"的五金店")
driver.find_element(By.ID, 'address').send_keys(data.address())
driver.find_element(By.ID, 'phone').send_keys(data.phone_number())
# 回到父级页面中
driver.switch_to.parent_frame()
driver.find_element(By.XPATH, '//*[text()="确定"]').click()
time.sleep(2)
# 再次进入嵌套的网页
driver.switch_to.frame(element)
driver.find_element(By.XPATH, '//*[text()="确定"]').click()
# 回到主页面
driver.switch_to.default_content()

selenium三种等待方式

  • 强制等待:time.sleep(n)
  • 隐式等待: 全局的等待时间,implicitly_wait(n),为定位每个元素设置的一个相同的等待时间,如果在等待时间内,能够定位到元素,不报错,如果超过等待时间,没有定位到元素就会报错。
  • 显式等待(explict_wait): 为每个元素设置独立的等待时间,或等待事件(元素可点击时/元素出现在页面上时)才定位。

注意:隐式等待和显式等待不要同时使用。隐式等待和显式等待都可以和强制等待相互结合使用。

WebDriverWait 的作用

  • 主要用途:显式等待元素达到可操作状态(存在、可见、可点击等),而非直接执行操作
  • 底层机制:通过轮询检查 ExpectedConditions,直到条件满足或超时。

使用Debug模式启动浏览器

Debug模式相当于独立启动一个浏览器来进行操作,会将浏览器的数据保存到单独的文件中,方便cookie的查看,加快页面的加载。

# 创建启动浏览器的Options对象
 options = webdriver.ChromeOptions()
# # 指明debug模式浏览器的启动程序
 options.binary_location = r"C:\Program Files\Google\Chrome\Application\chrome.exe"
# # 添加链接debug模式浏览器的实验选项
 options.add_experimental_option("debuggerAddress", "127.0.0.1:6789")
# # 创建浏览器的对象
 driver = webdriver.Chrome(service=webdriver.ChromeService("../chromedriver.exe"), options=options)

在启动debug模式时先关闭服务

res = os.popen(r'netstat -ano | findstr "6789"')
lines = res.readlines()
for line in lines:
    if "0.0.0.0:0" in line:
        pid = line.split(" ")[-1].strip()
        # taskkill /pid {pid}
        os.popen(f"taskkill /pid {pid}")
        break
此作者没有提供个人介绍。
最后更新于 2025-08-15