Python無頭爬蟲Selenium系列(02):等待機制


Python無頭爬蟲Selenium系列(02):等待機制



此係列文章收錄在公眾號中:數據大宇宙 > 爬蟲-Selenium > py

轉發本文並私信我"python",即可獲得Python資料以及更多系列文章(持續更新的)

作為"數據玩家",如果手頭上沒有數據怎麼辦?當然是用代碼讓程序自動化採集數據,但是現在"爬蟲"不是那麼容易,其中最困難的即是突破網站各種反爬機制。本系列將全面講解 Python 中一個非常成熟的庫 —— selenium,並教會你如何使用它爬取網絡上所需的數據

自動化爬蟲雖然方便,但希望大家能顧及網站服務器的承受能力,不要高頻率訪問網站。並且千萬不要採集敏感數據!!否則很容易"從入門到入獄"

本系列大部分案例同時採用 selenium 與 pyppeteer 庫講解,並且有 Python 和 C# 2門語言的實現文章,詳細請到公眾號目錄中找到。

前言

使用 Selenium 控制瀏覽器進行頁面跳轉時,經常需要等待機制才能讓爬蟲繼續執行,這次我們來看看等待機制的流程,如何隨心所欲做出各種等待效果。


機制

想象一下如果是一個機器人幫你從網頁上查找某個信息,比較合理的流程是:

  • 讓機器人每隔1秒到頁面上"按規則"找一下
  • 如果找到,則通知你
  • 如果找不到,下一秒繼續
  • 如果超過10秒都找不到,通知你

Selenium 的等待機制同樣如此,而上述機制中唯一可以變化的就是"查找規則",這體現為 wait.until 的第一個參數接受一個"可調用對象"


終於得到你

這次案例的網頁是我簡單創建的,啟動網站服務如下(jupyter notebook 為例子):

Python無頭爬蟲Selenium系列(02):等待機制

  • 打開 web_run.ipynb 文件
Python無頭爬蟲Selenium系列(02):等待機制

  • 執行第一個 cell 的代碼,直到下方出現"serving at port 8081"
Python無頭爬蟲Selenium系列(02):等待機制

  • 打開瀏覽器頁,輸入 "localhost:8081/web_sp" 出現頁面
  • 點擊頁面上的按鈕,下方出現新文本

現在用代碼對這個頁面採集,看看網頁內容結構。

用"開發者工具",查看元素的標籤:

Python無頭爬蟲Selenium系列(02):等待機制

  • 每個新增的內容為一個 div 標籤,屬性 class 都是 "content"

現在用代碼控制 Selenium ,找上述的 div 標籤。

首先導入包:

Python無頭爬蟲Selenium系列(02):等待機制

from selenium import webdriverimport selenium.webdriver.support.wait as WA

主要代碼如下:

Python無頭爬蟲Selenium系列(02):等待機制

driver = webdriver.Chrome()driver.get('http://localhost:8081/web_sp/')wait = WA.WebDriverWait(driver, poll_frequency=0.5, timeout=10)ct = wait.until(lambda w: w.find_element_by_css_selector('div.content'))ct.text
  • 行4:定義 WebDriverWait
  • 第一個參數傳入 driver
  • 參數 poll_frequency=0.5 是每 0.5 秒執行一次查找
  • 參數 timeout=10 是 10 秒都沒有找到任何東西,就超時錯誤
  • 行5:調用 wait.until 方法,參數就一個,傳入一個"可調用對象"(此處是一個 lambda),wait 對象會每隔 0.5 秒執行一次這個方法
  • css 選擇器 "div.content" 相當於 "div[class=content]"
  • 行6:打印一下找到的文本

現在執行這個代碼,如下:

Python無頭爬蟲Selenium系列(02):等待機制

  • 一開始,你會發現代碼被卡住,其實是卡在行5的代碼上
  • 因為此時瀏覽器上一直沒有找到 class 屬性為 "content" 的標籤

大概 10 秒後,代碼執行結束,報了一個錯誤:

Python無頭爬蟲Selenium系列(02):等待機制

  • 行5 中,wait.until 中的 lambda,大概被執行了 20 次(0.5秒一次,執行了10秒)


Python無頭爬蟲Selenium系列(02):等待機制


我們再次執行代碼,這次我們在頁面出來之後10秒內,點擊頁面上的按鈕:

Python無頭爬蟲Selenium系列(02):等待機制

  • 這次代碼執行完畢,並執行到行6,得到我們要的結果

等你 n 次

瞭解這個機制,我們可以很靈活定製屬於自己的查找條件。

這次,我希望可以等新增內容到達一定次數才繼續執行後續的操作。

首先,我們要知道一點,傳入 wait.until 的方法是有限制的,必須只有一個參數(此參數實際為 driver)。

但是,我們希望自定義函數能夠靈活一點,可以知道 css 選擇器 和 條件數量,比如:

def finds_by_count(css_selector, num):    pass

我們可以利用嵌套函數實現:

Python無頭爬蟲Selenium系列(02):等待機制

def finds_by_count(css_selector, num):    def finds_by_count_(wd):        res = wd.find_elements_by_css_selector(css_selector)        if len(res) >= num:            return res    return finds_by_count_
  • 行2到5:符合 wait.until 參數的函數(只有一個參數)
  • 行3:使用 find_elements_by_css_selector 查找元素,此時我們可以在"下級函數"中使用"上級函數"的參數 css_selector 。此方法不管是否找到元素,都會返回一個列表(沒有找到則為空列表)
  • 行4,5:一旦找到的數量高於等於指定數量,則把找到的列表返回即可。如果沒有找到,沒有執行 return ,相當於返回 none
  • 行7:最關鍵的代碼,這是"上級方法" finds_by_count 的返回語句,把"下級方法" finds_by_count_ 返回出去。注意只是返回 finds_by_count_ ,而沒有調用他(因為方法名字後面沒有括號)


Python無頭爬蟲Selenium系列(02):等待機制


現在,試試效果了,代碼如下:

Python無頭爬蟲Selenium系列(02):等待機制

driver = webdriver.Chrome()driver.get('http://localhost:8081/web_sp/')wait = WA.WebDriverWait(driver, timeout=10)cts = wait.until(finds_by_count('div.content', 3))[c.text for c in cts]
  • 行5:wait.until 中調用"上級方法" finds_by_count ,等待 class 屬性為 content 的 div 標籤,出現3個為止

出來頁面後,如果快速點擊3下按鈕,就能看到結果:

Python無頭爬蟲Selenium系列(02):等待機制

  • 如果10秒內沒有出現3個文本,就會超時錯誤

總結

用代碼控制 selenium 最關鍵的功能就是"等待機制",我們可以用來檢測各種條件,讓代碼無縫執行。

接下來,將介紹更多等待機制的應用場景(比如中途需要賬號登錄等),敬請關注!!

私信我"python",獲取本系列文章所有相關資料和源碼


分享到:


相關文章: