[Python] Selenium 教學

程式語言:Python
Package:selenium

官方網站

功能:控制瀏覽器

from selenium import webdriver
from selenium.common.exceptions import TimeoutException

# available since 2.4.0
from selenium.webdriver.support.ui import WebDriverWait

# available since 2.26.0
from selenium.webdriver.support import expected_conditions as EC

# 建立 driver
# 需下載 Browser Drivers http://www.seleniumhq.org/download/ 
# 若 geckodriver 有在 PATH 中, firefox 可不帶路徑參數
driver = webdriver.Firefox(executable_path=r'.\driver\geckodriver.exe')

# 去 google
driver.get("http://www.google.com")

# 顯示標題
print(driver.title)

# 找到搜尋框
inputElement = driver.find_element_by_name("q")

# 搜尋框輸入字
inputElement.send_keys("cheese!")

# 提交
inputElement.submit()

try:
    # 直到標題有 cheese
    WebDriverWait(driver, 10).until(EC.title_contains("cheese!"))

    # 顯示標題,可看到 cheese
    print(driver.title)
except TimeoutException:
    print('time out')
finally:
    driver.quit()

建立 driver

詳細可參考
driver = webdriver.Firefox()

# 需下載 driver 才能用 http://www.seleniumhq.org/download/ 
# 若 geckodriver 有在 PATH 中, firefox 可不帶路徑參數
driver = webdriver.Firefox(executable_path=r'.\driver\geckodriver.exe')
driver = webdriver.Chrome('/path/to/chromedriver')
driver = webdriver.Ie('/path/to/Iedriver')

# 可更改 firefox 的 profile
profile = webdriver.FirefoxProfile()
profile.native_events_enabled = True
driver = webdriver.Firefox(profile)

# 目前網址
driver.current_url

# 截圖
driver.save_screenshot('/Screenshots/foo.png')

# 原始碼 driver.page_source
with open('test.html','wb') as f:
    f.write(driver.page_source.encode('utf-8'))

# 標題
driver.title

# 重新整理
driver.refresh()

# 關閉目前視窗
driver.close()

# 結束全部視窗
driver.quit()

如何選擇 element

找不到會回傳 NoSuchElementException
建議使用
from selenium.common.exceptions import NoSuchElementException 
PS. 複數選擇加上 s 即可
例:
find_element_by_id => find_elements_by_id
find_element => find_elements
  • By id
    • <div id="coolestWidgetEvah">...</div>
    • element = driver.find_element_by_id("coolestWidgetEvah")
    • from selenium.webdriver.common.by import By
      element = driver.find_element(By.ID, "coolestWidgetEvah")
  • By Class Name
    • <div class="coolestWidgetEvah">...</div>
    • element = driver.find_element_by_class_name("coolestWidgetEvah")
    • from selenium.webdriver.common.by import By
      element = driver.find_element(By.CLASS_NAME, "coolestWidgetEvah")
  • By Tag Name
    • <iframe src="..."></iframe>
    • element = driver.find_element_by_tag_name("iframe")
    • from selenium.webdriver.common.by import By
      element = driver.find_element(By.TAG_NAME, "iframe")
  • By Name
    • <div name="coolestWidgetEvah">...</div>
    • element = driver.find_element_by_name("coolestWidgetEvah")
    • from selenium.webdriver.common.by import By
      element = driver.find_element(by=By.NAME, value="coolestWidgetEvah")
  • By Link Text
    • <a href="http://www.google.com/search?q=cheese">abc</a>>
    • element = driver.find_element_by_link_text("abc")
    • from selenium.webdriver.common.by import By
      element = driver.find_element(by=By.LINK_TEXT, value="abc")
  • By Partial Link Text
    • <a href="http://www.google.com/search?q=cheese">abc</a>>
    • element = driver.find_element_by_partial_link_text("ab")
    • from selenium.webdriver.common.by import By
      element = driver.find_element(By.PARTIAL_LINK_TEXT, "ab")
  • By CSS
    • CSS Selector Reference
    • <div id="food"><span class="dairy aged">cheese</span></div>
    • element = driver.find_element_by_css_selector("#food span.dairy.aged")
    • from selenium.webdriver.common.by import By
      element = driver.find_element(By.CSS_SELECTOR, "#food span.dairy.aged")
  • By XPath
    • [Python] XPath 教學
    • <input type="text" name="example" />
    • element = driver.find_element_by_xpath("//input")
    • from selenium.webdriver.common.by import By
      element = driver.find_element(By.XPATH, "//input")
  • Using JavaScript
    • element = driver.execute_script("return $('.cheese')[0]")
    • labels = driver.find_elements_by_tag_name("label")
      inputs = driver.execute_script(
          "var labels = arguments[0], inputs = []; for (var i=0; i < labels.length; i++){" +
          "inputs.push(document.getElementById(labels[i].getAttribute('for'))); } return inputs;", labels)
      

