歡迎來到第十天(三分之一了...),今天要用 Selenium 進行動態爬蟲,首先要先選定一個目標網站進行爬蟲。由於筆者曾經有過到聯合國工作的夢想,因此就決定爬取 UN Career 作為範例。
依舊,爬蟲的開始從觀察開始,在第一個頁面中我們會發現有五種類型的工作分類分別是:"Professional and higher categories"、"Field services"、"General services and related categories"、"National Professional Officers"、"Internship"
打開 Chrome DevTool 並點擊 "Field services" 會觀察到一個 POST 請求的產生
但往下看他的 Form Body 會發現裡面的內容多到眼花撩亂,加上他的規則較難掌握,因此在這個階段會放棄使用模擬 POST 請求的方式拿取所需內容,轉而使用 Selenium 的方式爬取內容。
由於確定策略是使用 Selenium,我們將要完成兩個功能
由於這次我們要模擬人在瀏覽器上的行為(包含點擊下一頁、點擊下一個分類等)因此必須透過 Selenium 中 get
、 find_elements_by_xpath
、click
的函數。首先比較棘手的問題是要如何模擬人為不斷的點擊下一頁,一直到整個分類的所有頁面都被爬過。透過觀察,會發現在分頁處的 <tr>
標籤有著 pager 的 class name 可以利用,因此可以透過 find_elements_by_xpath("//tr[@class='pager']//table//td/a")
的方式定位出每個分頁按鈕的 <a>
標籤。
from selenium import webdriver
driver = webdriver.Chrome(executable_path = 'Path to webdriver')
url = "https://careers.un.org/lbw/home.aspx?viewtype=SJ&exp=All&level=0&location=All&occup=0&department=All&bydate=0&occnet=0&lang=en-US"
driver.get(url)
elements = driver.find_elements_by_xpath("//tr[@class='pager']//table//td/a") # 定位出 pager
在定位出分頁欄位後,利用一個簡易的 while loop 走過每個分頁。當找出符合條件的 element 時,透過 click
的函數可以模擬點擊該 element 的行為,進而達到前往下一分頁的動作。
page = 1
indexer = 0
elements = driver.find_elements_by_xpath("//tr[@class='pager']//table//td/a")
while True:
if elements[indexer].text == str(page+1) or (elements[indexer].text == "..." and indexer>3):
driver.find_elements_by_xpath("//tr[@class='pager']//table//td/a")[indexer].click() # 模擬點擊下一分頁
elements = driver.find_elements_by_xpath("//tr[@class='pager']//table//td/a") # 重新取得新分頁的分頁列
page +=1
indexer = 0
elif indexer+1 == len(elements):
break
indexer += 1
這時候基本上我們已經完成分頁問題,但是!!人生沒有這麼容易,在你執行這套程式碼時應該會發現有時候會出現 IndexError: list index out of range
的錯誤,這是為什麼呢?
在 Selenium 的設定下,只有 get
這個方法會在整個頁面完成讀取後才往下執行下一行程式碼,而在上述程式碼中使用的 click
並不會等到頁面完成讀取才往下繼續執行,因此會發現有時候在頁面還沒完成讀取時就執行了 elements = driver.find_elements_by_xpath("//tr[@class='pager']//table//td/a")
,而此時的 elements 即為空集合,因此就會出現 IndexError,該如何避免呢?
此時要利用 WebDriverWait
和 expected_conditions
搭配,讓程式碼等待頁面完整讀取後才繼續往下執行。結合了之前的程式碼後就變成這樣
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
driver = webdriver.Chrome(executable_path = 'Path to webdriver')
url = "https://careers.un.org/lbw/home.aspx?viewtype=SJ&exp=All&level=0&location=All&occup=0&department=All&bydate=0&occnet=0&lang=en-US"
driver.get(url)
page = 1
indexer = 0
timeout = 5
elements = driver.find_elements_by_xpath("//tr[@class='pager']//table//td/a")
while True:
if elements[indexer].text == str(page+1) or (elements[indexer].text == "..." and indexer>3):
driver.find_elements_by_xpath("//tr[@class='pager']//table//td/a")[indexer].click()
element_p = EC.presence_of_element_located((By.CLASS_NAME, 'pager')) # 條件值
WebDriverWait(driver, timeout).until(element_p)
elements = driver.find_elements_by_xpath("//tr[@class='pager']//table//td/a")
page +=1
indexer = 0
elif indexer+1 == len(elements):
break
indexer += 1
到這裡就正式解決了分頁問題!剩下的部份我們明天見!