## 讓爬蟲做我的股票助手
---
## 課程規劃
----
課程將分為基礎和進階兩部分
* 基礎 : 爬蟲原理、基礎 Python 和爬蟲工具介紹
* 進階 : 進階爬蟲原理、實作爬蟲程式
---
## 基礎課程規劃
----
* 爬蟲介紹
* 爬蟲是什麼 & 能做什麼 `(25 min)`
* 瀏覽網頁的流程
* HTML 是什麼
* 網頁的組成
* 怎麼取得資源的網址? `(20 min)`
* 爬蟲注意事項 `(5 min)`
* 基礎 Python `(40 min)`
* `10 min 環境+休息 / 30(3*10) min 教學+實作`
* 常用爬蟲工具 `(20 min)`
* 小實作
---
### 秀個小程式
---
### 爬蟲是什麼?
----
當我們需要定期或大量蒐集網站上的資訊...
* 每日股價
* Dcard 留言
* 職缺資訊
----
一個一個查詢
很麻煩
----
交給程式來做啊
----
那就是爬蟲
---
### 爬蟲能做什麼?
----
模擬真人瀏覽網站的行為
* 取得網站上的資料
* 送出資料給網站
----
| 真人 | 爬蟲 |
| -------- | -------- |
| 瀏覽 Dcard 上的貼文 | 取得 Dcard 上的貼文|
| 登入 Facebook | 送出帳密給 Facebook
----
想透過爬蟲程式爬取網站前
要先了解瀏覽網頁的流程
---
### 瀏覽網頁的流程
----
#### 人
1. 打開瀏覽器
2. 搜尋網站
3. 瀏覽器向目標網站發出請求 (request)
4. 伺服器回應 (response) 網頁 (HTML) 給瀏覽器
5. 瀏覽器將網頁顯示在畫面上
----

----
#### 爬蟲
1. 利用程式向目標網站發出請求 (request)
2. 伺服器回應 (response) 網頁 (HTML) 給程式
3. 拿到網頁後就可以拿來運用
----

----
瀏覽網站時會拿到網站的 HTML
那 HTML 是什麼?
---
### HTML
HyperText Markup Language
----
* 就是我們看到的網頁
* HTML 就像一個 Word 檔,可以在裡面寫入文字、排版、放圖片等,再用瀏覽器打開就會變成我們看到的網頁

----
* HTML 包含了網頁上的內容
* 爬蟲的做法就是拿到網頁的 HTML,從裡面找出資料
* ex: 從證交所網站的 HTML 中找出每日股價
----
網頁是只由一個 HTML 組成嗎?
還是有其它東西?
---
### 網頁的組成
----
* 網頁不一定只是一個 HTML,通常是由 HTML 加上如 CSS、JavaScript、資料、圖片等資源共同組成
* 這些資源都有各自對應的網址,只要對這些網址發送請求,就能取得想要的資源
----