element 相關

同樣具有上面的尋找 function 跟 send_keys 的功能
也有截圖 screenshot(filename) 若出現 error,表示瀏覽器不支援,可用 PIL 自行截取
詳細可參考
  • clear()
    • 清除內容
  • click()
    • 點擊
  • get_attribute(name)
    • 得到 attribute/property 的值
    •  is_active = "active" in target_element.get_attribute("class")
  • id
    • Internal ID 被 selenium  所使用
    • 可用來比較兩者是否一樣
  • is_displayed()
    • 是否可見
  • is_enabled()
    • 是否可使用
  • is_selected()
    • 是否被選擇
  • location
    • 元件位置 
  • location_once_scrolled_into_view
    • 可見的話回傳位置,不然為 None
  • parent
    •  元件的上一層
  • rect
    • 元件的位置與大小
  • size
    • 元件的大小
  • submit()
    • 提交
  • tag_name
    • tag 的名字
  • text
    • text 的值
  • value_of_css_property(property_name)
    • css 的值
    • element.value_of_css_property("font-size")

填表的方式

填入值
element.send_keys("pycon")

element.clear() # 清除

選擇值
select = driver.find_element_by_tag_name("select")
allOptions = select.find_elements_by_tag_name("option")
for option in allOptions:
    print "Value is: " + option.get_attribute("value")
    option.click()
<select name="YourLocation" style="width:200px;">
    <option value="Taipei">台北</option>
    <option value="Taoyuan">桃園</option>
    <option value="Hsinchu">新竹</option>  
    <option value="Miaoli">苗栗</option>
</select>
# available since 2.12
from selenium.webdriver.support.ui import Select
# 選擇
select = Select(driver.find_element_by_name('YourLocation'))
select.select_by_index(1) => 桃園
select.select_by_visible_text("新竹")
select.select_by_value("Taipei")

# 取消,只能用於多選,可用 select.is_multiple 判斷會為 True,但單選為 None
select.deselect_by_index(1) 
select.deselect_by_visible_text("新竹")
select.deselect_by_value("Taipei")
select.deselect_all()

# 目前選擇的項目
select.all_selected_options

# 選單項目
select.options

提交
driver.find_element_by_id("submit").click()

element.submit()

切換視窗的方法

需由程式產生的 driver,並由此 driver 生成的新視窗才有用
driver.switch_to.window("windowName")

for handle in driver.window_handles:
    driver.switch_to.window(handle)

# 也可切 frame
driver.switch_to.frame("frameName")
driver.switch_to_frame("frameName.0.child")

# 回到初始位置
driver.switch_to_default_content()

警告視窗

# 得到警告視窗
alert = driver.switch_to_alert()

# 忽略
alert.dismiss()

# 接受
alert.accept()

# 輸入帳密
alert.authenticate(username, password)

瀏覽方法

# 取得網址,直到網頁完成才會繼續往下,但 AJAX 需用檢查元件存在的方式
driver.get("http://www.example.com")

# 下一頁
driver.forward()

# 上一頁
driver.back()

Cookies 操作

# 加入
driver.add_cookie({'name':'key', 'value':'value', 'path':'/'})

# 得到
for cookie in driver.get_cookies():
    print "%s -> %s" % (cookie['name'], cookie['value'])

