# Selenium Locating

## 根据xxx查找元素

在浏览器上查找一处元素，使用 `webdriver.find_element_by_xxx` 的形式，具体而言有如下几种(按照自己的使用频率排序)：

### find\_element\_by\_xpath()

原称之为最强大最傻瓜式的查找方式，一招毙命，实在想不到为什么还有其他的方式存在（误\
在 chrome 中定位到想要的对象，右键 copy->copy full XPath 作为参数即可\
这里注意 copy 中有两个选择，一个是 copy XPath 一个是 copy full XPath\
由于有一些网页上可能会存在除了功能什么都一样的元素，如：一个搜索框搜索 Project，一个搜索框搜索 Owner\
这时候使用单纯的 copy XPath 可能两者是一样的，都是 xxx\_search ，这就导致会去定位第一个找到的搜索框\
所以 copy full XPth 是最保险的选择，直接返回从 html 开始的 XPath\
e.g. copy XPath->`//*[@id="rightmenu"]/ul[2]/li[2]` copy full XPath->`/html/body/div[3]/ul[2]/li[2]`

那么为什么还需要其他方式呢？自己想了两点： 1. 查找一类元素，如爬贴吧的每个分类里面的帖子，肯定就不能每次定位一个确切的 XPath 了，而是要根据 class 或者 tag 找到一个 list 然后逐个爬取。 2. 效率慢，XPath 是直接逐级往下进行查找，每次查找(估计)都要将里面的所有tag整理出来，导致进度缓慢。

### find\_element\_by\_css\_selector()

首先看懂 CSS 语句：

```markup
<div id='inner'>呵呵呵</div> : <type> 是 <div>，其 <id> 为 'inner'
<a href='http://www.baidu.com'>百度一下</a> : <type> 是 <a>，其中有个 [href] 的属性值是 'http://www.baidu.com'  
<li class='two three four'>Two</li> : <type> 是 <li>，其属于 'two' 'three' 'four' 三种 <class>
```

**基本CSS选择器**

|        选择器       |                      描述                      |     举例    |
| :--------------: | :------------------------------------------: | :-------: |
|        \*        |                 通配选择器，选择所有的元素                |     \*    |
|     `<type>`     |             选择特定类型的元素，支持基本HTML标签             |     h1    |
|    `.<class>`    |                选择具有特定class的元素。               |  .class1  |
| `<type>.<class>` | <p>特定类型和特定class的交集。<br> 直接将多个选择器连着一起表示交集</p> | h1.class1 |
|      `#<id>`     |                选择具有特定id属性值的元素                |    #id1   |

**属性选择器**

|       选择器       |                描述                |            举例           |
| :-------------: | :------------------------------: | :---------------------: |
|     \[attr]     |      选取定义attr属性的元素，即使该属性没有值      |      \[placeholder]     |
|  \[attr="val"]  |         选取attr属性等于val的元素         | \[placeholder="请输入关键词"] |
|  \[attr^="val"] |         选取attr属性开头为val的元素        |  \[placeholder^="请输入"]  |
|  \[attr$="val"] |         选取attr属性结尾为val的元素        |  \[placeholder$="关键词"]  |
| \[attr\*="val"] |         选取attr属性包含val的元素         |  \[placeholder\*="入关"]  |
| \[attr\~="val"] | 选取attr属性包含多个空格分隔的属性，其中一个等于val的元素 |  \[placeholder\~="关键词"] |

**关系选择器**

|            选择器            |                      描述                      |         举例         |
| :-----------------------: | :------------------------------------------: | :----------------: |
|  `<selector> <selector>`  |   <p>第二个选择器为第一个选择器的后代元素<br>选取第二个选择器匹配结果</p>  |     .class1 h1     |
| `<selector> > <selector>` |  <p>第二个选择器为第一个选择器的直接子元素<br>选取第二个选择器匹配结果</p>  |    .class1 > \*    |
| `<selector> + <selector>` | <p>第二个选择器为第一个选择器的兄弟元素<br>选取第二个选择器的下一兄弟元素</p> |  .class1 + \[lang] |
| `<selector> ~ <selector>` | <p>第二个选择器为第一个选择器的兄弟元素<br>选取第二个选择器的全部兄弟元素</p> | .class1 \~ \[lang] |

**联合选择器与反选择器**

|           选择器           |                  描述                  |     举例     |
| :---------------------: | :----------------------------------: | :--------: |
| `<selector>,<selector>` | <p>属于第一个选择器的元素<br>或者是属于第二个选择器的元素</p> |   h1, h2   |
|    `:not(<selector>)`   |              不属于选择器选中的元素             | :not(html) |

