本节介绍如何获取元素对象。 定位元素是自动化最重要的的技能,虽然可在开发者工具直接复制绝对路径,但这样做一来代码冗长,可读性低,二来难以应付动态变化的页面。 因此本库提供一套简洁易用的语法,用于快速定位元素,并且内置等待功能、支持链式查找,减少了代码的复杂性。 定位元素大致分为三种方法: - 在页面或元素内查找子元素 - 根据 DOM 结构相对定位 - 根据页面布局位置相对定位 # ✔️ 示例 先看一些示例,后面在详细讲解用法。 ## 📍 简单示例 假设有这样一个页面,本节示例皆使用此页面: ```html

第一行

第二行

第三行

第二个div
``` 我们可以用页面对象去获取其中的元素: ```python # 获取 id 为 one 的元素 div1 = page.ele('#one') # 获取 name 属性为 row1 的元素 p1 = page.ele('@name=row1') # 获取包含“第二个div”文本的元素 div2 = page.ele('第二个div') # 获取所有div元素 div_list = page.eles('tag:div') ``` 也可以获取到一个元素,然后在它里面或周围查找元素: ```python # 获取到一个元素div1 div1 = page.ele('#one') # 在div1内查找所有p元素 p_list = div1.eles('tag:p') # 获取div1后面一个元素 div2 = div1.next() ``` --- ## 📍 实际示例 复制此代码可直接运行查看结果。 ```python from DrissionPage import SessionPage page = SessionPage() page.get('https://gitee.com/explore') # 获取包含“全部推荐项目”文本的 ul 元素 ul_ele = page.ele('tag:ul@@text():全部推荐项目') # 获取该 ul 元素下所有 a 元素 titles = ul_ele.eles('tag:a') # 遍历列表,打印每个 a 元素的文本 for i in titles: print(i.text) ``` **输出:** ```console 全部推荐项目 前沿技术 智能硬件 IOT/物联网/边缘计算 车载应用 ... ``` --- # ✔️ 查找元素方法 ## 📍 查找单个元素 🔸 `ele()` 页面对象和元素对象都拥有此方法,用于查找第一个匹配条件的元素。 页面对象和元素对象的`ele()`方法参数名称稍有不同,但用法一样。 ?>**Tips:**
使用 xpath 可直接获取后代元素的属性。 | 参数名称 | 类型 | 默认值 | 说明 | |:------------------:|:----------------------------------------------:|:------:| -------------------------------------------- | | `loc_or_str`(元素对象) | `str`
`Tuple[str, str]` | 必填 | 元素的定位信息,可以是查询字符串,或 loc 元组 | | `loc_or_ele`(页面对象) | `str`
`SessionElement`
`Tuple[str, str]` | 必填 | 元素的定位信息,可以是查询字符串、loc 元组或一个`SessionElement`对象 | | `timeout` | `int`
`float` | `None` | 不起实际作用,用于和`ChromiumElement`对应,便于无差别调用 | | 返回类型 | 说明 | |:----------------:| --------------------- | | `SessionElement` | 返回查找到的第一个符合条件的元素对象 | | `None` | 未找到符合条件的元素时返回`None` | | `str` | 使用 xpath,可直接获取后代元素的属性 | **示例:** ```python from DrissionPage import SessionPage page = SessionPage() # 在页面内查找元素 ele1 = page.ele('#one') # 在元素内查找后代元素 ele2 = ele1.ele('第二行') # 使用 xpath 获取后代中第一个 div 元素的 class 属性(元素内查找可用) ele_class = ele1.ele('xpath://p/@class') ``` --- ## 📍 查找多个元素 🔸 `eles()` 此方法与`ele()`相似,但返回的是匹配到的所有元素组成的列表,用 xpath 获取元素属性时,返回属性文本组成的列表。 | 参数名称 | 类型 | 默认值 | 说明 | |:------------:|:--------------------------:|:------:| ------------------------------------- | | `loc_or_str` | `str`
`Tuple[str, str]` | 必填 | 元素的定位信息,可以是查询字符串,或 loc 元组 | | `timeout` | `int`
`float` | `None` | 不起实际作用,用于和`ChromiumElement`对应,便于无差别调用 | | 返回类型 | 说明 | |:----------------------:| ---------------------------- | | `List[SessionElement]` | 返回找到的所有`SessionElement`组成的列表 | | `List[str]` | 使用 xpath,可直接获取后代元素的属性 | **示例:** ```python # 获取ele元素内的所有p元素 p_eles = ele.eles('tag:p') # 打印第一个p元素的文本 print(p_eles[0]) ``` --- # ✔️ 查找语法 我们使用一套简洁高效的语法去定位元素,大大简化了定位元素的代码量,增强了功能,也兼容 css selector、xpath、selenium 原生的 loc 元组。 🔸 **匹配模式** 指字符串是否完全匹配,有以下两种: ## 📍 精确匹配符 `=` 表示精确匹配,匹配完全符合的文本或属性。 --- ## 📍 模糊匹配符 `:` 表示模糊匹配,匹配含有某个字符串的文本或属性。 --- 🔸 **关键字** 是出现在定位语句最左边,用于指明该语句以哪种方式去查找元素,有以下这些: ## 📍 id 匹配符 `#` 表示`id`属性,只在语句最前面且单独使用时生效,可配合`=`或`:`。 ```python # 在页面中查找id属性为one的元素 ele1 = page.ele('#one') # 在ele1元素内查找id属性包含ne文本的元素 ele2 = ele1.ele('#:ne') ``` --- ## 📍 class 匹配符 `.` 表示`class`属性,只在语句最前面且单独使用时生效,可配合`=`或`:`。 ```python # 查找class属性为p_cls的元素 ele2 = ele1.ele('.p_cls') # 查找class属性包含_cls文本的元素 ele2 = ele1.ele('.:_cls') ``` --- ## 📍 单属性匹配符 `@` 表示某个属性,只匹配一个属性。 `@`关键字只有一个简单功能,就是匹配`@`后面的内容,不再对后面的字符串进行解析。因此即使后面的字符串也存在`@`或`@@`,也作为要匹配的内容对待。 !> **注意:** 如果属性中包含特殊字符,如包含`@`,用这个方式不能正确匹配到,须使用 css selector 方式查找。且特殊字符要用`\`转义。 ```python # 查找name属性为row1的元素 ele2 = ele1.ele('@name=row1') # 查找name属性包含row文本的元素 ele2 = ele1.ele('@name:row') # 查找有name属性的元素 ele2 = ele1.ele('@name') # 查找没有任何属性的元素 ele2 = ele1.ele('@') # 查找email属性为abc@def.com的元素,有多个@也不会重复处理 ele2 = ele1.ele('@email=abc@def.com') # 属性中有特殊字符的情形,匹配abc@def属性等于v的元素 ele2 = ele1.ele('css:div[abc\@def="v"]') ``` --- ## 📍 多属性匹配符 `@@` 多属性匹配时使用,个数不限。还能匹配要忽略的元素,匹配文本时也和`@`不一样。 `@@`后跟 - 时,表示 not。如: - `@@-name`表示匹配没有`name`属性的元素 - `@@-name=ele_name`表示匹配`name`属性不为`ele_name`的元素 如有以下情况,不能使用此方式,须改用 xpath 的方式: - 匹配文本或属性中出现`@@` - 属性名本身以`-`开头 !> **注意:**:
如果属性中包含特殊字符,如包含`@`,用这个方式不能正确匹配到,须使用 css selector 方式查找。且特殊字符要用`\`转义。 ```python # 查找name属性为name且class属性包含cls文本的元素 ele2 = ele1.ele('@@name=row1@@class:cls') # 查找没有class属性的元素 ele2 = ele1.ele('@@-class') # 查找name属性不包含row1的元素 ele2 = ele1.ele('@@-name:row1') ``` --- ## 📍 文本匹配符 `text` 要匹配的文本,查询字符串如开头没有任何关键字,也表示根据传入的文本作模糊查找。 如果元素内有多个直接的文本节点,精确查找时可匹配所有文本节点拼成的字符串,模糊查找时可匹配每个文本节点。 没有任何匹配符时,默认匹配文本。 ```python # 查找文本为“第二行”的元素 ele2 = ele1.ele('text=第二行') # 查找文本包含“第二”的元素 ele2 = ele1.ele('text:第二') # 与上一行一致 ele2 = ele1.ele('第二') ``` ?> **Tips:**
若要查找的文本包含`text:` ,可下面这样写,即第一个`text:` 为关键字,第二个是要查找的内容: ```python ele2 = page.ele('text:text:') ``` --- ## 📍 文本匹配符 `text()` 作为查找属性时使用的文本关键字,必须与`@`或`@@`配合使用。 ```python # 查找文本为“第二行”的元素 ele2 = ele1.ele('@text()=第二行') # 查找文本包含“第二行”的元素 ele2 = ele1.ele('@text():二行') # 查找文本为“第二行”且class属性为p_cls的元素 ele2 = ele1.ele('@@text()=第二行@@class=p_cls') # 查找文本为 some text 且没有任何属性的元素(因第一个 @@ 后为空) ele2 = ele1.ele('@@@@text():some text') # 查找直接子文本包含 some text 字符串的元素 ele = page.ele('@text():二行') # 查找元素内部包含 some text 字符串的元素 ele = page.ele('@@text():二行') ``` 须要注意的是,`'text=xxxx'`与`'@text()=xxxx'`使用上是有细微差别的。 `text=`表示在元素的直接子文本节点中匹配,`@text()=`会忽略一些文本标签,在整个元素的内容里匹配。 --- ## 📍 类型匹配符 `tag` 表示元素的标签,只在语句最前面且单独使用时生效,可与`@`或`@@`配合使用。`tag:`与`tag=`效果一致。 ```python # 定位div元素 ele2 = ele1.ele('tag:div') # 定位class属性为p_cls的p元素 ele2 = ele1.ele('tag:p@class=p_cls') # 定位文本为"第二行"的p元素 ele2 = ele1.ele('tag:p@text()=第二行') # 定位class属性为p_cls且文本为“第二行”的p元素 ele2 = ele1.ele('tag:p@@class=p_cls@@text()=第二行') # 查找直接文本节点包含“二行”字符串的p元素 ele2 = ele1.ele('tag:p@text():二行') # 查找内部文本节点包含“二行”字符串的p元素 ele2 = ele1.ele('tag:p@@text():二行') ``` !> **注意:**
`tag:div@text():text` 和 `tag:div@@text():text` 是有区别的,前者只在`div`的直接文本节点搜索,后者搜索`div`的整个内部。 --- ## 📍 css selector 匹配符 `css` 表示用 css selector 方式查找元素。`css:`与`css=`效果一致。 ```python # 查找 div 元素 ele2 = ele1.ele('css:.div') # 查找 div 子元素元素,这个写法是本库特有,原生不支持 ele2 = ele1.ele('css:>div') ``` --- ## 📍 xpath 匹配符 `xpath` 表示用 xpath 方式查找元素。`xpath:`与`xpath=`效果一致。 在元素中查找时,该方法支持完整的 xpath 语法,能使用 xpath 直接获取元素属性,selenium 不支持这种用法。 ```python # 查找 div 元素 ele2 = ele1.ele('xpath:.//div') # 和上面一行一样,查找元素的后代时,// 前面的 . 可以省略 ele2 = ele1.ele('xpath://div') # 获取 div 元素的 class 属性,返回字符串 txt = ele1.ele('xpath://div/@class') ``` ?> **Tips:**
查找元素的后代时,selenium 原生代码要求 xpath 前面必须加`.`,否则会变成在全个页面中查找。作者觉得这个设计是画蛇添足,既然已经通过元素查找了,自然应该只查找这个元素内部的元素。所以,用 xpath 在元素下查找时,最前面`//`或`/`前面的`.`可以省略。 --- ## 📍 selenium 的 loc 元组 查找方法能直接接收 selenium 原生定位元组进行查找。 ```python from selenium.webdriver.common.by import By # 查找id为one的元素 loc1 = (By.ID, 'one') ele = page.ele(loc1) # 按 xpath 查找 loc2 = (By.XPATH, '//p[@class="p_cls"]') ele = page.ele(loc2) ``` --- # ✔️ 相对定位 以下方法可以以某元素为基准,在 DOM 中按照条件获取其兄弟元素、祖先元素、文档前后元素。 除获取元素外,还能通过 xpath 获取任意节点内容,如文本节点、注释节点。这在处理元素和文本节点混排的时候非常有用。 !>**注意:**
如果元素在`