## 讓爬蟲做我的股票助手 --- ## 課程規劃 ---- 課程將分為基礎和進階兩部分 * 基礎 : 爬蟲原理、基礎 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. 瀏覽器將網頁顯示在畫面上 ---- ![](https://hackmd.io/_uploads/rJuFw85ZT.png) ---- #### 爬蟲 1. 利用程式向目標網站發出請求 (request) 2. 伺服器回應 (response) 網頁 (HTML) 給程式 3. 拿到網頁後就可以拿來運用 ---- ![](https://hackmd.io/_uploads/SkRIIkneT.png) ---- 瀏覽網站時會拿到網站的 HTML 那 HTML 是什麼? --- ### HTML HyperText Markup Language ---- * 就是我們看到的網頁 * HTML 就像一個 Word 檔,可以在裡面寫入文字、排版、放圖片等,再用瀏覽器打開就會變成我們看到的網頁 ![](https://hackmd.io/_uploads/SkrHtU5-p.png) ---- * HTML 包含了網頁上的內容 * 爬蟲的做法就是拿到網頁的 HTML,從裡面找出資料 * ex: 從證交所網站的 HTML 中找出每日股價 ---- 網頁是只由一個 HTML 組成嗎? 還是有其它東西? --- ### 網頁的組成 ---- * 網頁不一定只是一個 HTML,通常是由 HTML 加上如 CSS、JavaScript、資料、圖片等資源共同組成 * 這些資源都有各自對應的網址,只要對這些網址發送請求,就能取得想要的資源 ---- ![](https://hackmd.io/_uploads/SyvNXl2g6.png) [圖片連結](https://developers.google.com/community/gdsc/images/gdsc-social-share.png) ---- 當我們瀏覽網站時 ---- 1. 瀏覽器連上網站 2. 向伺服器請求網站會用到的資源 3. 把回應的資源放到 HTML 裡,組合成完整網頁 4. 顯示在畫面上 ---- ![](https://hackmd.io/_uploads/rkzhOc9ZT.png) ---- 所以我們瀏覽網頁時所看到的 HTML 是已經將這些資源放進去後產生的完整 HTML ---- #### 小結 網頁上的資源會有自己的網址 對這些網址發送請求可以拿到對應的資源 ---- 但我只知道網頁的網址 要怎麼取得其它資源的網址? --- ### 怎麼取得資源的網址? ---- 開啟瀏覽器的 Network 介面 * `F12` * 右鍵 -> 檢查 ---- ![](https://hackmd.io/_uploads/rJ-g2GBWa.png) ---- * 右上方選 Network (之後要先重新整理畫面一次) * Network 會監測瀏覽器發出的請求與收到的回應 ---- ![](https://hackmd.io/_uploads/SyQ0iR2Tn.png =80%x) ---- 可以根據資源類型篩選,會更容易找到 * ![](https://hackmd.io/_uploads/rkbgrzpa3.png) * All: 全部類型 * Fetch/XHR: 網頁資料,ex: 證交所每日股價 * Img: 圖片檔案 * Doc: HTML檔案 ---- 以證交所網站為例,先篩選資源類型 再從 Preview 或 Response 中查看回應資料的內容 ![](https://hackmd.io/_uploads/SJyPLf6T2.png) ---- * 找到資料後,從 Headers 中的 Request URL 可以得到資源對應的網址 ![](https://hackmd.io/_uploads/HknKBBabT.png) ---- * 對這個網址發送請求,就能取得台股收盤價 ![](https://hackmd.io/_uploads/rJcSHjj-6.png) ---- 打開 https://www.twse.com.tw/zh/trading/historical/stock-day-avg.html 用 `F12` 找出 `2330台積電` 的收盤價網址 再訪問該網址,顯示收盤價在瀏覽器上 ---- #### Request Method 發送請求的方法,表示你想對資源做什麼操作 ---- ![](https://hackmd.io/_uploads/r1h88STbp.png) ---- 常見的 Request Method * GET: 取得資料 * POST: 送出資料,ex: 送出登入帳密 * PUT: 修改資料 * DELETE: 刪除資料 ---- 若 Headers 的 Request Method 是 GET,爬蟲的 Request Method 就要設為 GET ![](https://hackmd.io/_uploads/H1UvydsWa.jpg) --- ### 爬蟲注意事項 ---- 要有禮貌 ---- * 對同一個網頁持續爬蟲盡量每發一次請求就停頓幾秒 * 過多的流量消耗會導致伺服器吃不消掛掉 * 短時間大量發送請求會被認為是在攻擊網站,可能被網站封鎖 ---- 爬蟲穩定度較低,可能會遇到一些例外的情況 ---- 1. 發送請求時連線失敗 * 可以根據回應的 HTTP 狀態碼或其他方式確保請求有發送成功 * 每次發送請求之後都停頓幾秒,讓伺服器有喘息時間 ---- 2. 網頁內容如果有更改,爬蟲程式會出現錯誤 * 可能要持續記錄網頁變化或資料的蒐集情況,才能及時去更改程式碼 * 有些網站有提供 API 的方式,發請請求可以直接拿到資料 ---- 3. 持續向某一網站爬蟲時,伺服器可能因效能問題,會回傳錯誤的資料 * 可以利用程式的 try catch 機制或其他方式確保回應的資料是正確的 * 每次發送請求之後都停頓幾秒,讓伺服器有喘息時間 --- ### 爬蟲原理總結 ---- * 爬蟲可以模擬真人瀏覽網站的行為 * 對網站的網址發送請求可以拿到網站的 HTML * 爬蟲的做法就是拿到網站的 HTML,再從裡面找出資料 * 或是對資源的網址發送請求,直接拿到該資源 (ex: 圖片) --- ### 基礎 Python ---- ### 執行環境 ---- ![](https://hackmd.io/_uploads/SJk0rzpZ6.png =80%x) [連結](https://colab.research.google.com/?hl=zh-tw) ---- ### Hello World ![](https://hackmd.io/_uploads/BkoJ0rpWT.png) * 按左邊的圖標執行程式 ---- ### 宣告變數 ```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 ![](https://hackmd.io/_uploads/ByrnBPaZT.png) ---- 取得股票的收盤價 ---- 執行下面程式,並輸入股票代號來爬收盤價 ```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]) ``` ---- ![](https://hackmd.io/_uploads/rJaBsvaWT.png) --- ## 進階課程介紹 ---- * HTML 詳細介紹 `(15 min)` * 爬蟲工具詳細介紹 `(40 min)` * 實作上會遇到的狀況 `(40 min)` * 用爬蟲製作股價助手 `(55 min)` --- ### HTML 詳細介紹 看得懂 HTML 才能整理出想要的資料 ---- ### 標籤 & 元素 ---- * HTML 透過 **標籤 `<>` (tag)** 來描述文字、超連結、圖片等內容的種類和顯示方式 * 標籤就是告訴 HTML 我要寫入的資料是哪種類型,而用標籤包起來的資料就稱為 **元素** * 例如 `<h1>Hello</h1>` 在網頁上會顯示成大標題格式的文字 * 這裡的 `<h1>` 是標籤,而 `<h1>Hello</h1>` 是元素 ---- ![](https://hackmd.io/_uploads/HJFUmGTWp.png) ---- 常見標籤 * `<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> ``` ![](https://hackmd.io/_uploads/Bytvp3qZa.png) ---- 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>` 這兩個元素 ---- ![](https://hackmd.io/_uploads/HJ884zaWT.png) ---- #### 小結 爬蟲程式取得 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`之外,還有以下這些解析器 ![](https://hackmd.io/_uploads/B1S-kyI02.png) ---- * 除了 `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 動態網站 ---- 靜態網站 ![](https://hackmd.io/_uploads/ByF4mwUW6.png) ---- 特點 * 伺服器會將資料寫在回應給瀏覽器的 HTML 上 * 資料就在 HTML 裡,只要對網站的網址發送請求就可以得到資料 * 但因為資料是在 HTML 裡,所以要自己從 HTML 整理出資料 ---- 例子 * https://histock.tw/stock/rank.aspx * 打開網頁時,股票資訊就已經寫在網頁裡 * 對這個網頁發送請求,拿到 HTML 後再從 HTML 裡找出想要的資料 ---- 動態網站 ![](https://hackmd.io/_uploads/S1VxluoZp.png) ---- 特點 * 資料不是一開始就寫在 HTML 上,而是瀏覽器另外向伺服器請求該資料後再寫入 * 對網站的網址發送請求不會拿到該資料 ---- 特點 * 要對資料的網址發送請求 -> 在 Network 介面中尋找資料的網址並發送請求 * 因為是直接取得資料,所以整理起來比較方便 ---- 例子 * https://www.twse.com.tw/zh/trading/historical/stock-day-avg.html * 輸入年、月、股票並按查詢後,才會去請求對應的資料,取得回應後再顯示在網頁上 ![](https://hackmd.io/_uploads/SyMJzmB-6.png =70%x) * 只要對股價資料的網址發送請求,就能拿到股價的資料 ---- 怎麼分辨是靜態網站還是動態網站 ---- 個人做法 * Network 介面看回應的 HTML 裡有沒有想要的資料 * 用爬蟲爬 HTML 下來看有沒有想要的資料 --- Q2: 網站需要登入 ---- 可以透過 cookie 自動登入 ---- cookie 是什麼? ---- 登入 Moodle 後 在一段時間內若沒關閉瀏覽器 可以不用重新登入 ---- 登入成功後 伺服器會用使用者的帳密或其他資訊 產生一個時限內有效的 cookie 之後登入時,伺服器會直接驗證 cookie 若 cookie 有效,就會直接登入 ---- 第一次登入 ![](https://hackmd.io/_uploads/HkQov_i-p.png) ---- 之後登入 ![](https://hackmd.io/_uploads/rkVvv_sba.png) ---- 爬蟲可以將 cookie 放在 header 裡傳給網站 就能解決需要登入的問題 ---- 怎麼取得 cookie? ---- 先看 cookie 在哪裡 ---- 1. `F12` 或 右鍵->檢查 2. 選 Application 3. 選 Cookies 4. 選對應的網站 ---- ![](https://hackmd.io/_uploads/rkDxndiWa.png) * Name 是 cookie 名稱 * Value 是 cookie 的值 ---- 怎麼知道登入是看哪個 cookie? 看登入前後有哪個 cookie 出現或值有變化 ---- * 登入前 ![](https://hackmd.io/_uploads/rJ1X6Oib6.png =80%x) ---- * 登入後 ![](https://hackmd.io/_uploads/S1kZCOj-a.png =80%x) ---- 所以在請求的 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 了解使用者使用的瀏覽器 ![](https://hackmd.io/_uploads/SyPzN0LW6.png) ---- 在 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 定時啟動爬蟲程式 ---- ![](https://hackmd.io/_uploads/HyGEz-gza.png) ---- ### 實作流程 ---- 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 ![](https://hackmd.io/_uploads/S1v54-lG6.png =80%x) ---- * 依序輸入 bot 的名稱和 username * 最後記下 BotFather 回傳的 token ![](https://hackmd.io/_uploads/rk9GUZxGT.png) ---- ![](https://hackmd.io/_uploads/B1uCVJ0M6.jpg =30%x) ---- ![](https://hackmd.io/_uploads/HkKPSkAz6.png =30%x) --- ### 編寫爬蟲程式 ---- * 目標 1. 爬取股票 2. 透過機器人回報 ---- * 實作流程 * 確認網站資料怎麼取得 * 編寫爬蟲程式 ---- * 確認網站資料怎麼取得 1. 觀察 [yahoo股市](https://tw.stock.yahoo.com/quote/2330.TW) 網站上,股價資料的網址規則 2. 發現網址會包含`股票編號`,可以用網址找到不同股票 ![image.png](https://hackmd.io/_uploads/SynateGX6.png =80%x) ---- 3. 觀察股價的 HTML ![image.png](https://hackmd.io/_uploads/SyR85gGma.png) * `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) ``` ---- 執行一次程式看看能不能正常運作 ![image.png](https://hackmd.io/_uploads/SJPUalfX6.png) --- ### 上傳程式到 github ---- 1. [註冊 github](https://github.com/) ---- 2. 建立專案 (new repository) ---- ![](https://hackmd.io/_uploads/rJ-BWVAza.png) ---- ![](https://hackmd.io/_uploads/r1tSlERMp.png =80%x) ---- ![](https://hackmd.io/_uploads/SkuFb4RfT.png) ---- 3. 上傳程式碼 ---- ![](https://hackmd.io/_uploads/SJCXzN0Gp.png) ---- * 把爬蟲程式從 colab 貼過來 ![image.png](https://hackmd.io/_uploads/rkd8ebzma.png) ---- ![](https://hackmd.io/_uploads/r19bVVAf6.png =70%x) ---- ![](https://hackmd.io/_uploads/rknnEV0fp.png) --- ### 利用 git action 定時啟動爬蟲程式 ---- ![](https://hackmd.io/_uploads/H17XF4Cz6.png) ---- 複製以下設定檔內容 ```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://hackmd.io/_uploads/SJgSJ8CGp.png) [自動執行規則連結](https://crontab.guru/) ---- **小提醒** * 執行時間設定 * 因為本課程並不在開市時間範圍內 * 為了方便測試,先設成每 5 分鐘執行一次 * 確保機器人能執行後再修改 ---- * 注意時區問題 * github 時區比台北時間慢 8 小時 * 開市時間: * **周一**至**周五** **早上 9 點**到**下午 1 點半** * `cron: "*/5 9-14 * * 1-5"` * 換算時區: * **周一**至**周五** **半夜 1 點**到**早上 5 點半** * `cron: "*/5 1-6 * * 1-5"` ---- ![](https://hackmd.io/_uploads/SycJh4CGa.png =60%x) ---- ![](https://hackmd.io/_uploads/rJichECz6.png) ---- 等幾分鐘看看是否能正常運作 順便幫一下還未完成的同學 --- 股票助手提醒事項 ---- 1. 調整回報的股票 2. 調整回報時間 3. 關閉定時通知 ---- 1. 調整回報的股票 ---- ![](https://hackmd.io/_uploads/Hkz9WICGT.png) ---- ![image.png](https://hackmd.io/_uploads/S1oMReGXT.png) ---- ![image.png](https://hackmd.io/_uploads/r19AAlM7p.png) ---- ![](https://hackmd.io/_uploads/BJsttUAMp.png =60%x) ---- 2. 調整回報時間 ---- ![](https://hackmd.io/_uploads/B1EfWI0f6.png) ---- ![](https://hackmd.io/_uploads/S1rjxICfa.png) ---- ![](https://hackmd.io/_uploads/r1UfwU0z6.png) ---- ![](https://hackmd.io/_uploads/rkTrOIAfa.png) ---- ![](https://hackmd.io/_uploads/HJ0T_LAza.png =60%x) ---- 3. 關閉定時通知 ---- ![](https://hackmd.io/_uploads/r1e9XIAGa.png) ---- ![](https://hackmd.io/_uploads/Hka8V8CzT.png) ---- ![](https://hackmd.io/_uploads/rkqRV8RGT.png) ---- ![](https://hackmd.io/_uploads/r1pDSUCMT.png) 這樣就成功關閉了 --- # END
{"slideOptions":"{}","description":"我們平常會透過瀏覽器連上網站來查找資訊,比如購物網站的商品、徵才網站上的職缺、即時股價…,當我們需要定時或是大量蒐集這些資訊時,每次要都手動操作是非常費時費力的,如果能交給程式來做是不是就輕鬆多了?這個能幫我們蒐集網站資料的程式,就叫做爬蟲。","title":"讓爬蟲做我的股票助手!","contributors":"[{\"id\":\"4bb17f52-f21e-45e0-8333-8a4e05a74b62\",\"add\":48328,\"del\":27445}]"}
    2302 views