# [ 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** ![截圖 2024-01-12 上午10.44.31](https://hackmd.io/_uploads/rJAX_Q0dT.png) ==注意: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 ![ 11111](https://hackmd.io/_uploads/HJKJWEAOa.jpg) :::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>