[練習爬蟲codejudger](https://www.codejudger.com/) 💥😅 帳號權限過期了 來不及完成想練習的,之後再找其他可以來練習看看 ## 關於這篇文章的動機 需要練習 TQC 測驗,但平台會員會過期,想練習題目順便嘗試爬蟲效果。 ## 爬蟲遇到有登入需先過關 需要 `from selenium.webdriver.common.by import By` ```python= # 找到用戶名和密碼的輸入框,並填入資訊 username_field = driver.find_element(By.ID, "email") password_field = driver.find_element(By.ID, "password") ``` 需要 `from selenium.webdriver.common.keys import Keys` ```python= # 提交登入 # 帳號密碼 馬掉嚕(目前過期惹 QQ) username_field.send_keys("yojijunXXXXX@XXXXX.com") password_field.send_keys("199XXXX23") password_field.send_keys(Keys.RETURN) ``` - **password_field.send_keys(Keys.RETURN)** 在Selenium中,使用 `element.send_keys(Keys.RETURN)` 時,它模擬的是在該元素上,按下 "Enter" 鍵。 對於 <input> 元素(例如密碼輸入框),按下 "Enter" 鍵通常會觸發提交表單的操作。 `password_field.send_keys(Keys.RETURN)` 會自動提交登入表單,就好像你按下了登入按鈕一樣。 不用再去找 確認按鈕 的程式碼來鎖定跟動作 ## 定位元素 目的是定位網頁上的元素 需要 `from selenium.webdriver.common.by import By` ### 使用 ID: 該元素具有唯一的 ID ```python= driver.find_element(By.ID, "your_element_id") ``` ### 使用 name 屬性: 如果該元素有 name 屬性 ```ptyhon= driver.find_element(By.NAME, "your_element_name") ``` ### 使用 class 類別名稱: 這裡會說多一點,因為之前不是很明白甚至是誤會了什麼,所以踏實的記錄一下 在`HTML`中,`class`屬性中的 空格 被用來區分 不同的類別。 如果一個元素的 `class` 屬性包含多個類別,這表示該元素同時屬於這些類別。 以本次練習來說 ```html= <div class="group card"> ``` 這個` <div> `元素同時擁有 "group" 和 "card" 這兩個類別。 這不是一個 "group" 類別和一個 "card" 類別,而是一個元素同時擁有兩個不同的類別。 如果有多個元素具有相同的類別名稱,這種方法可能會找到第一個匹配的元素。 ```python= card_element= driver.find_element(By.CLASS_NAME, "card") card_element.click() ``` 也可以簡化成⬇️ ```python= driver.find_element(By.CLASS_NAME, "group").click() ``` ```python= driver.find_element(By.CLASS_NAME, "card").click() ``` 所以,使用 `driver.find_element(By.CLASS_NAME, "group card") `將無法正確選取該元素,因為 `By.CLASS_NAME` 期望單一的類別名稱,而 "group card" 被視為一個整體的類別。除非 group-card 那就是一個 要正確選擇具有 "group" 和 "card" 這兩個類別的元素,可以使用 By.CLASS_NAME 來選擇其中一個類別,或者使用 `By.CSS_SELECTOR` 並使用 .group.card 這樣的 `CSS 選擇器`,後面會提到這種用法 ### 使用 CSS 選擇器: ```python= card_element= driver.find_element(By.CSS_SELECTOR, "div.group.card > a") card_element.click() ``` 其中 `>` 拉出來紀錄一下 在 CSS 選擇器中,`>` 符號表示子選擇器,用來選擇某個元素的直接子元素。 **HTML 中有這樣的結構** ```html= <div class="group card"> <a href="/groups/2939/info">Link Text</a> </div> ``` ⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️ ```div.group.card > a``` : 表示選擇具有 "group" 和 "card" 這兩個類別的 `<div>` 元素的直接子元素是 `<a>` 元素。簡而言之,這個選擇器選擇了擁有 "group" 和 "card" 這兩個類別的 `<div>` 元素的子元素中的所有 `<a>` 元素。 不過這裡我可以寫 ```python= #第一種 card_element=driver.find_element(By.CSS_SELECTOR, "div.group.card") card_element.click() # 這樣也可以 card_element=driver.find_element(By.CSS_SELECTOR, "div.group") card_element.click() #或是簡寫成 #第二種 driver.find_element(By.CSS_SELECTOR, "div.group.card").click() # 或 driver.find_element(By.CSS_SELECTOR, "div.card").click() ``` ### 使用 XPath: 這我覺得比較難超難 肥腸難~ - html 如下 ```html! <div class="group card"><a href="/groups/2939/info"><div class="group-header p-0 false card-header"><img class="card-img-top" src="/static/media/group-6.038b1ba7.png"></div><div class="card-body"><div class="card-title h5">11210112新莊-學員-AI數據分析暨XXX用班</div><div><p>管理者:簡X峰</p><p>成員數:32</p><p>課程結束日:2023/12/31</p></div></div></a></div> ``` - 定位方法是 ```python= driver.find_element(By.XPATH, "//div[@class='group card']/a") #加上動作如下 driver.find_element(By.XPATH, "//div[@class='group card']/a").click() ``` 拆解裡面的組成 ⬆️ ⬆️ ⬆️ 1. `//`: 選擇文檔中的任何位置。 2. `div`: 選擇`<div>`元素。 1. `[@class='group card']`: 這是一個條件,表示選擇 class 屬性值為"group card"的`<div>`元素。 這是一個且(and)的關係,表示這個 `<div>` 元素同時擁有 "group" 和 "card" 這兩個類別。 3. `/` : 選擇所選擇元素的直接子元素。 4. `a` : 選擇 `<div>` 的直接子 `<a>` 元素。 💥💥💥再舉一個例子( 為了比較`@` 用途 ) 這裡狀況是我想要鎖定一整排按鈕的其中一個 "物件導向程式語言Java" 現在的 html 是這樣,有點不太會抓 ```html= --在這一包裡面 <div class="d-flex flex-wrap justify-content-center select-tqc"> <button data-select="物件導向程式語言C#" type="button" class="mr-2 mt-2 btn btn-outline-secondary">物件導向程式語言C#</button> <button data-select="物件導向程式語言Java" type="button" class="mr-2 mt-2 btn btn-outline-secondary selected">物件導向程式語言Java</button><button data-select="程式語言(第2版)" type="button" class="mr-2 mt-2 btn btn-outline-secondary">程式語言(第2版)</button> <button data-select="程式語言Python" type="button" class="mr-2 mt-2 btn btn-outline-secondary">程式語言Python</button> <button data-select="網頁資料擷取與分析 Python" type="button" class="mr-2 mt-2 btn btn-outline-secondary">網頁資料擷取與分析 Python</button> <button data-select="網頁資料擷取與分析 R" type="button" class="mr-2 mt-2 btn btn-outline-secondary">網頁資料擷取與分析 R</button> </div> --我想要定位其中的 <button data-select="物件導向程式語言Java" type="button" class="mr-2 mt-2 btn btn-outline-secondary selected">物件導向程式語言Java</button> ``` 前面已經過關進入 TQC, 接下來要選擇類型 ![截圖 2023-12-24 下午9.22.17](https://hackmd.io/_uploads/S1Vpg3HD6.png) 想要選擇 "物件導向程式語言Java" 該按鈕的程式碼為 ```html! #<button data-select="物件導向程式語言Java" type="button" class="mr-2 mt-2 btn btn-outline-secondary selected">物件導向程式語言Java</button> ``` ![截圖 2023-12-22 下午3.42.43](https://hackmd.io/_uploads/SJ7zA3MP6.png) 抓定位 ⬇️⬇️⬇️ ```python= tqc_java_test= driver.find_element(By.XPATH, "//button[text()='物件導向程式語言Java']") time.sleep(5) tqc_java_test.click() time.sleep(5) ``` 1. `//button` : 選擇文檔中所有的 `<button>` 元素。 2. `[text()='物件導向程式語言Java']`: 選擇那些文本內容(在 `<button>` 元素中)等於 "物件導向程式語言Java" 的 `<button>` 元素。 3. `[@class='group card']`: 這是一個條件,表示選擇 class 屬性值為"group card"的`<div>`元素。 這是一個且 (and) 的關係,表示這個 `<div>` 元素同時擁有 "group" 和 "card" 這兩個類別。 --- 1. `//button` : 選擇文檔中所有的 <button> 元素。 1. `[text()='物件導向程式語言Java']`: 選擇那些文本內容(在 `<button>` 元素中)等於 "物件導向程式語言 Java" 的 `<button>` 元素。 這樣的 XPath 表達式可以精確地定位包含指定文本的 `<button>` 元素。這是在使用 Selenium 中 find_element 方法時,指定 By.XPATH 來找到符合條件的元素。 ## 卡片式(點擊卡片) 目前檢查看到程式碼是 ```html! <div class="group card"><a href="/groups/2939/info"><div class="group-header p-0 false card-header"><img class="card-img-top" src="/static/media/group-6.038b1ba7.png"></div><div class="card-body"><div class="card-title h5">11210112新莊-學員-AI數據分析暨技術應用班</div><div><p>管理者:簡志峰</p><p>成員數:32</p><p>課程結束日:2023/12/31</p></div></div></a></div> ``` 要選到這個卡片有幾種做法 其中順便說說我誤會的寫法 顯然就是對前端 基本 html 不靈活也不熟悉 QQ ![卡片式選單](https://hackmd.io/_uploads/Bk2_TBbw6.png) 透過上面程式碼,我要鎖定卡片我以為會是這樣 ⬇️ 依據原始碼: ```html= <div class="group card"> ``` 我的想法: ❌❌❌❌❌❌❌❌ ```python= card_element= driver.find_element(By.CLASS_NAME, "group card") card_element.click() ``` 之類的 首先 先修正觀念 class > HTML中的 class 屬性 可以包含多個類別。 > 一個元素可以同時擁有多個不同的類別,這些類別 用空格分隔。 > 這樣的多類別機制允許在不同的元素上應用相同的樣式,更具靈活性。 例如: ```html= <div class="header banner"> <!-- content here --> </div> <div class="sidebar"> <!-- content here --> </div> ``` 第一個 `<div>` 元素有兩個類別:"header" 和 "banner"。 允許在樣式表中使用這兩個類別進行樣式設置,例如: ```css= .header { background-color: #333; color: white; } .banner { font-size: 20px; } ``` > 在JavaScript和Selenium等自動化測試工具中, > 可以使用這些類別來定位和操作元素,使得測試代碼更容易管理。 > 這樣的設計方式使得樣式能夠更模組化和可重用, 並且不同的元素可以共享相同的樣式。這在設計和維護大型網站時非常有用。 所以回到前面 `<div class="group card">` `CLASS_NAME` 並不是名字叫做"group card",而是有兩種類別 ,分別叫做 group 跟 card 利用卡片這裡來 試看看 幾個應用 ```python= # 點選課堂卡片(目前只有一種)定位元素 以下有各種方式 # 成功可運行的寫法 card_element = driver.find_element(By.CSS_SELECTOR, "div.group") card_element.click() card_element = driver.find_element(By.CSS_SELECTOR, "div.group.card") card_element.click() driver.find_element(By.CSS_SELECTOR, "div.group.card").click() card_element = driver.find_element(By.CLASS_NAME, "card") card_element.click() driver.find_element(By.CLASS_NAME, "card").click() driver.find_element(By.XPATH, "//div[@class ='group card']/a").click() ------------------------------------------------------- # 下面是 無法運行的哦! driver.find_element(By.XPATH, "//div[@class ='card']/a").click() driver.find_element(By.XPATH, "//div[class ='card']/a").click() driver.find_element(By.XPATH, "//div[class ='group']/a").click() driver.find_element(By.XPATH, "//div[class ='group card']/a").click() ``` ## 題外話 之前chatGPT 提供我 element = driver.find_element(By.CSS_SELECTOR, "li > a[text()='TQC+ 題庫']") 但沒有反應 目前還沒找到可以成功的 利用> 的部分 很棒的教學 https://www.cnblogs.com/Hang666/articles/17471186.html ## 點選 TQC 過關 ```python= #<li class=""><a href="/groups/2939/tqc">TQC+ 題庫</a></li> # 成功寫法 tqc = driver.find_element(By.XPATH, "//a[text()='TQC+ 題庫']") 或 tqc = driver.find_element(By.XPATH, "//li/a[text()='TQC+ 題庫']") 或 driver.find_element(By.XPATH, "//li/a[text()='TQC+ 題庫']").click() time.sleep(3) tqc.click() ``` ``` # 抓題目文字 標題 <h1 class="header-title">11210112新莊-學員-AI數據分析暨技術應用班</h1> # 題目內文 # 存成檔案 檔名需要考慮嗎? # 補上try catch # current_url = driver.current_url # if "/groups/2939/tqc" in current_url: # print("成功導航到目標網址") ```