# 動態網頁爬蟲-使用Selenium ###### tags: `python` `selenium` `crawler` ## 介紹 #### 動態網頁 動態網頁顧名思義,指的就是網頁內容是動態的,每次瀏覽同一個網頁的內容可能都不同,例如: 書本的搜尋,匯率,新聞等等,根據使用者的選擇或輸入不同的互動,動態的產生網頁內容。 #### 客戶端動態網頁 產生有兩種方式: - 從網頁伺服器下載JavaScript程式碼,在客戶端執行後產生內容。 - 透過客戶端的需求,在伺服器端產生內容後,以AJAX(Asynchronous JavaScript And XML)技術下載到客戶端。 #### 伺服器端動態網頁 在網頁伺服器內以伺服器程式語言(如: PHP,ASP.NET或JSP等等)執行後來動態產生網頁後再回應給客戶端。 #### Selenium 對於爬蟲來說,如果爬取動態網頁內容,只會得到一堆JavaScript程式碼,因為網頁內容需要透過執行JavaScript程式碼後產生,所以對於動態網頁,爬蟲會看不到執行後的結果,所以無法使用Beautiful Soup或lxml來爬取資料。 Selenim為一個跨平台的自動瀏覽器(Automates Browsers),其原本是用來做為Web應用程式的軟體測試框架,本身為開放原始碼,用在爬蟲上,可以做為模擬使用者輸入內容,點擊連結來與網頁作互動。 #### Selenium套件 Selenium為數個元件組合而成的一個套件,其元件有: - Selenium IDE: 一套建立Selenium測試的整合開發環境,附屬在Chrome或Firefox瀏覽器內,可以錄製,編輯和除錯建立的Selenium測試。 - Selenium Client API: 可以直接使用程式語言來建立Selenium測試,支援: Java,Ruby,JavaScript,C#和Pythonx來跟Selenium WebDriver通訊。 - Selenium WebDriver: 接收來自Selenium API的命令來控制瀏覽器,支援Chrome,Firefox,Safari,Microsoft Edge ,IE等瀏覽器。 ## 安裝Selenium 對Python來說,需要安裝有個,一個是Selenium Client API(也就是Python模組),另一個就是WebDriver,。 #### 安裝Selenium Client API ``` pip3 install selenium ``` #### ~~安裝WebDriver~~ ##### 步驟一: 下載 https://www.selenium.dev/documentation/webdriver/getting_started/install_drivers/ > **備註:** > > 1. Selenium 4.6版(包含)以後不需要再下載driver了。 > 2. Selenium 4.5版以前請下載對應瀏覽器的WebDriver > **注意:** > - WebDriver並非下載最新版本就好,而是下載的版本必須和你的瀏覽器版本一樣,例如你的Chrome瀏覽器是106版,那WebDriver就要下載106版的。 > Windows作業系統請下載chromedriver_win32.zip即可。 ##### 解壓縮 將下載的zip檔案解壓縮到你的Python程式目錄下。 ## Selenium基本操作 引入selenium的webdriver物件 ``` from selenium import webdriver ``` 初始化WebDriver ``` driver = webdriver.Chrome('chromedriver') ``` > **注意:** > > 如果是在MacOS下,如果和Python放在同一層目錄,需要額外加上代表目前路徑的`./`符號,如: > > ``` > driver = webdriver.Chrome('./chromedriver') > ``` 設定等待網頁載入的時間,如果提早載入完成,會提早結束等待,如果時間不夠,讀到的網頁內容會有殘缺: ``` driver.implicitly_wait(10) ``` 連線到網站: ``` driver.get('https://rate.bot.com.tw/xrt?Lang=zh-TW') ``` 印出網頁標題: ``` print(driver.title) ``` 取得網頁內容並印出: ``` html = driver.page_source print(html) ``` 也可以利用BeautifulSoup來分析網頁內容: ``` soup = BeautifulSoup(driver.page_source, 'lxml') print(soup.prettify()) ``` 將取得的網頁內容存到本地端: ``` with open('index.html', 'w', encoding='utf-8',) as file: file.write(soup.prettify()) ``` 關閉WebDriver, 瀏覽器也會跟著關閉: ``` driver.quit() ``` #### 完整程式碼 ```python= from selenium import webdriver from bs4 import BeautifulSoup import requests driver = webdriver.Chrome('chromedriver') driver.implicitly_wait(10) driver.get('https://rate.bot.com.tw/xrt?Lang=zh-TW') print(driver.title) html = driver.page_source print(html) soup = BeautifulSoup(driver.page_source, 'lxml') print(soup.prettify()) with open('index.html', 'w', encoding='utf-8',) as file: file.write(soup.prettify()) driver.quit() ``` ## Selenium資料定位 Selenium有兩個系列的資料定位函式: - `find_element()`: 用來取得網頁中的第一個定位到的HTML元素。 - `find_elements()`: (名稱中多了一個s),用來取得所有網頁中定位到的元素。 這兩個函式都可以使用下面方式來定位元素: | 名稱 | 說明 | | --------------------- | ---------------------------- | | `"id"` | 使用id屬性值來定位元素 | | `"name"` | 使用name屬性值來定位元素 | | `"xpath"` | 使用XPath表達式來定位元素 | | `"link text"` | 使用超連結文字來定位元素 | | `"partial link text"` | 使用部分超連結文字來定位元素 | | `"tag name"` | 使用標籤名稱來定位元素 | | `"class name"` | 使用class屬性值來定位元素 | | `"css selector"` | 使用CSS選擇器來定位元素 | ``` ID = "id" XPATH = "xpath" LINK_TEXT = "link text" PARTIAL_LINK_TEXT = "partial link text" NAME = "name" TAG_NAME = "tag name" CLASS_NAME = "class name" CSS_SELECTOR = "css selector" ``` #### 定位台灣銀行匯率下載CSV按鈕,並模擬點擊來下載檔案 ``` from selenium import webdriver from selenium.webdriver.common.by import By from selenium.common.exceptions import NoSuchElementException from bs4 import BeautifulSoup import requests driver = webdriver.Chrome('chromedriver') driver.implicitly_wait(10) driver.get('https://rate.bot.com.tw/xrt?Lang=zh-TW') print(driver.title) html = driver.page_source try: download_csv = driver.find_element(By.LINK_TEXT, '下載 Excel (CSV) 檔') print(download_csv.tag_name) print(download_csv.get_attribute('href')) download_csv.click() except NoSuchElementException: print('無法定位') driver.quit() ``` > **備註:** > 下載的檔案名稱會跟直接從網站下載的檔名不同 #### Selenium動作函式: | 名稱 | 說明 | | ------------------- | ---------------------- | | `click()` | 點擊元素 | | `click_and_hold()` | 在元素上按住滑鼠左鍵 | | `context_click()` | 在元素上按住滑鼠右鍵 | | `double_click()` | 點擊兩次元素 | | `move_to_element()` | 移動滑鼠游標到元素中間 | | `key_up()` | 放開鍵盤的按鍵 | | `key_down()` | 按下鍵盤的按鍵 | | `perform()` | 執行所有儲存的動作 | | `send_keys()` | 送出按鍵到元素 | | `release()` | 在元素上鬆開滑鼠按鍵 | #### 執行JavaScript程式碼 ##### 使用 Selenium 滾動網頁捲軸(scrollBar) ``` driver.execute_script('window.scrollBy(0,1000)') ``` **說明:** scrollBy(x,y)中,x為必須參數,表示向右滾動的像素值;y也為必須參數,表示向下滾動的像素值 ``` driver.execute_script('window.scrollTo(0,1000)') ``` **說明:** scrollTo(x,y) 中,x為必要參數,表示要在視窗顯示區左上角顯示的x坐標;y也為必要參數,表示要在視窗顯示區左上角顯示的y坐標 ##### 捲軸拉到底 ``` driver.execute_script('window.scrollTo(0,document.body.scrollHeight)') ``` 或 ``` driver.execute_script('window.scrollTo(0,document.documentElement.scrollHeight)') ``` #### send_key()常用的按鍵代碼 `ENTER` `SHIFT` `LEFT_SHIFT` `CONTROL` `LEFT_CONTROL` `ALT` `LEFT_ALT` `SPACE` #### Selenium例外物件 | 名稱 | 說明 | | ----------------------------- | ------------------------ | | ElementNotSelectableException | 選取的該元素不允許被選取 | | ElementNotVisibleException | 元素存在,但是不可見 | | ErrorInResponseException | 伺服器端回應錯誤 | | NoSuchElementException | 選取的元素不存在 | | TimeoutException | 超過時間限制 |