# selenium ## References + 🔗 [**Selenium 教學**](https://steam.oxxostudio.tw/category/python/spider/selenium.html) + 🔗 [**Selenium IDE 教學**](https://www.tpisoftware.com/tpu/articleDetails/1846#markdown-header-selenium-ide) + 🔗 [**Chrome Driver**](https://chromedriver.chromium.org/downloads) + 🔗 [**robots.txt**](https://www.cloudflare.com/zh-tw/learning/bots/what-is-robots-txt/) ## Selenium IDE | command | desciption | |:--- |:---| |`assert` |(驗證) 檢核目標是否符合期待的值,出現錯誤就終止測試。| |`verify` |(辨識) 檢核目標是否為設定值,出現錯誤測試仍繼續。| |`wait for` |(等待) 檢核目標是否在指定時間內出現某狀態,出現錯誤就終止測試。| |`type` |(輸入)| |`send keys` |(按下某鍵)| ## Note ### 尋找元素 + 說明 | by | description | |--- |--- | |`By.ID`|透過 id,尋找第一個相符的元素| |`By.CLASS_NAME`|透過 class,尋找第一個相符的元素| |`By.CSS_SELECTOR`|透過 css 選擇器,尋找第一個相符的元素| |`By.NAME`|透過 name 屬性,尋找第一個相符的元素| |`By.TAG_NAME`|透過 HTML tag,尋找第一個相符的元素| |`By.LINK_TEXT`|透過 hyperlink 的文字,尋找第一個相符的元素| |`By.PARTIAL_LINK_TEXT`|透過 hyperlink 的部分文字,尋找第一個相符的元素| |`By.XPATH, xpath`|透過 xpath 的方式,尋找第一個相符的元素| + 範例 ```py # 尋找第一個 option web_element = driver.find_element(By.TAG_NAME, "option") print(web_element.text) # OXXO # 再尋找下一個 option web_element = web_element.find_element(By.XPATH, "following-sibling::*[1]") print(web_element.text) # GKPen # 再尋找下兩個 option web_elements = web_element.find_elements(By.XPATH, "following-sibling::*[position() <= 2]") for web_element in web_elements: print(web_element.text) # OK # Hello ``` ### 操作元素 + 說明 | method | params | description | |--- |--- |--- | |`click()`|`element`|按下滑鼠左鍵| |`click_and_hold()`|`element`|滑鼠左鍵按著不放| |`double_click()`|`element`|連續按兩下滑鼠左鍵| |`context_click()`|`element`|按下滑鼠右鍵 ( 需搭配指定元素定位 )| |`drag_and_drop()`|`source, target`|點擊 source 元素後,移動到 target 元素放開| |`drag_and_drop_by_offset()`|`source, x, y`|點擊 source 元素後,移動到指定的座標位置放開| |`move_by_offset()`|`x, y`|移動滑鼠座標到指定位置| |`move_to_element()`|`element`|移動滑鼠到某個元素上| |`move_to_element_with_offset()`|`element, x, y`|移動滑鼠到某個元素的相對座標位置| |`release()`|`element`|放開滑鼠| |`send_keys()`|`values`|送出某個鍵盤按鍵值| |`send_keys_to_element()`|`element`, values|向某個元素發送鍵盤按鍵值| |`key_down()`|`value`|按著鍵盤某個鍵| |`key_up()`|`value`|放開鍵盤某個鍵| |`reset_actions()`||清除儲存的動作| |`pause()`|`seconds`|暫停動作| |`perform()`||執行儲存的動作| ```py # WebElement a: WebElement a = driver.find_element(By.ID, 'a') a.click() # 點擊 a time.sleep(1) # 等待一秒 # ActionChains actions: ActionChains actions = ActionChains(driver) actions.click(a).pause(1) # 點擊 a 並等待 1 秒 actions.perform() # 執行動作鏈 ``` ### 取得元素內容 + 說明 | method | params | description | |--- |--- |--- | |`get_attribute()`|`name`|元素的某個 HTML 屬性值 (例如:"class")| |`get_property()`|`name`|元素的某個 HTML 特性值 (例如:"readonly" / "disabled" / "required" / "checked" ...)| |`is_displayed()`||元素是否顯示在網頁上| |`is_enabled()`||元素是否可用| |`is_selected()`||元素是否被選取| |`screenshot()`|`filename`|將元素截圖並儲存成圖片| | attribute | description | |--- |--- | |`text`|元素的內容文字| |`id`|元素的 id| |`tag_name`|元素的 tag 名稱| |`size`|元素的長寬尺寸| |`parent`|元素的父元素| + 範例 ```py web_element = driver.find_element(By.TAG_NAME, "select") print(web_element.get_attribute("id")) # select web_element = driver.find_element(By.TAG_NAME, "select") print(web_element.get_attribute("innerHTML")) # <option value="OXXO">OXXO</option> # <option value="GKPen">GKPen</option> # <option value="OK">OK</option> # <option value="Hello">Hello</option> web_element = driver.find_element(By.TAG_NAME, "select") print(web_element.get_attribute("outerHTML")) # <select id="select"> # <option value="OXXO">OXXO</option> # <option value="GKPen">GKPen</option> # <option value="OK">OK</option> # <option value="Hello">Hello</option> # </select> outer_text = web_element.get_attribute("outerHTML") soup = BeautifulSoup(outer_text, "html.parser") print(soup.find("option", {"value": "GKPen"}).text) # GKPen ``` ### 預期元素 |☢️ <span class="warning">WARNING</span> : `time.sleep()` bad👎| |:---| |你應預期某元素顯現可點擊、已載入等等的狀態,卻把這樣的預期付諸等待。<br>你永遠不知道可能發生什麼事,導致元素沒能在你寫死的時間內顯現狀態!| + 說明 | function | params | description | |--- |--- |--- | |`presence_of_element_located()`|`locator`|等待元素出現在 DOM 中(不保證可見)。| |`presence_of_all_elements_located()`|`locator`|等待一組元素出現在 DOM 中。| |`visibility_of()`|`element`|等待傳入的 WebElement 可見。| |`visibility_of_element_located()`|`locator`|等待指定 locator 的元素可見。| |`visibility_of_all_elements_located()`|`locator`|等待一組元素都可見。| |`invisibility_of_element_located()`|`locator`|等待元素不可見或不存在。| |`invisibility_of_element()`|`element`|等待指定的元素消失或不可見。| |`element_to_be_clickable()`|`locator`|元素可見且可點擊(常用)。| |`element_to_be_selected()`|`element_or_locator`|等待元素被選取(如 checkbox)。| |`element_selection_state_to_be()`|`element_or_locator, is_selected`|等待元素的選取狀態符合期望。| |`text_to_be_present_in_element()`|`locator, text`|等待元素內包含特定文字。| |`text_to_be_present_in_element_value()`|`locator, text`|等待元素的 `value` 屬性包含特定文字。| |`attribute_contains()`|`locator, attribute, value`|等待屬性包含指定子字串。| |`attribute_to_be()`|`locator, attribute, value`|等待屬性等於指定值。| |`number_of_windows_to_be()`|`count`|等待視窗數量符合指定值(常用於彈出視窗)。| |`number_of_elements_to_be()`|`locator, count`|等待某 locator 對應的元素數量符合指定值。| |`staleness_of()`|`element`|等待元素變得無效(例如 DOM 更新後)。| |`frame_to_be_available_and_switch_to_it()`|`locator`|等待 iframe 可用並自動切換進入。| |`alert_is_present()`||等待 alert 彈出(可接 `.accept()`)。| + 範例 ```py # [掛機神器] # 等 900 s,每 500 ms 檢查一次 "某元素是否可以點擊",如果可以點就立馬點 # ⚠️ 注意:傳進去的是 tuple! try: buy_button: WebElement = WebDriverWait(driver, 900).until( EC.element_to_be_clickable((By.CSS_SELECTOR, "...")) ) buy_button.click() except Exception as err: print(err) driver.quit() ``` ```py # [等待載入] # 等 5 s,每 500 ms 檢查一次 "某元素是否載入",如果已載入就立馬使用 # ⚠️ 注意:傳進去的是 tuple! try: beginning_block: WebElement = WebDriverWait(driver, 5).until( EC.presence_of_element_located((By.CLASS_NAME, "...")) ) except Exception as err: print(err) driver.quit() ``` ### 執行腳本 + 範例 ```py driver.execute_script('window.scrollTo(0, 2500)') # 捲動到 2500px 位置 ``` ### 錯誤 + 說明 | exception | description | |--- |--- | | `NoSuchElementException` | 無法找到符合條件的元素,例如 `find_element()` 找不到任何結果。 | | `TimeoutException` | `WebDriverWait` 等待條件超時,例如元素遲遲未出現。 | | `StaleElementReferenceException` | 元素在 DOM 中被更新或移除,原本的 WebElement 引用失效。 | | `ElementNotInteractableException` | 元素存在但不可互動(例如被隱藏、disabled 或被其他元素遮住)。 | | `ElementClickInterceptedException` | 嘗試點擊元素時,被其他元素攔截(例如彈窗、遮罩)。 | | `ElementNotSelectableException` | 元素無法被選取(多發生在 select/option 未啟用時)。 | | `InvalidElementStateException` | 元素狀態不允許執行操作(例如 disabled 的輸入框送鍵)。 | | `MoveTargetOutOfBoundsException` | 嘗試移動滑鼠到螢幕或 viewport 之外的座標。 | | `JavascriptException` | 執行 `execute_script()` 時 JS 發生錯誤。 | | `UnexpectedAlertPresentException` | 操作期間突然跳出 alert,導致命令中斷。 | | `NoAlertPresentException` | 嘗試處理 alert 時(accept/dismiss)實際上不存在 alert。 | | `WebDriverException` | 所有 webdriver 錯誤的父類別(通用錯誤)。 | | `SessionNotCreatedException` | 啟動瀏覽器 session 失敗,常見於 driver 與瀏覽器版本不符。 | | `InvalidSessionIdException` | 操作已關閉或失效的 session(例如 driver.quit() 後仍呼叫)。 | | `InvalidArgumentException` | 傳入不合法的參數(例如錯誤的 URL、locator 類型)。 | | `NoSuchWindowException` | 嘗試切換到不存在的視窗或 tab。 | | `NoSuchFrameException` | 嘗試切換到不存在的 iframe。 | | `NoSuchCookieException` | 嘗試取得不存在的 cookie。 | | `UnexpectedTagNameException` | 使用錯誤的 WebElement 類型(例如非 select 元素傳給 Select)。 | | `InvalidSelectorException` | locator CSS/XPath 語法錯誤。 | | `ImeNotAvailableException` | IME(輸入法引擎)不可用或不支援。 | | `ImeActivationFailedException` | 嘗試啟用 IME 失敗。 | | `UnhandledAlertException` | alert 未被正確處理導致操作中斷。 | | `NoSuchDriverException` | 嘗試存取已被關閉的 driver。 | | `ScreenshotException` | 無法對元素或頁面截圖。 | | `ErrorInResponseException` | 低階通訊錯誤(通常是舊版 Selenium)。 | | `RemoteDriverServerException` | 遠端 webdriver server 發生錯誤(remote 執行時)。 | | `InvalidCookieDomainException` | 嘗試設定 cookie 到不屬於目前 domain 的網站。 | | `InsecureCertificateException` | SSL 憑證錯誤或瀏覽器拒絕載入網站。 | ### 結束 + 範例 ```py # 關閉視窗 driver.close() # 結束瀏覽器進程 driver.quit() ```