# 刪除
driver.delete_cookie("CookieName")
driver.delete_all_cookies()

執行操作

  • click(on_element=None)
    • 左鍵點擊
    • 可傳入 element ,若為 None,則依滑鼠位置進行動作
  • click_and_hold(on_element=None)
    • 左鍵按住
    • 可傳入 element ,若為 None,則依滑鼠位置進行動作
  • context_click(on_element=None)
    • 右鍵點擊
    • 可傳入 element ,若為 None,則依滑鼠位置進行動作
  • context_click(on_element=None)
    • 右鍵點擊
    • 可傳入 element ,若為 None,則依滑鼠位置進行動作
  • double_click(on_element=None)
    • 左鍵連擊
    • 可傳入 element ,若為 None,則依滑鼠位置進行動作
  • drag_and_drop(source, target)
    • 拖曳並放開
    • 拖曳 source 到 target 中
  • drag_and_drop_by_offset(source, xoffset, yoffset)
    • 拖曳並放開
    • 拖曳 source 到遠離原本位置 (xoffset, yoffset) 的相對位置
  • key_down(value, element=None)
    • 按住鍵盤某個值
    • 傳入值,也可傳入 element ,若為 None,則依目前 focused 的元件進行動作
    • ctrl+c
      • from selenium.webdriver.common.keys import Keys
        ActionChains(driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
  • key_up(value, element=None)
    • 放開鍵盤某個值
    • 傳入值,也可傳入 element ,若為 None,則依目前 focused 的元件進行動作
  • move_by_offset(xoffset, yoffset)
    • 移動滑鼠,為虛擬的,並非實際的滑鼠,所以看不到
    • 可傳入 element ,若為 None,則依滑鼠位置進行動作
  • move_to_element(to_element)
    • 移動滑鼠至元件,為虛擬的,並非實際的滑鼠,所以看不到
    • 傳入 element
  • move_to_element_with_offset(to_element, xoffset, yoffset)
    • 移動滑鼠至元件的相對位置,為虛擬的,並非實際的滑鼠,所以看不到
    • 傳入 element 與相對位置 (xoffset, yoffset)
  • perform()
    • 執行動作
    • 所以動作都必須執行,才會成立
  • release(on_element=None)
    • 放開左鍵
    • 可傳入 element ,若為 None,則依滑鼠位置進行動作
  • send_keys(*keys_to_send)
    • 送出複數的鍵盤值
    • 傳入值,可用 Keys 得到特殊鍵
      from selenium.webdriver.common.keys import Keys
  • send_keys_to_element(element, *keys_to_send)
    • 送出複數的鍵盤值給元件
    • 傳入元件與值,可用 Keys 得到特殊鍵
      from selenium.webdriver.common.keys import Keys
範例
from selenium.webdriver.common.action_chains import ActionChains
menu = driver.find_element_by_css_selector(".nav")
hidden_submenu = driver.find_element_by_css_selector(".nav #submenu1")

ActionChains(driver).move_to_element(menu).click(hidden_submenu).perform()

menu = driver.find_element_by_css_selector(".nav")
hidden_submenu = driver.find_element_by_css_selector(".nav #submenu1")

actions = ActionChains(driver)
actions.move_to_element(menu)
actions.click(hidden_submenu)
# actions 已記錄上面動作,只需執行便可重覆之
actions.perform()

# 拖曳放置
element = driver.find_element_by_name("source")
target =  driver.find_element_by_name("target")

ActionChains(driver).drag_and_drop(element, target).perform()

等待

WebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None)
from selenium.webdriver.support.ui import WebDriverWait

# 可使用 until
element = WebDriverWait(driver, 10).until(lambda x: x.find_element_by_id("someId"))

# 可使用 until_not
is_disappeared = WebDriverWait(driver, 30, 1, (ElementNotVisibleException)).
        until_not(lambda x: x.find_element_by_id("someId").is_displayed())
明確型的等待
# 等待 10s 直到有 id 符合
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException

