# [ Python ] Selenium 模擬人類逛網站 Uniqlo
動態網頁爬蟲
<span style="font-size:10px"> 2023 ccClub 期末專案筆記整理 若有錯誤歡迎留言 </span>
## 目標功能:
> - 自動輸入 **user_tracking_dict.json** 裡的商品代號,按**搜尋鍵**,送出搜尋
> - ==在**搜尋結果**頁面抓出**全部搜尋商品**==
> - 將個別商品名稱、價格、是否特價判斷,儲存到 **products_dict.json**
> - 特價商品自動通知,加入 **push_dict.json**
## 安裝 Selenium 套件
平常應該是 `pip install` 但我的電腦需指定 python3 => `pip3` ,可以依照自己電腦環境修正
```shell=
pip3 install selenium
```
## Download Webdriver
根據你想打開的瀏覽器不同,而下載不一樣的 driver
webdriver 會打開瀏覽器,來執行程式裡需要操作的動作
以下操作使用 [ChromeDriver](https://chromedriver.chromium.org/downloads)
在瀏覽器最右邊的三個點 > 說明 > 關於 Google Chrome,可已看到目前所使用的瀏覽器版本
**請下載和自己瀏覽器版本相符的 driver**

==注意:chromedriver 執行檔需放進專案資料夾裡==
## 開啟 Web Driver 服務
```python=
# main.py
# import 相關套件
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
# 打開 webdriver
# (在 Finder 中選取 chromedriver 後,視窗最下面會顯示路徑)
chromedriver_path = "chromedriver 絕對路徑"
service = Service(chromedriver_path)
driver = webdriver.Chrome(service=service)
# 開啟想去的網站
driver.get(url)
```
看更多瀏覽器開啟功能設定:[selenium.dev](https://www.selenium.dev/documentation/webdriver/browsers/chrome/)
## Find_element() 找到想要的網頁元素,以 [Uniqlo](https://www.uniqlo.com/tw/zh_TW) 網站為例
電商的網站使用的很習慣,很少思考到一個網頁是由成千上萬的元素組成,從標題、每一行的文字、不同區塊的呈現,幾乎是看得到、按得到,試想,如果你看到一個有趣的商品卻不能按,或是一定要對準文字才按的到,是不是會覺得這個網站很爛?(好嚴格)
### 簡單介紹:觀察網頁元素
打開 **開發人員工具**
檢視 > 開發人員選項 > 開發人員工具
mac 快捷鍵: option + command + i

:::warning
使用觀察元素 + [Selenium finders](https://www.selenium.dev/documentation/webdriver/elements/finders/)
讓你看得到,抓得到
:::
### 定位元素(DOM element)
根據不同 [Locators](https://www.selenium.dev/documentation/webdriver/elements/locators/) 來拿到想要的元素
`find_element()` => 找到**第一個**符合的 element
`find_elements()` => 找到**全部**符合的 elements
以下示範透過 **CLASS_NAME** 和 **CSS_SELECTOR** 這兩種 locators 來篩選
```python=
# import By module
from selenium.webdriver.common.by import By
# 進入網站
url = "https://www.uniqlo.com/tw/zh_TW"
driver.get(url_uq)
# 搜尋的框框
search = driver.find_element(By.CSS_SELECTOR, ".h-search-box input")
# 搜尋的按鈕
search_icon = driver.find_element(By.CLASS_NAME, "icon-search")
search.clear() # 清除框框(以免有預設值)
search.send_keys(six_letter_product_code) # 輸入要搜尋的關鍵字(這邊放商品代號)
search_icon.click() # 點擊送出
# 定位元素,放到 product_elements 裡
product_elements = driver.find_elements(By.CSS_SELECTOR, ".product-ul .product-li")
```
## `WebDriverWait()` 應用
#### 為什麼抓到的元素是空的?或跑出錯誤?
很有可能是因為網頁送出後,框架出來了,但內容還在跑,馬上進行下一步時,什麼也定位不到,這時候如果又想使用資料內容去做下一個動作,很容易報錯。
又或者,設定固定等待時間,但是隨著電腦功能不同,假設設定20秒,也可能跑超過或是造成時間的浪費,不是很理想的設定。
**[WebDriverWait()](https://selenium-python.readthedocs.io/waits.html#:~:text=locate%20an%20element.-,Expected%20Conditions,-There%20are%20some)**
設定 **預期的元素(Expected Conditions)** 出現之後,才繼續進行下一步,可以去[官網](https://selenium-python.readthedocs.io/waits.html)看各種等待的詳細介紹
```python=
# 載入模組
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 等到 class name = "product-ul" 出現時才進行下一步,最多等 10 秒
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CLASS_NAME, "product-ul")))
# 確定載入搜尋商品後,定位元素,放到 product_elements 裡
product_elements = driver.find_elements(By.CSS_SELECTOR, ".product-ul .product-li")
# 用 for loop 將 product_elements 裡的標題、價格等等取出
for product_element in product_elements:
# 商品網址
a_tag = product_element.find_element(By.CLASS_NAME, "product-herf")
href_value = a_tag.get_attribute('href')
# 商品名稱
product_name_pTags = product_element.find_elements(By.CSS_SELECTOR, ".h-product .product-herf .product-content p")
product_name = product_name_pTags[1].text
product_price_element = product_element.find_element(By.CSS_SELECTOR, ".h-product .product-herf .product-content .h-currency[style*='color']")
# 判斷特價或原價
# 每一個網站都不一樣,隨著時間會更新改版,如果要一直使用,必須定期確認是否抓得到
style_attribute_value = product_price_element.get_attribute("style")
color_match = re.search(r'color:\s*([^;]+)', style_attribute_value)
price_color = color_match.group(1)
if price_color == "red":
price_state = "特價中"
else:
price_state = "原價"
# 商品價格
product_price_text = product_price_element.text[3:]
product_price = int(product_price_text.replace(",", ""))
# 輸出,或是後面處理後儲存資料
print(six_letter_product_code, product_price, price_state)
# 記得關掉 Driver
driver.quit()
```
</br>
## 參考資料
- selenium-python.readthedocs.io
- [selenium.dev](https://www.selenium.dev/)
</br>
</br>
</br>
</br>
</br>