看盤小助理之 Line Notify 即時通知

文彥 | Nov 12, 2023

流程圖如上

小論文pdf

程式碼:

#--------------------------載入模組------------------------------
import requests
from bs4 import BeautifulSoup
import time 
from datetime import datetime
#--------------------------參數設定------------------------------
n_time = datetime.now() #現在時間 
d_time1 = datetime.strptime(str(datetime.now().date())+"9:00","%Y-%m-%d%H:%M")  #開盤時間 9:00
d_time2 = datetime.strptime(str(datetime.now().date())+"13:30","%Y-%m-%d%H:%M") #收盤時間 13:30 
#-----Line Notify-----
token = 'sSnk6ptHXzweoZtU29YzLVoufk7CMh0pD6JF8CdCqQ5'   #權杖代碼
#定義send_message,傳入權杖(token)和要推送的字串(msg)來達到字典型態。
def send_message(token,msg):
#Line Notify 推送已post的方式提交請求,因此要定義「headers」它是字點型態
#其中一個索引key名稱「Authorization」對應的值,是由持有人(Bearer)和權杖(token)組成的字串
    token = 'sSnk6ptHXzweoZtU29YzLVoufk7CMh0pD6JF8CdCqQ5'
    haeaders = {
        'Authorization':"Bearer "+token,
        "Content-Type":"application/x-www-from-urlencoded"
    }
    #將要推送的訊息字串存在字典內,放進Message的索引中
    payload = {"message":msg}
    #以post的方式,向'https://notify-api.line.me/api/notify'提出請求
    r = requests.post('https://notify-api.line.me/api/notify',headers = haeaders,params = payload)
    #可以做到例外排除,將請求的回傳用r接起,並把這個請求的執行狀態作為send_messaage函數的回傳
    return r.status_code
    #如果請求成功 r.status_code = 200

#-----爬蟲設定-----
def web_crawler():
    url = 'https://tw.stock.yahoo.com/quote/2330'    #設動網址
    web = requests.get(url)
    soup = BeautifulSoup(web.text,'html.parser')
    return soup
soup = web_crawler()
title = soup.select('.D\(f\) h1')[1].text       #標題
number = '('+ soup.select('.Fz\(24px\)')[1].text +")"     #該檔股票代號
title += number #整理成 標題+代號

test = True #測試狀態
first = True   #程式今日第一次執行

#--------------------------main---------------------------------
while (n_time >= d_time1 and n_time <= d_time2) or test:  

#--------------------------爬取網站資料--------------------------
    n_time = datetime.now()
    soup = web_crawler()
    now_type =  soup.select('.C\(\#6e7780\)')[0].text   #開盤收盤狀態
    situation = soup.select("#main-0-QuoteHeader-Proxy")[0].select(".Fz\(16px\)")[2].text   #該檔運勢
    data = soup.select("#main-2-QuoteOverview-Proxy")[0].select(".Pos\(r\)")[0].select(".D\(f\)")[1]
    data = data.select('.Pos\(r\) ul')[0].text #該檔即時的數據