driver = webdriver.Firefox()
driver.get("http://somedomain/url_that_delays_loading")
try:
    element = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.ID, "myDynamicElement"))
    )
except TimeoutException:
    print('time out')
finally:
    driver.quit()

暗示型的等待
from selenium import webdriver

driver = webdriver.Firefox()
# 尋找時間初始值為 0
driver.implicitly_wait(10) # seconds
driver.get("http://somedomain/url_that_delays_loading")
myDynamicElement = driver.find_element_by_id("myDynamicElement")

期望條件

建立後,需傳入 driver 同下或是用 until 執行同上
from selenium.webdriver.support import expected_conditions as EC

test = EC.title_is("google")
test(driver) # get result
locator = (by, path),例:(By.NAME, "google")
  • alert_is_present()
    • 檢查是否有警告視窗
  • element_located_selection_state_to_be(locator, is_selected)
    • 檢查選擇的狀態
  • element_located_to_be_selected(locator)
    • 檢查是否被選擇
  • element_selection_state_to_be(element, is_selected)
    • 檢查選擇的狀態
  • element_to_be_clickable(locator)
    • 檢查是否 visible 且 enabled
  • element_to_be_selected(element)
    • 檢查是否被選擇
  • frame_to_be_available_and_switch_to_it(locator)
    • frame 是否可用,可用則切換過去
  • invisibility_of_element_located(locator)
    • 檢查是否不可見或不存在
  • presence_of_all_elements_located(locator)
    • 檢查是否存在,並回傳所有存在
  • presence_of_element_located(locator)
    • 檢查是否存在,並回傳第一個存在
  • staleness_of(element)
    • 等待直到元件不存在
  • text_to_be_present_in_element(locator, text_)
    • 檢查元件 text 是否包含特定字串
  • text_to_be_present_in_element_value(locator, text_)
    • 檢查元件 value 是否包含特定字串
  • title_contains(title)
    • 檢查標題是否包含特定字串
  • title_is(title)
    • 檢查標題是否為特定字串
  • visibility_of(element)
    • 檢查是否可見,可見:代表非隱藏,並且寬和高都不等於 0
  • visibility_of_element_located(locator)
    • 檢查是否可見,可見:代表非隱藏,並且寬和高都不等於 0

建立具有事件的 driver

但只有透過程式執行,才算數
範例
from selenium.webdriver import Firefox
from selenium.webdriver.support.events import EventFiringWebDriver, AbstractEventListener

class MyListener(AbstractEventListener):
    def before_navigate_to(self, url, driver):
        print("Before navigate to %s" % url)
    def after_navigate_to(self, url, driver):
        print("After navigate to %s" % url)

driver = Firefox()
ef_driver = EventFiringWebDriver(driver, MyListener())
ef_driver.get("http://www.google.co.in/")

事件種類
  • after_change_value_of(element, driver)
  • after_click(element, driver)
  • after_close(driver)
  • after_execute_script(script, driver)
  • after_find(by, value, driver)
  • after_navigate_back(driver)
  • after_navigate_forward(driver)
  • after_navigate_to(url, driver)
  • after_quit(driver)
  • before_change_value_of(element, driver)
  • before_click(element, driver)
  • before_close(driver)
  • before_execute_script(script, driver)
  • before_find(by, value, driver)
  • before_navigate_back(driver)
  • before_navigate_forward(driver)
  • before_navigate_to(url, driver)
  • before_quit(driver)
  • on_exception(exception, driver)

執行 JavaScript

  • execute_async_script(script, *args)
    • 非同步
  • execute_script(script, *args)
    • 同步
範例
driver.execute_script(script)

r.find_elements_by_tag_name("label")
inputs = driver.execute_script(script)

顏色值轉換

from selenium.webdriver.support.color import Color

# rgba(0, 255, 51, 1)
print(Color.from_string('#00ff33').rgba)

# #01ff03
print(Color.from_string('rgb(1, 255, 3)').hex)

# rgb(0, 0, 255)
print(Color.from_string('blue').rgb)

參考:

Selenium Python Bindings
Selenium Documentation

留言

張貼留言