天天看點

Class 17 - 1 動态渲染頁面爬取 — Selenium使用

利用Selenium 可以驅動浏覽器執行特定的動作,如點選、下拉等操作, 同時還可以擷取浏覽器目前呈現的頁面的源代碼 ,做到可見即可爬。

  1. 基本使用
  • 示例:
    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()      
    運作代碼後,會自動彈出一個 Chrome 浏覽器。首先會跳轉到百度,然後在搜尋框中輸入 Python,接着跳轉到搜尋頁。  搜尋結果加載出來後,控制台分别會輸出目前的 URL 、目前的 Cookies 和網頁源代碼.
  • 如果用 Selenium 來驅動浏覽器加載網頁的話,就可以直接拿到 JavaScript 渲染的結果,不用擔心使用的是什麼加密系統。聲明浏覽器對象  

聲明浏覽器對象

  • Selenium 支援非常多浏覽器,如 Chrome/ Firefox/ Edge等,還有 Android 等手機端的浏覽器,也支援無界面浏覽器 PhantomJS。
    • 初始化方式:
      from selenium import webdriver
      browser = webdriver.Chrome()
      browser = webdriver.Firefox()
      browser = webdriver.Edge()
      browser = webdriver.PhantomJS()
      browser = webdriver.Safari()      
      浏覽器對象的初始化并将其指派為 browser對象。接下來,就是調用 browser 對象,讓其執行各個動作以模拟浏覽器操作

通路頁面

  • 用 get()方法來請求網頁,參數傳傳入連結接 URL 即可。如用 get()方法通路淘寶, 然後輸出源代碼,代碼:
    from selenium import webdriver
    browser = webdriver.Chrome()
    browser.get('https://www.taobao.com/')
    print(browser.page_source)      
    彈出了 Chrome 浏覽器并且自動通路了淘寶,然後控制台輸出了淘寶頁面的源代碼, 随後浏覽器關閉。可以實作浏覽器的驅動并擷取網頁源碼

查找節點

  • Selenium 可以驅動浏覽器完成各種操作,如填充表單、模拟點選等。Selenium 提供了系列查找節點的方法,我們可以用這些方法來擷取想要的節點,以便執行一些動作或者提取資訊。
  1. 單個節點
    • 如,要從淘寶頁面中提取搜尋框這個節點,觀察源代碼(它的 id 是 q, name 也是q。此時就可以用多種方式擷取它。如,find_element_by_name()是根據 name 值擷取,find element_by_id()是根據 id 擷取。 還有根據 XPath、css 選擇器等擷取的方式):
      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()      
      這裡使用 3 種方式擷取輸入框,分别根據 ID, CSS 選擇器和 XPath 擷取,它們傳回的結果完全一緻。
    • 列出所有擷取單個節點的方法:
      • 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()      
      查找方式的功能與上面列舉函數一緻,參數更加靈活。
  2. 多個節點
    • 如果有多個節點,用 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
        也可以直接用 find_elements()方法來選擇,可以這樣寫,結果是完全一緻的:
        •  lis = browser.find_elements(By.CSS_SELECTOR, ’.service-bd li')  

節點互交

  • Selenium 可以讓浏覽器模拟執行一些動作。常見的用法有:輸入文字時用 send_keys()方法,清空文字時用 clear()方法,點選按鈕時用 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()      
    首先驅動浏覽器打開淘寶,用 find_element_by_id()方法擷取輸入框,再用 send_keys() 方法輸入 iPhone 文字,等待秒後用 clear()方法清空輸入框,再次調用 send_keys()方法輸入 iPad 文字,之後再 find_element_by_class_name()方法擷取搜尋按鈕,最後調用 click ()方法完成搜尋動作。
  • 更多的操作參見官方文檔互動動作介紹:http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.remote.webelement。

動作鍊

  • 動作鍊 沒有特定的執行對象,如:滑鼠拖曳、鍵盤按鍵等。
  • 如:現在實作一個節點的拖曳操作,将某個節點從一處拖曳到另外一處,實作代碼:
    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      
    打開網頁中一個拖曳執行個體,依次選中要拖曳的節點和拖曳到的目标節點,接着聲明 ActionChains 對象并将其指派為 actions 變量,然後調用 actions 變量的 drag_and_drop()方法,再調 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()方法,然後傳人想要擷取的屬性名,就可以得到它的值了

  • 擷取文本值
    • 每個 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)      
  • 擷取id、位置、标簽名和大小
    • Web Element 節點還有 些其他屬性,如 id 屬性可以擷取節點 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)      
      首先獲得“提問”按鈕節點,再調用其 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 ),則抛出找不到節點的異常。示例:
      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)      
      用 implicitly_wait()方法實作隐式等待。
  • 顯式等待
    • 指定要查找的節點,然後指定一個最長等待時間。如果在規定時間内加載出來了這個節點,就傳回查找的節點;如果到了規定時間依然沒有加載出該節點, 則抛出逾時異常。示例:
      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。

前進和後退

  • Selenium 可以完成前進和後退操作,它使用 back()方法後退,使用 forward()方法前進。示例:
    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()      
    連續通路3個頁面,調用 back()方法回到第二個頁面,再調用 forward()方法前進到第 3個頁面。

Cookies

  • Selenium 可以友善地對 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())
          
    通路知乎,加載完成後,浏覽器已經生成 Cookies。調用 get_cookies() 方法擷取所有的 Cookies。然後,添加一個 Cookie,傳入一個字典,有name、domain、value内容。再次擷取所有 Cookies。結果就多了新加的 Cookie。調用 delete_all_cookies()方法删除所有 Cookies。再重新擷取,結果就為空了。

頁籤管理

  • 通路網頁時候,會開啟一個個頁籤。Selenium 中,也可以對頁籤進行操作。示例:
    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')      
    首先通路百度,然後調用 execute_script()方法,傳入 window.open()這個 JavaScript 語句新開啟一個頁籤。接下來,想切換到該頁籤,調用 window_handles 屬性擷取目前開啟的所有頁籤,傳回的是頁籤的代号清單。要想切換頁籤,隻需要調用 switch_to_window()方法即可,其中參數是頁籤的代号。将第二個頁籤代号傳人,即跳轉到第二個頁籤,接下來在第二個頁籤下打開一個新頁面,然後切換回第一個頁籤重新調用 switch_to_window()方法,再執行其他操作。

異常處理

  • 在使用 Selenium 過程中,可以使用 try except 語句來捕獲各種異常。(模拟節點未找到異常)示例:
    from selenium import webdriver
    browser = webdriver.Chrome()
    browser.get('https://www.baidu.com')
    browser.find_element_by_id('hello')      
    首先打開百度頁面,嘗試選擇一個并不存在的節點 ,此時就會遇到異常。捕獲異常示例:
    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()      
    這裡使用 try except 來捕獲各類異常。如,對 find_element_by_id()查找節點方法捕獲 NoSuchElementException 異常,一旦出現這樣的錯誤,就進行異常處理,程式也不會中斷。
  • 更多異常類,參考官方文檔 http://selenium-python.readthedocs.io/api.html#module-selenium.common.exceptions。

轉載于:https://www.cnblogs.com/Mack-Yang/p/10184662.html