[圖片連結](https://developers.google.com/community/gdsc/images/gdsc-social-share.png)
----
當我們瀏覽網站時
----
1. 瀏覽器連上網站
2. 向伺服器請求網站會用到的資源
3. 把回應的資源放到 HTML 裡,組合成完整網頁
4. 顯示在畫面上
----

----
所以我們瀏覽網頁時所看到的 HTML
是已經將這些資源放進去後產生的完整 HTML
----
#### 小結
網頁上的資源會有自己的網址
對這些網址發送請求可以拿到對應的資源
----
但我只知道網頁的網址
要怎麼取得其它資源的網址?
---
### 怎麼取得資源的網址?
----
開啟瀏覽器的 Network 介面
* `F12`
* 右鍵 -> 檢查
----

----
* 右上方選 Network (之後要先重新整理畫面一次)
* Network 會監測瀏覽器發出的請求與收到的回應
----

----
可以根據資源類型篩選,會更容易找到
* 
* All: 全部類型
* Fetch/XHR: 網頁資料,ex: 證交所每日股價
* Img: 圖片檔案
* Doc: HTML檔案
----
以證交所網站為例,先篩選資源類型
再從 Preview 或 Response 中查看回應資料的內容

----
* 找到資料後,從 Headers 中的 Request URL 可以得到資源對應的網址

----
* 對這個網址發送請求,就能取得台股收盤價

----
打開
https://www.twse.com.tw/zh/trading/historical/stock-day-avg.html
用 `F12` 找出 `2330台積電` 的收盤價網址
再訪問該網址,顯示收盤價在瀏覽器上
----
#### Request Method
發送請求的方法,表示你想對資源做什麼操作
----

----
常見的 Request Method
* GET: 取得資料
* POST: 送出資料,ex: 送出登入帳密
* PUT: 修改資料
* DELETE: 刪除資料
----
若 Headers 的 Request Method 是 GET,爬蟲的 Request Method 就要設為 GET

---
### 爬蟲注意事項
----
要有禮貌
----
* 對同一個網頁持續爬蟲盡量每發一次請求就停頓幾秒
* 過多的流量消耗會導致伺服器吃不消掛掉
* 短時間大量發送請求會被認為是在攻擊網站,可能被網站封鎖
----
爬蟲穩定度較低,可能會遇到一些例外的情況
----
1. 發送請求時連線失敗
* 可以根據回應的 HTTP 狀態碼或其他方式確保請求有發送成功
* 每次發送請求之後都停頓幾秒,讓伺服器有喘息時間
----
2. 網頁內容如果有更改,爬蟲程式會出現錯誤
* 可能要持續記錄網頁變化或資料的蒐集情況,才能及時去更改程式碼
* 有些網站有提供 API 的方式,發請請求可以直接拿到資料
----
3. 持續向某一網站爬蟲時,伺服器可能因效能問題,會回傳錯誤的資料
* 可以利用程式的 try catch 機制或其他方式確保回應的資料是正確的
* 每次發送請求之後都停頓幾秒,讓伺服器有喘息時間
---
### 爬蟲原理總結
----
* 爬蟲可以模擬真人瀏覽網站的行為
* 對網站的網址發送請求可以拿到網站的 HTML
* 爬蟲的做法就是拿到網站的 HTML,再從裡面找出資料
* 或是對資源的網址發送請求,直接拿到該資源 (ex: 圖片)
---
### 基礎 Python
----
### 執行環境
----

[連結](https://colab.research.google.com/?hl=zh-tw)
----
### Hello World

* 按左邊的圖標執行程式
----
### 宣告變數
```python=1
a = 1
print(a)
```
* 左邊的 `a` 是我宣告的變數
* 右邊的 `1` 是我要給 `a` 的數值
* 所以 `a` 的值為 `1`
----
### 輸入 & 輸出
```python=1
a = input()
print(a)
```
* `input()` 可以讓使用者輸入數值
* `print()` 會把裡面放的東西印出來
----
### 資料型態
常見的資料型態有`整數`、`小數`、`字串`、`list`
----
* #### 整數
* int
* 就是數學中的整數
* 可以做 `+` `-` `*` `/` `//` `%`的運算
```python=1
print(1+2*3)
print(type(1))
# output:
# 7
# int
```
----
* #### 小數
* float
* 數學中的小數
* 跟 int 用法一樣,就是多了小數點
```python = 1
print(3-2*1.2)
print(type(1.2))
# output:
# 0.6
# float
```
----
* #### 字串
* str
* 就是文字,左右要加單引號或雙引號
* `"abc"`、`"一二三"`、`"123"` 都是字串
* 可以做 `+` 和 `*`
```python=1
print("123"+"456")
print("789"*3)
print(type("你好"))
# output:
# 123456
# 789789789
# str
```
----
* #### list
* 可以裝很多種變數
* 左右邊要加`[]`
* 就像一台公車,每個變數都有自己的位置
* 透過索引值 (index) 可以找到 list 中的資料
* 索引值是從0開始
```python=1
a = [1, 2.0, "哈囉"]
print(a)
print(a[0])
print(a[2])
# output
# [1, 2.0, "哈囉"]
# 1
# 哈囉
```
----
### 條件判斷
----
* #### if & else
* `if` 可以判斷後面的條件是否成立
* 符合條件就會做接下來的事
* 判斷符號有 `==` , `>` , `<` , `>=` , `<=` , `!=`
* `if` 的下一行開始表示符合條件要做什麼
```python=1
if (1 == 1):
print("1等於1")
```
----
* #### if & else
* `else` 表示不符合條件時要做什麼
```python=1
if (1 != 1):
print("1 不等於 1")
else:
print("1 等於 1")
```
----
### 迴圈
----
分為兩種
* while
* for
----
* #### while 迴圈
* 當符合條件時,會重複做接下來的事
* 直到不符合條件時,才會停止
```python=1
a = 1
while (a < 10):
print(a)
a = a + 1
```
----
* #### for 迴圈
* 設定一個變數 (會從0開始) 和終止數字
* 迴圈每做一次該變數值就會`+1`
* 當變數值等於設定的終止數字時就會停止
* `for i in range(5)` 代表迴圈會做 5 次
```python=1
for i in range(5):
print(i)
```
---
### Python 爬蟲工具介紹
----
### Requests
* 請求工具
* 用程式發送請求,取得回應的資源
----
### BeautifulSoup
* 解析工具
* 如果 Requests 得到的回應資源是 HTML,可以用 BeautifulSoup 來解析(整理) HTML
----
### Selenium
* 動態爬蟲
* 用程式模擬真人使用瀏覽器(預設會開啟瀏覽器)
* 爬取動態網站較難找到資源網址時適合使用
---
### 小實作
----
取得網站的 HTML
----
執行下面程式,並輸入網站的網址來爬網站的 HTML
```python=1
import requests
url = input("請輸入網址:")
r = requests.get(url)
print(r.text)
```
----
爬暨大網站的 HTML

----
取得股票的收盤價
----
執行下面程式,並輸入股票代號來爬收盤價
```python=1
import requests
import datetime
stock_id = input()
date = datetime.datetime.today().strftime("%Y%m%d")
url = "https://www.twse.com.tw/rwd/zh/afterTrading/STOCK_DAY_AVG?date="+date+"&stockNo="+stock_id+"&response=json&_=1697638279385"
r = requests.get(url)
result = r.json()
print("股票 "+str(stock_id)+" 的今日收盤價為 "+result["data"][-2][1])
```
----

---
## 進階課程介紹
----
* HTML 詳細介紹 `(15 min)`
* 爬蟲工具詳細介紹 `(40 min)`
* 實作上會遇到的狀況 `(40 min)`
* 用爬蟲製作股價助手 `(55 min)`
---
### HTML 詳細介紹
看得懂 HTML 才能整理出想要的資料
----
### 標籤 & 元素
----
* HTML 透過 **標籤 `<>` (tag)** 來描述文字、超連結、圖片等內容的種類和顯示方式
* 標籤就是告訴 HTML 我要寫入的資料是哪種類型,而用標籤包起來的資料就稱為 **元素**
* 例如 `<h1>Hello</h1>` 在網頁上會顯示成大標題格式的文字
* 這裡的 `<h1>` 是標籤,而 `<h1>Hello</h1>` 是元素
----

----
常見標籤
* `<h1>` 大標題文字
* `<h2>` 比大標題小的文字,依此類推到 `<h6>`
* `<p>` 段落文字
* `<a>` 超連結文字
* `<img>` 圖片
* `<button>` 按鈕
* `<form>` 表單,ex : 登入表單
* `<input>` 輸入欄位
* `<table>` 表格。通常會搭配`<th>`表頭、`<tr>`列、`<td>`欄
* `<div>` 區塊,可以包含多個標籤
----
```html=1
<html>
<head>
<title>範例網頁</title>
</head>
<body>
<h1>Hello</h1>
<p>哈囉!</p>
</body>
</html>
```

----
Q: tag 跟爬蟲有什麼關係?
----
A: 爬蟲程式取得 HTML 後,可以透過 tag 找到資料
----
### class & id
----
網頁程式通常會透過 **tag (標籤)**、**class (類別)**、**id (識別號)** 來控制元素
爬蟲也可以用 class 和 id 來快速找到資料的位置
----
**class**
* 可以將多個元素設為同個 class,並對這個 class 的元素統一進行操作
* 就像多個學生可以是相同學系,多個元素也可以有相同的 class
----
**id**
* 為一個元素設定該元素獨有的 id,可以透過 id 直接找到這個元素
* 就像學生有自己唯一的學號,元素也可以有唯一的 id
* 若其他元素 id 設為相同會有 bug
----
```html=1
<!DOCTYPE html>
<html>
<head>
<title>範例網頁</title>
<style>
.text2 {
color: red;
}
</style>
</head>
<body>
<h1 id="text1" class="text2">Hello</h1>
<p class="text2">哈囉!</p>
</body>
<script>
document.getElementById('text1').innerHTML = 'Hello World!';
</script>
</html>
```
* 取得 id 為 text1 的元素會找到 `<h1 id="text1" class="text2">Hello</h1>` 這個元素
* 取得 class 為 text2 的元素會找到 `<h1 id="text1" class="text2">Hello</h1>` 和 `<p class="text2">哈囉!</p>` 這兩個元素
----

----
#### 小結
爬蟲程式取得 HTML 後,可以透過 tag、class、id
從 HTML 中快速找到想要的資料
---
### 爬蟲工具詳細介紹
---
### Requests
* 程式可以透過 Requests 向伺服器發送請求並回傳回應
* 安裝指令 (在本機執行的話要安裝)
`pip install requests`
* 導入程式
`import requests`
----
* 發送請求
* 發送 get 請求
* 程式在發送請求後會回傳回應結果,可以用變數來存放 (這裡的r)
`r = requests.get('網址')`
* 發送 post 請求
* 第一個位置是網址,第二個位置放要傳送的字典型態資料。ex : 登入的帳密
`r = requests.post('網址', {'key':'value'})`
* key 是名稱,value 是數值
* ex:'name' : 'user1'
----
* 印出回應的文字內容
* 如果回應是 html :
`print(r.text)`
* 如果回應是 json :
`print(r.json())`
* 印出 HTTP 狀態碼
`print(r.status_code)`
----
#### HTTP 狀態碼
一個三位數,表示一個 HTTP 請求是否已完成
`可以用狀態碼來判斷爬蟲是否成功`
----
* 分為五種類:
* 資訊回應 (Informational responses, 100–199)
* 請求已被接收,伺服器正在等待進一步操作
* 成功回應 (Successful responses, 200–299)
* 請求已成功接收
----
* 分為五種類:
* 重定向 (Redirects, 300–399)
* 客戶端需要採取進一步的操作才能完成請求
* 客戶端錯誤 (Client errors, 400–499)
* 客戶端出現了錯誤或無法完成請求
* 伺服器端錯誤 (Server errors, 500–599)
* 伺服器在處理請求時出現錯誤
----
常見狀態碼
* 200 OK : 請求成功,很棒
* 403 Forbidden : 客戶端沒有訪問權限
* 404 Not Found : 伺服器找不到對應的資源,比如使用者輸入不存在的網址
* 500 Internal Server Error : 伺服器發生錯誤
----
* 如果今天爬到一個 HTML,用 `print(r.text)` 印出網頁內容,輸出會有 HTML 的 tag,並不是乾淨的資料
* 若要取出乾淨的資料,則要透過解析工具整理,也就是接下來要介紹的 **BeautifulSoup**
---
### BeautifulSoup
透過 Requests 取得網頁 HTML 之後,通常會搭配 BeautifulSoup 來解析 HTML,找出想要的資料
----
* 安裝指令 (在本機執行的話要安裝)
`pip install BeautifulSoup4`
* 導入程式
`from bs4 import BeautifulSoup`
----
* 使用解析器解析 HTML
* 將前面 Requests 回傳的 HTML,利用 `html.parser` 這個解析器解析,並把結果存到變數 `soup` 裡
```python=1
r = requests.get('網址')
soup = BeautifulSoup(r.text, 'html.parser')
```
----
所有解析器
* 除了`html.parser`之外,還有以下這些解析器

----
* 除了 `html.parser` 是內建之外,其他解析器要透過指令安裝
* `lxml` : `pip install lxml`
* `html5lib` : `pip install html5lib`
----
開始前先複製這段程式碼
```python=1
from bs4 import BeautifulSoup
html_file = '''
<html>
<head>
<title>hello</title>
</head>
<body>
<h1 class="class1">Hello World</h1>
<p id="id1">NCNU GDSC</p>
<h2 class="class1">哈囉</h2>
<h2 class="class2">GDSC</h2>
<h2 id="id2">NCNU</h2>
</body>
</html>'''
soup = BeautifulSoup(html_file, 'html.parser')
```
----
印出排版過後的 HTML
`print(soup.prettify())`
----
解析 HTML 之後
可以用 `find` 來找出想要的資料
----
* **find**
* 以 `tag` 搜尋
* 找出**第一個**符合條件的標籤
```python
soup.find('h2')
```
* 會回傳第一個 `<h2>` 標籤
* 找出**所有**符合條件的標籤
```python
soup.find_all('h2')
```
* 會回傳包含所有 `<h2>` 標籤的 list
----
* **find**
* 以 `tag` 搜尋
* 同時搜尋多種標籤
```python
soup.find_all(['h2', 'p'])
```
* 將條件的標籤放在 list 裡
* 只要是 `<h2>` 或 `<p>` 標籤都會回傳
* 如果是 find 只會回傳第一個
* 限制 `find_all` 搜尋數量
```python
soup.find_all('h2', limit=2)
```
* 會回傳前兩個 `<h2>` 標籤的 list
* 多條件就把標籤放在 list 裡
----
* **find**
* 以 `id` 搜尋
* 找出 `id` 為 `id1` 的標籤
```python
soup.find(id='id1')
```
* 同時搜尋多個 id
```python
soup.find(id=['id1','id2'])
```
* 可以同時使用 `tag` 和 `id` 搜尋
```python
soup.find('h2', id='id2')
```
----
* **find**
* 以 `class` 搜尋
* 找出所有 `class` 為 `class1` 的標籤
```python
soup.find_all(class_='class1')
```
* 同時搜尋多個 class
```python
soup.find_all(class_=['class1','class2'])
```
* 可以同時使用 `tag` 和 `class` 搜尋
```python
soup.find_all('h2',class_='class1')
```
----
* **find**
* 同時搜尋 tag、id、class
* 找出 `tag` 為 `<p>`、`id` 為 `id1`、`class` 為 `class1`
```python
soup.find('p', id='id1', class_='class1')
```
----
* **find**
* 搜尋上層標籤
* 如果無法定位想要的標籤,但可定位到該標籤內的子標籤
* 可以用 `find_parent` 和 `find_parents` 來搜尋子標籤的父標籤
* `find_parent`、`find_parents` 跟 `find`、`find_all` 概念一樣
----
* **find**
* 搜尋上層標籤
* 如果 HTML 長這樣,想取得這個 `<p>` 標籤但無法定位,可以搜尋 `<b>` 的父節點來找到 `<p>`:
```html
<p>這是一份<b class="boldtext">HTML文件</b>。</p>
```
```python=1
child_b = soup.find('b',class_="boldtext")
parent_p = child_b.find_parent('p')
print(parent_p)
```
----
* **find**
* 只印出文字
* 前面的方法會得到完整的 HTML 標籤,若要取出文字,可以用 `getText()`
```python=1
p = soup.find('p')
print(p)
print(p.getText())
```
----
* **select**
* `select` 跟 `find` 都可以用來找出資料
* 由於時間因素,先認識一種就好
* 有興趣可以[參考連結](https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/#id42)
---
### 爬蟲時可能會發生的狀況
---
Q1: 明明網站的 HTML 上有想要的資料
但爬蟲爬下來後卻沒看到
----
可能是因為這個網站是**動態**網站
而不是**靜態**網站
----
#### 靜態網站 vs 動態網站
----
靜態網站

----
特點
* 伺服器會將資料寫在回應給瀏覽器的 HTML 上
* 資料就在 HTML 裡,只要對網站的網址發送請求就可以得到資料
* 但因為資料是在 HTML 裡,所以要自己從 HTML 整理出資料
----
例子
* https://histock.tw/stock/rank.aspx
* 打開網頁時,股票資訊就已經寫在網頁裡
* 對這個網頁發送請求,拿到 HTML 後再從 HTML 裡找出想要的資料
----
動態網站

----
特點
* 資料不是一開始就寫在 HTML 上,而是瀏覽器另外向伺服器請求該資料後再寫入
* 對網站的網址發送請求不會拿到該資料
----
特點
* 要對資料的網址發送請求 -> 在 Network 介面中尋找資料的網址並發送請求
* 因為是直接取得資料,所以整理起來比較方便
----
例子
* https://www.twse.com.tw/zh/trading/historical/stock-day-avg.html
* 輸入年、月、股票並按查詢後,才會去請求對應的資料,取得回應後再顯示在網頁上

* 只要對股價資料的網址發送請求,就能拿到股價的資料
----
怎麼分辨是靜態網站還是動態網站
----
個人做法
* Network 介面看回應的 HTML 裡有沒有想要的資料
* 用爬蟲爬 HTML 下來看有沒有想要的資料
---
Q2: 網站需要登入
----
可以透過 cookie 自動登入
----
cookie 是什麼?
----
登入 Moodle 後
在一段時間內若沒關閉瀏覽器
可以不用重新登入
----
登入成功後
伺服器會用使用者的帳密或其他資訊
產生一個時限內有效的 cookie
之後登入時,伺服器會直接驗證 cookie
若 cookie 有效,就會直接登入
----
第一次登入

----
之後登入

----
爬蟲可以將 cookie 放在 header 裡傳給網站
就能解決需要登入的問題
----
怎麼取得 cookie?
----
先看 cookie 在哪裡
----
1. `F12` 或 右鍵->檢查
2. 選 Application
3. 選 Cookies
4. 選對應的網站
----

* Name 是 cookie 名稱
* Value 是 cookie 的值
----
怎麼知道登入是看哪個 cookie?
看登入前後有哪個 cookie 出現或值有變化
----
* 登入前

----
* 登入後

----
所以在請求的 header 中放入這個值的 cookie
就能自動登入 Moodle
----
放入 cookie 範例
```python=1
url = 'https://moodle.ncnu.edu.tw/'
cookies = {'MoodleSession': 'cookie 值'}
r = requests.get(url, cookies=cookies)
```
---
Q3: 爬蟲程式連不上網站
----
* 有些網站會擋爬蟲,可能可以透過偽裝成瀏覽器解決
* 修改 Request Header 的內容,就能將爬蟲程式偽裝成瀏覽器
----
網頁伺服器可以從 Request Header 中的 User-Agent 了解使用者使用的瀏覽器

----
在 Header 的 User-Agent 中加上瀏覽器的資訊
網頁伺服器就會認為這個請求是由瀏覽器發送
而不是爬蟲程式
----
User-Agent 範例
```python=1
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
r = requests.get(url, headers=headers)
```
---
## 用爬蟲製作股票助手
----
### 目標
建立`定時`自動回報`即時股價`的`機器人`
----
### 系統架構
1. 爬蟲程式取得即時股價
2. 透過 telegram bot 傳送股價給使用者
3. gitAction 定時啟動爬蟲程式
----

----
### 實作流程
----
1. 建立 telegram bot `(10 min)`
2. 編寫爬蟲程式 `(25 min)`
3. 上傳程式到 github `(10 min)`
4. 利用 git action 定時啟動爬蟲程式 `(10 min)`
---
### 建立 telegram bot
----
* 到 [@botfather](https://t.me/BotFather) 申請 bot
* 輸入 /newbot 開始建立 bot

----
* 依序輸入 bot 的名稱和 username
* 最後記下 BotFather 回傳的 token

----

----

---
### 編寫爬蟲程式
----
* 目標
1. 爬取股票
2. 透過機器人回報
----
* 實作流程
* 確認網站資料怎麼取得
* 編寫爬蟲程式
----
* 確認網站資料怎麼取得
1. 觀察 [yahoo股市](https://tw.stock.yahoo.com/quote/2330.TW) 網站上,股價資料的網址規則
2. 發現網址會包含`股票編號`,可以用網址找到不同股票

----
3. 觀察股價的 HTML

* `tag` 是 `span`
* 跌的話 `class` 是 `Fz(32px) Fw(b) Lh(1) Mend(16px) D(f) Ai(c) C($c-trend-down)`
----
4. 程式要爬到漲、跌、持平的股價
* 漲: `Fz(32px) Fw(b) Lh(1) Mend(16px) D(f) Ai(c) C($c-trend-up)`
* 持平: `Fz(32px) Fw(b) Lh(1) Mend(16px) D(f) Ai(c)`
* 跌: `Fz(32px) Fw(b) Lh(1) Mend(16px) D(f) Ai(c) C($c-trend-down)`
----
* 編寫爬蟲程式
```python=1
# 先導入後面會用到的套件
import requests # 請求工具
from bs4 import BeautifulSoup # 解析工具
import time # 用來暫停程式
# 要爬的股票
stock = ["1101","2330"]
```
----
```python=8
for i in range(len(stock)): # 迴圈依序爬股價
# 現在處理的股票
stockid = stock[i]
# 網址塞入股票編號
url = "https://tw.stock.yahoo.com/quote/"+stockid+".TW"
# 發送請求
r = requests.get(url)
# 解析回應的 HTML
soup = BeautifulSoup(r.text, 'html.parser')
# 定位股價
price = soup.find('span',class_=["Fz(32px) Fw(b) Lh(1) Mend(16px) D(f) Ai(c) C($c-trend-down)","Fz(32px) Fw(b) Lh(1) Mend(16px) D(f) Ai(c)","Fz(32px) Fw(b) Lh(1) Mend(16px) D(f) Ai(c) C($c-trend-up)"]).getText()
```
----
* 先去取得自己的 telegram id
https://t.me/userinfobot
----
```python=19
# 回報的訊息 (可自訂)
message = "股票 "+stockid+" 即時股價為 "+price
# 用 telegram bot 回報股價
# bot token
token = "輸入你的 bot token"
# 使用者 id
chat_id="輸入你的 telegram id"
# bot 送訊息
url = f"https://api.telegram.org/bot{token}/sendMessage?chat_id={chat_id}&text={message}"
requests.get(url)
# 每次都停 3 秒
time.sleep(3)
```
----
執行一次程式看看能不能正常運作

---
### 上傳程式到 github
----
1. [註冊 github](https://github.com/)
----
2. 建立專案 (new repository)
----

----

----

----
3. 上傳程式碼
----

----
* 把爬蟲程式從 colab 貼過來

----

----

---
### 利用 git action 定時啟動爬蟲程式
----

----
複製以下設定檔內容
```yml=1
name: get stock price
on:
schedule:
- cron: "*/5 * * * *"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests
pip install BeautifulSoup4
- name: Check out code
uses: actions/checkout@v2
- name: Run Python script
run: python test1.py
```
----

[自動執行規則連結](https://crontab.guru/)
----
**小提醒**
* 執行時間設定
* 因為本課程並不在開市時間範圍內
* 為了方便測試,先設成每 5 分鐘執行一次
* 確保機器人能執行後再修改
----
* 注意時區問題
* github 時區比台北時間慢 8 小時
* 開市時間:
* **周一**至**周五** **早上 9 點**到**下午 1 點半**
* `cron: "*/5 9-14 * * 1-5"`
* 換算時區:
* **周一**至**周五** **半夜 1 點**到**早上 5 點半**
* `cron: "*/5 1-6 * * 1-5"`
----

----

----
等幾分鐘看看是否能正常運作
順便幫一下還未完成的同學
---
股票助手提醒事項
----
1. 調整回報的股票
2. 調整回報時間
3. 關閉定時通知
----
1. 調整回報的股票
----

----

----

----

----
2. 調整回報時間
----

----

----

----

----

----
3. 關閉定時通知
----

----

----

----

這樣就成功關閉了
---
# END
{"slideOptions":"{}","description":"我們平常會透過瀏覽器連上網站來查找資訊,比如購物網站的商品、徵才網站上的職缺、即時股價…,當我們需要定時或是大量蒐集這些資訊時,每次要都手動操作是非常費時費力的,如果能交給程式來做是不是就輕鬆多了?這個能幫我們蒐集網站資料的程式,就叫做爬蟲。","title":"讓爬蟲做我的股票助手!","contributors":"[{\"id\":\"4bb17f52-f21e-45e0-8333-8a4e05a74b62\",\"add\":48328,\"del\":27445}]"}