Python Selenium 動態爬蟲教學:從入門到自動化實戰
寫爬蟲寫到一半,發現目標網站的資料怎麼都抓不到?打開開發者工具一看,原來資料是透過 JavaScript 動態載入的。這時候 requests + BeautifulSoup 的組合就不夠用了,你需要的是 Selenium。
老實說,我第一次接觸 Selenium 是因為要爬一個電商網站的即時價格。那個網站用了大量的 AJAX 請求來載入商品資訊,用傳統的 HTTP 請求根本拿不到任何資料。後來用 Selenium 模擬瀏覽器操作,才順利拿到需要的數據。從那之後,Selenium 就成了我處理動態網頁的首選工具。
為什麼需要 Selenium
在討論 Selenium 之前,我們先釐清一個問題:什麼時候需要用 Selenium,什麼時候用 requests 就夠了?
如果目標網站的資料在 HTML 原始碼中就已經存在(也就是伺服器端渲染的靜態頁面),那用 requests 搭配 BeautifulSoup 就足夠了。但如果資料是透過 JavaScript 在瀏覽器端動態生成的,例如無限捲動的社群動態、點擊按鈕後才載入的內容、或是需要登入才能看到的頁面,這些情況就需要一個能執行 JavaScript 的工具——Selenium 就是這個工具。
Selenium 本質上是一個瀏覽器自動化框架。它可以啟動一個真實的瀏覽器(Chrome、Firefox 等),然後透過程式碼控制這個瀏覽器去做任何使用者能做的事:點擊按鈕、填寫表單、捲動頁面、等待元素載入。對於想要把重複性的瀏覽器操作自動化的人來說,Selenium 不只能爬資料,還能做到很多自動化的任務,就像Python 自動化寄信教學中用 SMTP 自動寄信一樣,都是讓程式替你完成繁瑣的工作。
環境安裝與 Selenium 4.x 自動管理 Driver
以前用 Selenium 最麻煩的就是 WebDriver 的管理。你需要手動下載對應瀏覽器版本的 driver,還要確保 driver 的路徑設定正確。瀏覽器一更新,driver 就可能失效,然後又要重新下載。
好消息是,從 Selenium 4.6 開始,這些問題都解決了。Selenium 現在內建了 Selenium Manager,它會自動偵測你的瀏覽器版本並下載對應的 driver。安裝方式非常簡單:
pip install selenium就這樣,不需要額外安裝 webdriver-manager 或手動下載 chromedriver。Selenium Manager 會在你第一次執行的時候自動處理所有事情。
如果你用的是 Python 虛擬環境(強烈建議),完整的安裝流程是這樣的:
python -m venv venv
source venv/bin/activate # Windows 用 venv\Scripts\activate
pip install seleniumWebDriver 初始化與基本設定
安裝完成後,來寫第一段 Selenium 程式。最基本的用法是啟動一個 Chrome 瀏覽器並開啟一個網頁:
from selenium import webdriver
driver = webdriver.Chrome()
driver.get("https://example.com")
print(driver.title)
driver.quit()這段程式碼會啟動一個 Chrome 視窗,導航到 example.com,印出頁面標題,然後關閉瀏覽器。很直覺對吧?
在實際使用中,你通常會想要加一些選項來自訂瀏覽器的行為:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--window-size=1920,1080")
options.add_argument("--disable-notifications")
options.add_argument("--lang=zh-TW")
driver = webdriver.Chrome(options=options)
driver.get("https://example.com")這些選項可以設定視窗大小、關閉通知彈窗、設定語言等。根據你的需求,可以加入更多的選項來模擬不同的瀏覽器環境。
元素定位方式完整介紹
Selenium 最核心的能力就是找到頁面上的元素並與之互動。它提供了多種定位方式,每種都有適合的使用場景:
from selenium.webdriver.common.by import By
# 透過 ID 定位(最可靠的方式)
element = driver.find_element(By.ID, "search-input")
# 透過 Class Name 定位
elements = driver.find_elements(By.CLASS_NAME, "product-card")
# 透過 CSS Selector 定位(最靈活)
element = driver.find_element(By.CSS_SELECTOR, "div.container > ul > li:first-child")
# 透過 XPath 定位(最強大)
element = driver.find_element(By.XPATH, "//div[@class=price]/span")
# 透過連結文字定位
element = driver.find_element(By.LINK_TEXT, "下一頁")
# 透過 Name 屬性定位
element = driver.find_element(By.NAME, "username")我個人最常用的是 CSS Selector 和 XPath。CSS Selector 語法簡潔,適合大部分情況。XPath 更為強大,可以做到一些 CSS Selector 做不到的事情,例如透過文字內容定位元素,或是往上層尋找父元素。
一個實用的技巧:在 Chrome 開發者工具中,對著任何元素按右鍵,選擇「Copy」然後選「Copy selector」或「Copy XPath」,就能直接取得該元素的定位路徑。不過自動生成的路徑通常很長,建議手動精簡一下。
等待機制:Implicit Wait 與 Explicit Wait
動態網頁最大的挑戰就是時機問題——你的程式執行速度比瀏覽器載入速度快得多。如果元素還沒載入就嘗試操作,程式就會報錯。Selenium 提供了兩種等待機制來解決這個問題。
Implicit Wait(隱式等待):設定一次,全域生效。當 Selenium 找不到元素時,會等待指定的秒數再嘗試。
driver.implicitly_wait(10) # 最多等 10 秒Explicit Wait(顯式等待):針對特定條件等待,更加精確。搭配 WebDriverWait 和 expected_conditions 使用:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 等待元素可被點擊
button = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, ".submit-btn"))
)
# 等待元素出現在 DOM 中
element = WebDriverWait(driver, 15).until(
EC.presence_of_element_located((By.ID, "result"))
)
# 等待元素可見
element = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.CLASS_NAME, "data-table"))
)我的建議是:用 Implicit Wait 設定一個基本的等待時間,然後在關鍵的互動步驟使用 Explicit Wait。不要把 Implicit Wait 設得太長,否則當元素真的不存在時,程式會等很久才報錯。
常用操作:點擊、輸入與捲動
找到元素後,最常見的操作就是點擊、輸入文字和捲動頁面:
# 點擊按鈕
driver.find_element(By.CSS_SELECTOR, ".load-more").click()
# 清除輸入框並輸入文字
search_box = driver.find_element(By.ID, "search")
search_box.clear()
search_box.send_keys("Python 教學")
# 按下 Enter 鍵
from selenium.webdriver.common.keys import Keys
search_box.send_keys(Keys.ENTER)
# 捲動到頁面底部
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
# 捲動到特定元素
element = driver.find_element(By.ID, "footer")
driver.execute_script("arguments[0].scrollIntoView();", element)捲動操作特別重要,因為很多網站使用「無限捲動」或「懶加載」的方式載入內容。你必須先捲動到對應的位置,內容才會被載入到 DOM 中。
Headless 模式:無頭瀏覽器
在開發階段,看到瀏覽器畫面有助於除錯。但在正式執行或部署到伺服器時,你通常不需要(也無法)顯示瀏覽器視窗。這時候就要用 Headless 模式:
options = Options()
options.add_argument("--headless=new")
options.add_argument("--window-size=1920,1080")
options.add_argument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
driver = webdriver.Chrome(options=options)注意幾個細節:用 --headless=new 而不是舊的 --headless,新版的 headless 模式行為更接近正常瀏覽器。即使是 headless 模式也要設定 window-size,否則預設的視窗大小可能會影響頁面的渲染結果。設定 user-agent 可以讓你的請求看起來更像正常的瀏覽器訪問。
實戰範例:爬取動態載入的電商價格
來看一個完整的實戰範例。假設我們要爬取一個電商網站的商品價格,這些商品資訊是透過 AJAX 動態載入的:
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
from selenium.webdriver.chrome.options import Options
import time
import csv
def scrape_products(url, max_pages=5):
options = Options()
options.add_argument("--headless=new")
options.add_argument("--window-size=1920,1080")
driver = webdriver.Chrome(options=options)
products = []
try:
driver.get(url)
for page in range(max_pages):
# 等待商品卡片載入
WebDriverWait(driver, 10).until(
EC.presence_of_all_elements_located(
(By.CSS_SELECTOR, ".product-card")
)
)
# 抓取所有商品資訊
cards = driver.find_elements(By.CSS_SELECTOR, ".product-card")
for card in cards:
name = card.find_element(By.CSS_SELECTOR, ".product-name").text
price = card.find_element(By.CSS_SELECTOR, ".product-price").text
products.append({"name": name, "price": price})
# 嘗試點擊下一頁
try:
next_btn = driver.find_element(By.CSS_SELECTOR, ".next-page")
if "disabled" in next_btn.get_attribute("class"):
break
next_btn.click()
time.sleep(2)
except:
break
finally:
driver.quit()
return products這個範例展示了幾個重要的概念:使用 try/finally 確保瀏覽器一定會被關閉、用 WebDriverWait 等待動態內容載入、翻頁邏輯的處理、以及錯誤處理。抓到的資料之後可以用Python Matplotlib 視覺化教學中的方法來做圖表分析。
搭配 BeautifulSoup 分析 HTML
Selenium 的元素定位功能雖然強大,但在處理大量 HTML 解析時,BeautifulSoup 的速度更快、語法更簡潔。最佳做法是用 Selenium 負責載入頁面和執行動態操作,然後將頁面原始碼交給 BeautifulSoup 解析:
from bs4 import BeautifulSoup
# Selenium 載入頁面後
page_source = driver.page_source
soup = BeautifulSoup(page_source, "html.parser")
# 用 BeautifulSoup 解析
titles = soup.select(".product-title")
for title in titles:
print(title.get_text(strip=True))這種組合的好處是:Selenium 處理動態載入和互動,BeautifulSoup 處理 HTML 解析。兩者各司其職,效率最高。
排程自動化執行
爬蟲寫好了,接下來就是讓它定期自動執行。在 macOS 或 Linux 上,最簡單的方式是用 crontab:
# 每天早上 9 點執行
0 9 * * * /path/to/venv/bin/python /path/to/scraper.py在 Windows 上則可以用工作排程器(Task Scheduler)。如果你想要更進階的排程管理,可以考慮用 Python 的 APScheduler 套件或 Celery。
另外,如果你的爬蟲需要跟其他自動化流程串接,例如爬完數據後自動寄送報告,可以參考Python 自動化寄信教學來實現完整的自動化工作流程。進階的自動化場景甚至可以結合LangChain Agent 開發入門中的技術,讓 AI 來判斷爬取的數據並做出決策。
常見問題與除錯技巧
在實際開發中,你一定會遇到各種問題。以下是我整理的常見問題和解決方式:
元素找不到(NoSuchElementException):最常見的原因是元素還沒載入。加上適當的等待機制通常能解決。也可能是元素在 iframe 中,需要先用 driver.switch_to.frame() 切換到對應的 iframe。
元素無法點擊(ElementClickInterceptedException):通常是因為有其他元素(例如彈窗、cookie 同意條)擋在前面。用 JavaScript 直接點擊可以繞過這個問題:driver.execute_script("arguments[0].click();", element)
被網站偵測到是機器人:可以嘗試設定 user-agent、加入隨機的等待時間、模擬人類的滑鼠移動和捲動行為。但要注意,爬蟲行為應該遵守網站的 robots.txt 規範和使用條款。
記憶體不斷增長:長時間運行的爬蟲可能會因為瀏覽器的記憶體洩漏而越來越慢。定期重啟 driver 是一個有效的解決方式——每爬完一定數量的頁面就 quit() 再重新建立。
結語
Selenium 是處理動態網頁爬蟲的強大工具,但它不是萬能的。對於簡單的靜態頁面,用 requests 更輕量高效。對於需要高效能的大規模爬取,可能需要考慮 Playwright 或 Scrapy。選擇工具的關鍵是根據實際需求,而不是一味追求功能最強大的方案。
不管你是用 Selenium 來爬資料、做自動化測試、還是建立 RPA 流程,掌握元素定位、等待機制和錯誤處理這三個核心概念,就足以應對大部分的使用場景了。動手做幾個專案,你很快就能上手。
繼續閱讀
Python 排程自動化教學:用 APScheduler 打造定時任務系統
相關文章
你可能也喜歡
探索其他領域的精選好文
DaVinci Resolve 免費影片剪輯入門教學:從安裝到完成第一支影片
DaVinci Resolve 是好萊塢等級的剪輯軟體,但免費版就能滿足 90% 的需求。這篇帶你從安裝開始,一步步完成第一支影片。
LangChain vs LlamaIndex 完整比較:2026 年 RAG 框架到底該怎麼選?
在 RAG 應用開發中,LangChain 和 LlamaIndex 是最常被拿來比較的兩大框架。這篇文章從架構設計、效能數據到實戰經驗,幫你釐清到底該選哪一個。
Google SGE 對 SEO 的影響:2026 年你必須知道的因應策略
Google AI Overview 已經出現在將近一半的搜尋結果中。SEO 不會死,但規則正在改變。這篇整理最新數據和五個你現在就該開始做的因應策略。
Redis 快取策略教學:Cache-Aside、Write-Through 到實戰踩坑全紀錄
快取不是 set/get 那麼簡單。這篇從 Cache-Aside、Write-Through 到 Write-Behind,帶你理解每種策略的取捨,加上我踩過的坑,幫你少走彎路。