# 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 元素会创建包含另外一个文档的内联框架（即行内框架）。\
> ![](https://2161500321-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M0bSIkrSKJhpcDpbSbW%2F-M15Gni0AInt189vooPg%2F-M15GpAnkYAw3GpJWr8n%2F2020-02-27-15-45-11.png?generation=1582800788773794\&alt=media)

也就是说，当前你的 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)
```

举个例子：\
![](https://2161500321-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M0bSIkrSKJhpcDpbSbW%2F-M1JIS15ZMiTATWipjp2%2F-M1JIWVxm-p_5UuntRjJ%2F2020-03-01-11-36-51.png?generation=1583036127239171\&alt=media)\
如何获取 `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;
