---
# System prepended metadata

title: Python Selenium 筆記
tags: [Python]

---

# Python Selenium 筆記

###### tags: `Python`

## 參考資料

* [Selenium-Python中文文档](https://selenium-python-zh.readthedocs.io/en/latest/getting-started.html)
* [Python selenium —— 深刻解析及操作frame、iframe](https://huilansame.github.io/huilansame.github.io/archivers/switch-to-frame)

## 安裝Selenium

指令如下：
```
pip3 install selenium
```

## 下載與安裝 Chrome Driver

請前往下方連結，下載對應版本的 Chrome Driver，並將壓縮檔內的**chromedriver.exe**解壓縮到指定的工作目錄。
[ChromeDriver - WebDriver for Chrome](http://chromedriver.chromium.org/downloads)

## 使用 Selenium 開啟 Chrome 瀏覽器

開啟 Chrome 瀏覽器，並前往 Google 搜尋頁面，然後再關閉瀏覽器，程式碼如下：
```python=
from selenium import webdriver
driver = webdriver.Chrome('chromedriver.exe')
driver.get('https://www.google.com')
driver.close()
```

## 使用 Selenium 查找元素定位

### 通過 Class name 定位元素
當你通過 class name 查找元素時可使用這個。在該策略下，頁面中第一個匹配該 class 屬性的元素會被匹配並返回。如果找不到任何元素，則會拋出 NoSuchElementException 異常。

```python=
driver.find_element_by_class_name('class name')
```

### 通過CSS選擇器查找元素

當你通過CSS選擇器查找元素時可以使用這個。在該策略下，頁面中第一個匹配該 CSS 選擇器的元素會被匹配並返回。如果找不到任何元素，則會拋出 NoSuchElementException 異常。

```python=
driver.find_element_by_css_selector('css selector')
```

### 通過 ID 查找元素
當你知道一個元素的 id 時，你可以使用本方法。在該策略下，頁面中第一個符合該 id 的元素會被匹配並返回。如果找不到任何元素，會拋出 NoSuchElementException 異常。

```python=
driver.find_element_by_id('id')
```

### 通過連結文字查找元素

當你知道在一個連結標籤中使用的文字時可使用這個。在該策略下，頁面中第一個匹配連結文字的標籤會被匹配並返回。如果找不到任何元素，則會拋出 NoSuchElementException 異常。

```python=
driver.find_element_by_link_text('link text')
```
也可以只用連結的部分文字定位元素：
```python=
driver.find_element_by_partial_link_text('partial link text')
```

### 通過 Name 查找元素

當你知道一個元素的 name 時，你可以使用本方法。在該策略下，頁面中第一個該 name 元素會被匹配並返回。如果找不到任何元素，會拋出 NoSuchElementException 異常。

```python=
driver.find_element_by_name('name')
```

### 通過標籤名 tag 查找元素

當你通過標籤名 tag 查找元素時可使用這個。在該策略下，頁面中第一個匹配該標籤名的元素 會被匹配並返回。如果找不到任何元素，會拋出 NoSuchElementException 異常。

```python=
driver.find_element_by_tag_name('tag')
```

### 通過XPath查找元素

XPath是XML文檔中查找結點的語法。因為HTML文檔也可以被轉換成XML(XHTML)文檔， Selenium的用戶可以利用這種強大的語言在web應用中查找元素。 XPath擴展了（當然也支持）這種通過id或name屬性獲取元素的簡單方式，同時也開闢了各種新的可能性， 例如獲取頁面上的第三個複選框。

使用XPath的主要原因之一就是當你想獲取一個既沒有id屬性也沒有name屬性的元素時， 你可以通過XPath使用元素的絕對位置來獲取他（這是不推薦的），或相對於有一個id或name屬性的元素（理論上的父元素）的來獲取你想要的元素。 XPath定位器也可以通過非id和name屬性查找元素。

絕對的XPath是所有元素都從根元素的位置（HTML）開始定位，只要應用中有輕微的調整，會就導致你的定位失敗。但是通過就近的包含id或者name屬性的元素出發定位你的元素，這樣相對關係就很靠譜， 因為這種位置關係很少改變，所以可以使你的測試更加強大。

```python=
driver.find_element_by_xpath('//*[@id="lst-ib"]')
```

## 使用 Selenium 操作網頁元素

開啟 Chrome 瀏覽器，並前往 Google 搜尋頁面，然後搜尋"你好"並按下 Enter，程式碼如下：
```python=
from selenium import webdriver
driver = webdriver.Chrome('chromedriver.exe')
driver.get('https://www.google.com')
q  = driver.find_element_by_name('q')

q.send_keys('你好')

from selenium.webdriver.common.keys import Keys
q.send_keys(Keys.RETURN)
```

開啟 Chrome 瀏覽器，並前往 Google 搜尋頁面，然後點擊登入按鈕，程式碼如下：

```python=
from selenium import webdriver
driver = webdriver.Chrome('chromedriver.exe')
driver.get('https://www.google.com')

login = driver.find_element_by_link_text('登入')

login.click()
```

## 使用 Selenium 登入Youtube

程式碼如下：

```python=
from selenium import webdriver
import time
driver = webdriver.Chrome('chromedriver.exe')
driver.get('https://www.youtube.com/')

yt_login = driver.find_element_by_xpath('//*[@id="buttons"]/ytd-button-renderer/a')
yt_login.click()

# 輸入 email
yt_email = driver.find_element_by_xpath('//*[@id="identifierId"]')
yt_email.send_keys('email') # Google 帳號 email
# 點擊繼續按鈕
yt_cte = driver.find_element_by_xpath('//*[@id="identifierNext"]/div/button')
yt_cte.click()

# 輸入密碼
yt_pwd = driver.find_element_by_xpath('//*[@id="password"]/div[1]/div/div[1]/input')
yt_pwd.send_keys('password') # Google 帳號的密碼
# 點擊繼續按鈕
yt_cte = driver.find_element_by_xpath('//*[@id="passwordNext"]/div/button')
yt_cte.click()

# 前往指定的 Youtube 頁面
driver.get('Youtube Link')
```

## 使用 Selenium 在Youtube 聊天室中留言

程式碼參考：
[Python selenium —— 深刻解析及操作frame、iframe](https://huilansame.github.io/huilansame.github.io/archivers/switch-to-frame)

在留言欄位輸入文字 "1" 並送出，程式碼如下：
```python=
driver.switch_to.frame('chatframe')

driver.find_element_by_css_selector('div#input').send_keys('1')

driver.find_element_by_css_selector('#send-button').click()
```

## 使用 Selenium 操作 frame 標籤中的元素

很多人在用selenium定位頁面元素的時候會遇到定位不到的問題，明明元素就在那兒，就是定位不到，這種情況很有可能是**frame** 在搞鬼（可能原因之一），可以去仔細看看你網頁的DOM樹！

**frame**標籤有**frameset**、**frame**、**iframe**三種，**frameset跟其他普通標籤沒有區別，不會影響到正常的定位**，而frame與iframe對selenium定位而言是一樣的，selenium有一組方法對frame進行操作：

```python=
driver.switch_to.frame(reference)  # 切到指定frame，可用id或name(str)、index(int)、元素(WebElement)定位
driver.switch_to.parent_frame()  # 切到父级frame，如果已是主文档，则无效果
driver.switch_to.default_content()  # 切到主文档，DOM树最开始的<html>标签
```

### 1. 怎麼切到frame中(switch_to.frame())

selenium提供了switch_to.frame()方法來切換frame

```python=
switch_to.frame(reference)
```

**reference**是傳入的參數，用來定位frame，可以傳入id、name、index以及selenium的WebElement對象，假設有如下HTML代碼 index.html：

```htmlmixed=
<html lang="en">
<head>
    <title>FrameTest</title>
</head>
<body>
<iframe src="a.html" id="frame1" name="myframe"></iframe>
</body>
</html>
```

想要定位其中的iframe並切進去，可以通過如下代碼：

```python=
from selenium import webdriver
driver = webdriver.Firefox()
driver.switch_to.frame(0)  # 1.用frame的index来定位，第一个是0
# driver.switch_to.frame("frame1")  # 2.用id来定位
# driver.switch_to.frame("myframe")  # 3.用name来定位
# driver.switch_to.frame(driver.find_element_by_tag_name("iframe"))  # 4.用WebElement对象来定位
```

通常採用id和name就能夠解決絕大多數問題。但有時候frame並無這兩項屬性，則可以用index和WebElement來定位：

* index從0開始，傳入整型參數即判定為用index定位，傳入str參數則判定為用id/name定位
* WebElement對象，即用find_element系列方法所取得的對象，我們可以用tag_name、xpath等來定位frame對象

舉個栗子：

```htmlmixed=
<iframe src="myframetest.html" />
```

用xpath定位，傳入 WebElement 物件：

```python=
driver.switch_to.frame(driver.find_element_by_xpath("//iframe[contains(@src,'myframe')]"))
```

### 2. 從frame中切回主文檔(switch_to.default_content())

切到frame中之後，我們便不能繼續操作主文檔的元素，這時如果想操作主文檔內容，則需切回主文檔。

```python=
driver.switch_to.default_content()
```

### 3. 嵌套frame的操作(switch_to.parent_frame())

有時候我們會遇到嵌套的frame，如下：

```htmlmixed=
<html>
	<iframe id="frame1">
		<iframe id="frame2" / >
	</iframe>
</html>
```

1. 從主文檔切到frame2，一層層切進去

```python=
driver.switch_to.frame("frame1")  # 先切到frame1
driver.switch_to.frame("frame2")  # 再切到frame2
```

2. 從frame2再切回frame1，這裡selenium給我們提供了一個方法能夠從子frame切回到父frame，而不用我們切回主文檔再切進來。

```python=
driver.switch_to.parent_frame()  # 如果目前已是主文檔，則無效果
```

有了parent_frame()這個相當於後退的方法，我們可以隨意切換不同的frame，隨意的跳來跳去了。

所以只要善用以下三個方法，遇到frame分分鐘搞定：

```python=
driver.switch_to.frame(reference)
driver.switch_to.parent_frame()
driver.switch_to.default_content()
```

## 使用 Selenium 滾動網頁捲軸(scrollBar)

參考：[Python Selenium控制网页滚动条缓慢滚动](https://www.pythonf.cn/read/89)

通過driver.execute()執行js代碼，達到目的。

1.
```python=
driver.execute_script('window.scrollBy(0,1000)')
```

scrollBy(x,y)中，x為必須參數，表示向右滾動的像素值；y也為必須參數，表示向下滾動的像素值

2.

```python=
driver.execute_script('window.scrollTo(0,1000)')
```

scrollTo(x,y) 中，x為必要參數，表示要在窗口文檔顯示區左上角顯示的文檔的x坐標；y也為必要參數，表示要在窗口文檔顯示區左上角顯示的文檔的y坐標

## 捲軸長度獲取

參考：[.body.scrollHeight doesn't work in Firefox](https://stackoverflow.com/questions/15932650/body-scrollheight-doesnt-work-in-firefox)

可以在 Chrome 的監控視窗的Console 下指令取得。
兩個指令皆可使用，但在某些網站(如：Youtube)這兩個指令，可能會有其中一個失效。

```javascript=
document.body.scrollHeight
```

```javascript=
document.documentElement.scrollHeight
```

![](https://i.imgur.com/WAyfefo.png)

selenium 捲軸拉到底：

方法一：
```python=
driver.execute_script('window.scrollTo(0,document.body.scrollHeight)')
```

方法二：
```python=
driver.execute_script('window.scrollTo(0,document.documentElement.scrollHeight)')
```

