[練習爬蟲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("成功導航到目標網址")
```