#--------------------------資料整理--------------------------
    def str_number_data(data):      #將爬下的股票數據整理成'字典'的資料型態
        #將'字'與'數字'分割
        d = {}
        data_name,Num = '',''  #暫存數據名稱 暫存數字 資料型態為str
        state = 0   #0表示目前為中文字 1表示目前為數字
        for i in data:
            if ord(i)==40 or ord(i)==41:    #ASCII碼判斷'('or')'
                data_name+=i        
                continue
            if ord(i)>=37 and ord(i)<=57:   #ASCII 符號是數字~number
                if not state:   #先判斷當前是否為中文字
                    Num = i     
                    state = 1
                else:Num+=i
            else:
                if state:
                    d[data_name] = Num
                    data_name = i
                else:
                    data_name+=i
                state = 0
        d[data_name] = Num
        return(d)
    def FS(data):   #格式化輸出資料
        a= '\n'
        s = 0
        for key,Value in data.items():
            a += '{:13s}'.format((f'{key} : {Value}'))
            if s:
                a += '\n'
                s = 0
            else: s+=1  
        return a
    def write_data_txt(data):    #將部分資料紀錄在記事本
        print("更新data.txt ~~")    
        word = ['成交','開盤','漲跌','漲跌幅']
        with open("data.txt",'w',encoding="utf-8") as f:
            f.write('漲跌狀況 '+s+'\n')
            for w in word:
                meg = w+" "+data[w]+'\n'
                f.write(meg)
    def read_data_txt():        #讀取舊資料
        print("讀取data.txt ~~\n")
        old_data = {}
        with open('data.txt','r',encoding='utf-8') as f:
            for line in f.readlines():
                a,b = line.split()
                old_data[a] = b.strip("\n")
        return old_data

    data = str_number_data(data)
    ups_and_down = f"{data['漲跌']}({data['漲跌幅']})"  #漲跌   
    Instant_price = f'{data["成交"]}'   #即時價
    if float(data['成交'])>float(data['昨收']):     s,emoji = '↑',['📈',' ⬆️']
    elif float(data['成交'])<float(data['昨收']):   s,emoji = '↓',['📉',' ⬇️']
    else:   s,emoji = '平',['-','-']    #s變數代表漲跌狀況
#--------------------------傳入Line Notify--------------------------
    if  first:   #每天第一次啟動時 抓取當時狀況
        write_data_txt(data)
        old_data = read_data_txt()
        send_msg=("\n開盤囉~🍉 \n排程啟動成功👌")
        print("發送開盤通知~",send_message(token,send_msg))
        send_msg = f"{title}🍉\n||  {now_type}  ||\n連漲連跌  :  {situation}  \n即時價 : {Instant_price} {emoji[0]}{emoji[1]}"
        send_msg += f"{ups_and_down} \n------------------------------------------------\n"   
        send_msg += FS(data)    
        print("發送開盤數據~",send_message(token,send_msg)) #傳送至Line notify
        print(send_msg) #在終端機上檢查
        print("啟動成功 fitst設定True")
        first = False
    else:   
        if old_data['漲跌']!=data['漲跌']:  #成交價若有變化才傳送通知
            if old_data["漲跌狀況"]==s: #判斷漲跌是否有變化
                send_msg = (f'~目前持續{emoji[0]}{emoji[0]}\n即時價:{data["成交"]} {emoji[1]}{ups_and_down}\n')
                if int(data['漲跌'])<int(old_data['漲跌']):
                    send_msg += (f'風向似乎有些變化唷🎃')
            else:
                send_msg = f'‼️‼️‼️‼️\n風向變了~由{old_data["漲跌狀況"]}{emoji[0]}\n'
                send_msg += f'即時價:{data["成交"]}{emoji[0]}{emoji[1]}{ups_and_down}\n'
                if data['成交']>old_data['成交']:
                    send_msg += (f'上漲囉~~{emoji[0]} ')
                else :
                    send_msg += (f'往下掉囉~~{emoji[0]} ')
            print("發送股價變化通知",send_message(token,send_msg))
            print(f'{send_msg}\n')
            write_data_txt(data)    #將更新資料寫進data.txt內
            old_data = read_data_txt()  #讀取新資料 
    test = False        
    end_time = datetime.now()
    print(f"目前即時價:{data['成交']} {s}{ups_and_down}")
    print("等待60s...\n")
    time.sleep(60 - float(end_time.timestamp() - n_time.timestamp()))    #等待用一分鐘扣掉前面執行的過程
    n_time  = datetime.now()
print("Exit Loop")
send_msg = "🎃收盤囉~🍉\n"
send_msg +=(f"{title}🍉\n||  {now_type}  ||\n連漲連跌  :  {situation}  \n即時價 : {Instant_price} {emoji[0]}{emoji[1]} {ups_and_down} \n------------------------------------------------\n")
send_msg += FS(data)
print('發送收盤通知',send_message(token,send_msg))

    

主要透過Python結合Line Notify與Windows排成工具實現全自動運行,將Yahoo上的股價資訊爬取下來做資料整理»將整理完的數據分析,判斷數據變化給予相應的回應。

comments powered by Disqus