利用Selenium 可以驅動浏覽器執行特定的動作,如點選、下拉等操作, 同時還可以擷取浏覽器目前呈現的頁面的源代碼 ,做到可見即可爬。
- 基本使用
- 示例:
運作代碼後,會自動彈出一個 Chrome 浏覽器。首先會跳轉到百度,然後在搜尋框中輸入 Python,接着跳轉到搜尋頁。 搜尋結果加載出來後,控制台分别會輸出目前的 URL 、目前的 Cookies 和網頁源代碼.from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.wait import WebDriverWait browser = webdriver.Chrome() try: browser.get('https://www.baidu.com') input = browser.find_element_by_id('kw') input.send_keys('Python') input.send_keys(Keys.ENTER) wait = WebDriverWait(browser,10) wait.until(EC.presence_of_element_located((By.ID,'content_left'))) print(browser.current_url) print(browser.get_cookies()) print(browser.page_source) finally: browser.close()
- 如果用 Selenium 來驅動浏覽器加載網頁的話,就可以直接拿到 JavaScript 渲染的結果,不用擔心使用的是什麼加密系統。聲明浏覽器對象
聲明浏覽器對象
- Selenium 支援非常多浏覽器,如 Chrome/ Firefox/ Edge等,還有 Android 等手機端的浏覽器,也支援無界面浏覽器 PhantomJS。
- 初始化方式:
浏覽器對象的初始化并将其指派為 browser對象。接下來,就是調用 browser 對象,讓其執行各個動作以模拟浏覽器操作from selenium import webdriver browser = webdriver.Chrome() browser = webdriver.Firefox() browser = webdriver.Edge() browser = webdriver.PhantomJS() browser = webdriver.Safari()
- 初始化方式:
通路頁面
- 用 get()方法來請求網頁,參數傳傳入連結接 URL 即可。如用 get()方法通路淘寶, 然後輸出源代碼,代碼:
彈出了 Chrome 浏覽器并且自動通路了淘寶,然後控制台輸出了淘寶頁面的源代碼, 随後浏覽器關閉。可以實作浏覽器的驅動并擷取網頁源碼from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.taobao.com/') print(browser.page_source)
查找節點
- Selenium 可以驅動浏覽器完成各種操作,如填充表單、模拟點選等。Selenium 提供了系列查找節點的方法,我們可以用這些方法來擷取想要的節點,以便執行一些動作或者提取資訊。
- 單個節點
- 如,要從淘寶頁面中提取搜尋框這個節點,觀察源代碼(它的 id 是 q, name 也是q。此時就可以用多種方式擷取它。如,find_element_by_name()是根據 name 值擷取,find element_by_id()是根據 id 擷取。 還有根據 XPath、css 選擇器等擷取的方式):
這裡使用 3 種方式擷取輸入框,分别根據 ID, CSS 選擇器和 XPath 擷取,它們傳回的結果完全一緻。from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.taobao.com') input_first = browser.find_element_by_id('q') input_second = browser.find_element_by_xpath('//*[@id ="q"]') input_third = browser.find_element_by_css_selector('#q') print(input_first, input_second, input_third) browser.close()
- 列出所有擷取單個節點的方法:
- find_element_by_id
- find_element_by_name
- find_element_by_xpath
- find_element_by_link_text
- find_element_by_partial_link_text
- find_element_by_tag_name
- find_element_by_class_name
- find_element_by_css_selector
- Selenium 提供了通用方法 find_element (),需要傳入兩個參數:查找方式 By 和值。就是 find_element_by_id()這種方法的通用函數版本,如 find_element_by_id(id )等價 find_element(By.ID, id),得到的結果完全一緻。代碼:
查找方式的功能與上面列舉函數一緻,參數更加靈活。from selenium import webdriver from selenium.webdriver.common.by import By browser = webdriver.Chrome() browser.get('https://www.taobao.com') input_first = browser.find_element(By.ID, 'q') print(input_first) browser.close()
- 如,要從淘寶頁面中提取搜尋框這個節點,觀察源代碼(它的 id 是 q, name 也是q。此時就可以用多種方式擷取它。如,find_element_by_name()是根據 name 值擷取,find element_by_id()是根據 id 擷取。 還有根據 XPath、css 選擇器等擷取的方式):
- 多個節點
- 如果有多個節點,用 find_element()方法查找,就隻能得到第一個節點。如果要查找所有滿足條件的節點, 需要用 find_elements()方法。注意,這個方法名稱中,element 多了一個s
- 查找淘寶側邊導覽列的所有條目:
from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.taobao.com') lis = browser.find_elements_by_css_selector('.service-bd li') print(lis) browser.close()
得到的内容變成了清單類型,清單中的每個節點都是 WebElement 類型。
如果用 find_element()方法,隻能擷取比對的第一個節點,結果是 WebElement類型。如果用 find_elements()方法,結果是清單類型,清單中的每個節點是 WebElement 類型。
- 列出所有擷取多個節點的方法:
- find_elements_by_id
- find_elements_by_name
- find_elements_by_xpath
- find_elements_by_link_text
- find_elements_by_tag_name
- find_elements_by_class_name
- find_elements_by_css_selector
- lis = browser.find_elements(By.CSS_SELECTOR, ’.service-bd li')
- 查找淘寶側邊導覽列的所有條目:
- 如果有多個節點,用 find_element()方法查找,就隻能得到第一個節點。如果要查找所有滿足條件的節點, 需要用 find_elements()方法。注意,這個方法名稱中,element 多了一個s
節點互交
- Selenium 可以讓浏覽器模拟執行一些動作。常見的用法有:輸入文字時用 send_keys()方法,清空文字時用 clear()方法,點選按鈕時用 click()方法。示例:
首先驅動浏覽器打開淘寶,用 find_element_by_id()方法擷取輸入框,再用 send_keys() 方法輸入 iPhone 文字,等待秒後用 clear()方法清空輸入框,再次調用 send_keys()方法輸入 iPad 文字,之後再 find_element_by_class_name()方法擷取搜尋按鈕,最後調用 click ()方法完成搜尋動作。from selenium import webdriver import time browser = webdriver.Chrome() browser.get('https://www.taobao.com') input = browser.find_element_by_id('q') input.send_keys('iPhone') time.sleep(1) input.clear() input.send_keys('iPad') button = browser.find_element_by_class_name('btn-search') button.click()
- 更多的操作參見官方文檔互動動作介紹:http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.remote.webelement。
動作鍊
- 動作鍊 沒有特定的執行對象,如:滑鼠拖曳、鍵盤按鍵等。
- 如:現在實作一個節點的拖曳操作,将某個節點從一處拖曳到另外一處,實作代碼:
打開網頁中一個拖曳執行個體,依次選中要拖曳的節點和拖曳到的目标節點,接着聲明 ActionChains 對象并将其指派為 actions 變量,然後調用 actions 變量的 drag_and_drop()方法,再調 perform()方法執行動作。from selenium import webdriver from selenium.webdriver import ActionChains browser = webdriver.Chrome() url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable' browser.get(url) browser.switch_to.frame('iframeResult') source = browser.find_element_by_css_selector('#draggable') target = browser.find_element_by_css_selector('#droppable') actions = ActionChains(browser) actions.drag_and_drop(source.target) actions.perform
- 更多動作鍊操作 官方文檔:http://selenium-python.readthedocs.io/api.html#moduleselenium.webdriver.common.action_chains
執行JavaScript
- 某些操作,Selenium API 并沒有提供。如,下拉進度條,它可以直接模拟運作 JavaScript,此時使用 execute_script()方法即可實作,(注意:execute_script):
from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.zhihu.com/explore') browser.execute_script('window.scrollTo(0,document.body.scrollHeight)') browser.execute_script('alert("To Bottom")')
擷取節點資訊
- 通過 page_source 屬性可以擷取網頁的源代碼,接着使用解析庫(如正則表式、Beautiful Soup、pyquery 等)提取資訊。
- 擷取屬性
- 可以使用 get_attribute ()方法來擷取節點的屬性,但是其前提是先選中這個節點,示例:
from selenium import webdriver browser = webdriver.Chrome() url = 'https://zhihu.com/explore' browser.get(url) logo = browser.find_element_by_id('zh-top-link-logo') print(logo) print(logo.get_attribute('class'))
結果擷取知乎的 logo 節點,最後列印出它的 class。
通過 get_attribute()方法,然後傳人想要擷取的屬性名,就可以得到它的值了
- 可以使用 get_attribute ()方法來擷取節點的屬性,但是其前提是先選中這個節點,示例:
- 擷取文本值
- 每個 WebElement 節點都有 text 屬性,直接調用這個屬性就可以得到節點内部的文本資訊,相當于 Beautiful Soup 的 get_text()方法、 pyquery 的 text()方法。示例:
from selenium import webdriver browser = webdriver.Chrome() url = 'https://www.zhihu.com/explore' browser.get(url) input = browser.find_element_by_class_name('zu-top-add-question') print(input.text)
- 每個 WebElement 節點都有 text 屬性,直接調用這個屬性就可以得到節點内部的文本資訊,相當于 Beautiful Soup 的 get_text()方法、 pyquery 的 text()方法。示例:
- 擷取id、位置、标簽名和大小
- Web Element 節點還有 些其他屬性,如 id 屬性可以擷取節點 id,location 屬性可以擷取該節點在頁面中的相對位置,tag_name 屬性可以擷取标簽名稱,size 屬性可以擷取節點的大小(寬高),示例:
首先獲得“提問”按鈕節點,再調用其 id、location、tag_name、size 屬性來擷取對應的屬性值from selenium import webdriver browser =webdriver.Chrome() url = 'https://www.zhihu.com/explore' browser.get(url) input = browser.find_element_by_class_name('zu-top-add-question') print(input.text) print(input.id) print(input.location) print(input.size) print(input.tag_name)
- Web Element 節點還有 些其他屬性,如 id 屬性可以擷取節點 id,location 屬性可以擷取該節點在頁面中的相對位置,tag_name 屬性可以擷取标簽名稱,size 屬性可以擷取節點的大小(寬高),示例:
切換Frame
- 網頁中有種節點叫作 iframe,也就是子 Frame,相當于頁面的子頁面,它的結構和外部網頁的結構完全一緻。Selenium 打開頁面後,預設在父級 Frame 裡面操作,而此時如果頁面中還有子Frame,是不能擷取到子 Frame 裡面的節點的。這時就需要使用 switch_to.frame()方法來切換Frame。示例:
import time from selenium import webdriver from selenium.common.exceptions import NoSuchElementException browser = webdriver.Chrome() url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable' browser.get(url) browser.switch_to.frame('iframeResult') try: logo = browser.find_element_by_class_name('logo') except NoSuchElementException: print('NO LOGO') browser.switch_to.parent_frame() logo = browser.find_element_by_class_name('logo') print(logo) print(logo.text)
以前面動作鍊操作的網頁例,首先通過 switch_to.frame()方法切換到子 Frame 裡,再嘗試擷取父級 Frame 裡的 logo 節點(這是不能找到的),如果找不到的話,就會抛出 NoSuchEle entException 異常,異常被捕捉之後,就會輸出 NO LOGO。接下來,重新切換父級 Frame, 然後再次重新擷取節點,發現此時可以成功擷取了。
當頁面中包含子 Frame 時,如果想擷取子 Frame 中的節點,需要先調用 switch_to.frame() 方法切換到對應的 Frame,再進行操作。
延時等待
- Selenium 中, get()方法會在網頁架構加載結束後結束執行,此時如果擷取 page_source ,可能并不是浏覽器完全加載完成的頁面,某些頁面有額外的 Ajax 請求,在網頁驚代碼中也不一定能成功擷取到。是以,這裡需要延時等待一定時間,確定節點已經加載出來。
- 延時等待方式有兩種:隐式等待和顯式等待
- 隐式等待
- 使用隐式等待執行測試的時候,如果 Selenium 沒有在 DOM 中找到節點,将繼續等待,超出設定時間後(預設時間為 0 ),則抛出找不到節點的異常。示例:
用 implicitly_wait()方法實作隐式等待。from selenium import webdriver browser = webdriver.Chrome() browser.implicitly_wait(10) browser.get('https://www.zhihu.com/explore') input = browser.find_element_by_class_name('zu-top-add-question') print(input)
- 使用隐式等待執行測試的時候,如果 Selenium 沒有在 DOM 中找到節點,将繼續等待,超出設定時間後(預設時間為 0 ),則抛出找不到節點的異常。示例:
- 顯式等待
- 指定要查找的節點,然後指定一個最長等待時間。如果在規定時間内加載出來了這個節點,就傳回查找的節點;如果到了規定時間依然沒有加載出該節點, 則抛出逾時異常。示例:
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 browser = webdriver.Chrome() browser.get('https://www.taobao.com/') wait = WebDriverWait(browser,10) input = wait.until(EC.presence_of_element_located((By.ID, 'q'))) button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '.btn-search'))) print(input, button)
首先引人 WebDriverWait 對象,指定最長等待時間,調用 until()方法,傳人要等待條件 expected_conditions。如:傳入 presence_of_element_located 條件,代表節點出出現意思,參數是節點的定位元組,也就是 ID 為 q 的節點搜尋框。
效果:在 10 秒内如果 ID 為 q的節點(搜尋框)成功加載出來,就傳回該節點;如果超過 10 秒還沒有加載出來,就抛出異常。
- 對于按鈕,更改一下等待條件,如改為 element_to_be_clickable,也就是可點選,是以找按鈕時查找 CSS 選擇器為 .btn_search 的按鈕,如果 10 秒内它是可點選的,也就是成功加載,傳回這個按鈕節點;如果超過 10 秒還不可點選,就抛出異常.
- 等待條件,如判斷标題内容,判斷某個節點内是否出現了某文字。等待條件:
- title_is:标題是某内容
- title_contains: 标題包含某内容
- presence_of_element_located:節點加載出來,傳入定位元組,如(By.ID,‘q’)
- visibility_of_element_located:節點課件,傳入定位元組
- visibility_of:可見,傳入節點對象
- presence_of_all_elements_ilocated:所有節點加載出來
- text_to_be_present_in_element:某個節點文本包含某文字
- text_to_be_present_in_element_value:某個節點值包含某文字
- frame_to_be_available_and_switch_to_it:加載并切換
- invisibility_of_element_located:節點不可見
- element_to_be_clickable:節點可點選
- staleness_of:判斷一個節點是否仍在DOM,可判斷頁面是否已經重新整理
- element_to_be_selected:節點可選擇,傳節點對象
- element_located_to_be_selected:節點可選擇,傳入定位元組
- element_election_state_to_be:傳入節點對象以及狀态,相等傳回True,否則傳回False
- element_located_selection_state_to_be:傳入定位元組。以及狀态,相等傳回True,否則傳回False
- alert_is_present:是否出現警告
- 更多等待條件的參數及用法,參考官方文檔: http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.support.expected_conditions。
- 對于按鈕,更改一下等待條件,如改為 element_to_be_clickable,也就是可點選,是以找按鈕時查找 CSS 選擇器為 .btn_search 的按鈕,如果 10 秒内它是可點選的,也就是成功加載,傳回這個按鈕節點;如果超過 10 秒還不可點選,就抛出異常.
- 指定要查找的節點,然後指定一個最長等待時間。如果在規定時間内加載出來了這個節點,就傳回查找的節點;如果到了規定時間依然沒有加載出該節點, 則抛出逾時異常。示例:
前進和後退
- Selenium 可以完成前進和後退操作,它使用 back()方法後退,使用 forward()方法前進。示例:
連續通路3個頁面,調用 back()方法回到第二個頁面,再調用 forward()方法前進到第 3個頁面。import time from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.baidu.com/') browser.get('https://www.taobao.com/') browser.get('https://www.python.org/') browser.back() time.sleep(1) browser.forward() browser.close()
Cookies
- Selenium 可以友善地對 Cookies 進行 擷取、添加、删除等。示例:
通路知乎,加載完成後,浏覽器已經生成 Cookies。調用 get_cookies() 方法擷取所有的 Cookies。然後,添加一個 Cookie,傳入一個字典,有name、domain、value内容。再次擷取所有 Cookies。結果就多了新加的 Cookie。調用 delete_all_cookies()方法删除所有 Cookies。再重新擷取,結果就為空了。from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.zhihu.com/explore') print(browser.get_cookies()) browser.add_cookie({'name': 'name', 'domain': 'www.zhihu.com', 'value': 'germey'}) print(browser.get_cookies()) browser.delete_all_cookies() print(browser.get_cookies())
頁籤管理
- 通路網頁時候,會開啟一個個頁籤。Selenium 中,也可以對頁籤進行操作。示例:
首先通路百度,然後調用 execute_script()方法,傳入 window.open()這個 JavaScript 語句新開啟一個頁籤。接下來,想切換到該頁籤,調用 window_handles 屬性擷取目前開啟的所有頁籤,傳回的是頁籤的代号清單。要想切換頁籤,隻需要調用 switch_to_window()方法即可,其中參數是頁籤的代号。将第二個頁籤代号傳人,即跳轉到第二個頁籤,接下來在第二個頁籤下打開一個新頁面,然後切換回第一個頁籤重新調用 switch_to_window()方法,再執行其他操作。import time from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.baidu.com') browser.execute_script('window.open()') print(browser.window_handles) browser.switch_to_window(browser.window_handles[1]) browser.get('https://www.taobap.com') time.sleep(1) browser.switch_to_window(browser.window_handles[0]) browser.get('https://python.org')
異常處理
- 在使用 Selenium 過程中,可以使用 try except 語句來捕獲各種異常。(模拟節點未找到異常)示例:
首先打開百度頁面,嘗試選擇一個并不存在的節點 ,此時就會遇到異常。捕獲異常示例:from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.baidu.com') browser.find_element_by_id('hello')
這裡使用 try except 來捕獲各類異常。如,對 find_element_by_id()查找節點方法捕獲 NoSuchElementException 異常,一旦出現這樣的錯誤,就進行異常處理,程式也不會中斷。from selenium import webdriver from selenium.common.exceptions import TimeoutException, NoSuchElementException browser = webdriver.Chrome() try: browser.get('https://www.baidu.com') except TimeoutException: print('Timeout') try: browser.find_element_by_id('hello') except NoSuchElementException: print('No Element') finally: browser.close()
- 更多異常類,參考官方文檔 http://selenium-python.readthedocs.io/api.html#module-selenium.common.exceptions。
轉載于:https://www.cnblogs.com/Mack-Yang/p/10184662.html