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

利用Selenium 能夠驅動瀏覽器執行特定的動做,如點擊、下拉等操做, 同時還能夠獲取瀏覽器當前呈現的頁面的源代碼 ,作到可見便可爬。php

  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 和網頁源代碼.
      css

    • 若是用 Selenium 來驅動瀏覽器加載網頁的話,就能夠直接拿到 JavaScript 渲染的結果,不用擔憂使用的是什麼加密系統。聲明瀏覽器對象  html

  2. 聲明瀏覽器對象
    • 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 對象,讓其執行各個動做以模擬瀏覽器操做python

  3. 訪問頁面
    • 用 get()方法來請求網頁,參數傳入連接 URL 便可。如用 get()方法訪問淘寶, 而後輸出源代碼,代碼:
      from selenium import webdriver
      browser = webdriver.Chrome()
      browser.get('https://www.taobao.com/')
      print(browser.page_source)

      彈出了 Chrome 瀏覽器而且自動訪問了淘寶,而後控制檯輸出了淘寶頁面的源代碼, 隨後瀏覽器關閉。能夠實現瀏覽器的驅動並獲取網頁源碼jquery

  4. 查找節點
    • 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 獲取,它們返回的結果徹底一致。web

      • 列出全部獲取單個節點的方法:
        • 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()

        查找方式的功能與上面列舉函數一致,參數更加靈活。api

    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')  
  5. 節點互交
    • 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 ()方法完成搜索動做。cookie

    • 更多的操做參見官方文檔交互動做介紹:http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.remote.webelement。框架

  6. 動做鏈
    • 動做鏈 沒有特定的執行對象,如:鼠標拖曳、鍵盤按鍵等。
    • 如:如今實現一個節點的拖曳操做,將某個節點從一處拖曳到另一處,實現代碼:
      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
  7. 執行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")')
  8. 獲取節點信息
    • 經過 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 屬性來獲取對應的屬性值

  9. 切換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,再進行操做。  
  10. 延時等待
    • 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。
  11. 前進和後退
    • 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個頁面。

  12. 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。再從新獲取,結果就爲空了。

  13. 選項卡管理
    • 訪問網頁時候,會開啓一個個選項卡。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()方法,再執行其餘操做。

  14. 異常處理
    • 在使用 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。
相關文章
相關標籤/搜索