### find\_element\_by\_link\_text()

### find\_element\_by\_partial\_link\_text()

### find\_element\_by\_tag\_name()

### find\_element\_by\_class\_name()

### find\_element\_by\_name()

### find\_elements\_by\_xxx()

每种方法还有对应的 `find_elements_by_xxx` 的查找多元素的方法，可以找到整个html页面中满足搜索条件的元素并返回一个 list\[]。

## 查找元素是否存在

有两种方法：\
1\. 通过 `find_elements_by_xxx()` 寻找并返回一个数组，如果数组长度为0则没有找到\
2\. 通过 `find_element_by_xxx()` 是否抛出异常来判断是否存在

## 无法找到元素

找不到自己想要的元素的时候可以通过：

```python
elements = b.find_elements_by_xxx() # 注意是 elements
for i in elements:  
    print(elements)
    print(elements.text)
```

来寻找所有有 xxx 属性的元素，检查自己的元素是否在内，如果元素都不在内，说明很大问题，接着下面。

## 特殊样式的查找

### iframe 框架

查看要寻找的元素前后，是否存在一个 `<iframe>` 的东西，如果是，可能就是被 iframe 阻挡了.

> `<iframe>` 标签定义：iframe 元素会创建包含另外一个文档的内联框架（即行内框架）。\
> ![](/files/-M15GpAnkYAw3GpJWr8n)

也就是说，当前你的 browser 只能看到一个最外部的框架，可能可以读到 iframe，但是 iframe 内部的内容被遮挡了，这种框架通常出现在SAP系统，QQ邮箱等地方。\
**解决方法**：

```python
driver.switch_to_frame('iframe标签的name或id') (*)
driver.find_element_by_xxx('what u need')
```

通过加一个 `switch_to_frame()` 的方式就可以进入到框架内去查找了。

#### 嵌套 iframe

嵌套：f1中嵌套着f2

```python
driver.switch_to_frame("f1")  
driver.switch_to_frame("f2")
```

#### 退出 iframe

```python
# 第一种方式：跳出所有iframe，回到主界面
driver.switch_to_default_content()

# 第二种方式：回到f1（返回上一级）
driver.switch_to.parent_frame()
```

### table 类

对于表格而言，我们是难以直接定位到里面的元素的，因为可能一个 `<table>` 中的多个 `<td>` 或 `<tr>` 都没有id，只有顺序，所以这个时候我们要先定位到 `<table>` 然后根据当前的 `<table>` 定位查找其子元素。

```python
# 定位到table，并获得table中所有得tr元素
menu_table = self.driver.find_element_by_xpath("xxx/table")
rows = menu_table.find_elements_by_tag_name('tr')

# python 的len()函数返回对象（字符、列表、元组）的长度或者元素得个数
before_add_numbers = len(rows)
print(before_add_numbers)
```

举个例子：\
![](/files/-M1JIWVxm-p_5UuntRjJ)\
如何获取 `WRONG ANSWER` 这个 `text`

```python
pos=0 # 正确点个数
neg=0 # 错误点个数
# 等待测试点的表出现
element = wait.until(
    EC.presence_of_element_located((
        By.XPATH, '//*[@id="app"]/div[2]/div/div/div/div[2]/div[1]/div/table')))  

# 获取测试点的表
menu_table=b.find_element_by_xpath(
    '//*[@id="app"]/div[2]/div/div/div/div[2]/div[1]/div/table')

rows=menu_table.find_elements_by_tag_name('tr')

# 对表的每一行进行遍历
for ii in rows:
    tds=ii.find_elements_by_css_selector('tr td pre') # 根据css深层定位
    for j in tds:
        # 正确加一个
        if j.text=='ACCEPTED': # j.text 就是元素的文字
            pos += 1
        elif j.text=='WRONG ANSWER':
            neg += 1
```

## Reference

1. [csdn | 提取页面包含iframe标签元素的办法](https://blog.csdn.net/liu_xzhen/article/details/80388465?depth_1-utm_source=distribute.pc_relevant.none-task\&utm_source=distribute.pc_relevant.none-task)  &#x20;
2. [cnblogs | python+selenium四：iframe查看、定位、切换](https://www.cnblogs.com/zhongyehai/p/9170366.html)  &#x20;
3. [csdn | Python3 CssSelector定位方式实例详解](https://blog.csdn.net/qq523176585/article/details/82860332) &#x20;
4. [cnblogs | 获取table列表中所有数据条数](https://www.cnblogs.com/lucky0425/p/10564690.html) &#x20;


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://legacy.cookielau.com/archives/3-python/3-spider/2-locating.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
