iT邦幫忙

2023 iThome 鐵人賽

DAY 22
0
SideProject30

用30天打好Python、LineBot的基礎&基本應用系列 第 22

[Day 22] 發送天氣預報結果給使用者吧

  • 分享至 

  • xImage
  •  

終於來到期待的實際應用篇了

今天來教大家該如何結合前面介紹的套件去發送天氣預報給加入LineBot機器人的好友吧。

我們爬取氣象預報資料的方式不會是selenium,為甚麼呢?因為爬取資料的過程實在太繁複啦,必須等網頁讀取完後才能爬取資料,所以我們的政府有開放API給民眾使用,以下來逐步跟各位講解該如何使用。

取得氣象資料

首先先來到氣象資料開放平臺註冊帳號

這邊可以選擇直接官網註冊,或者使用Facebook帳號登入,隨讀者喜好選擇

https://ithelp.ithome.com.tw/upload/images/20231002/201465555i45tALNoZ.png

註冊完畢後會來到這個畫面,點擊"取得授權碼"並將右邊紅字全部複製下來

https://ithelp.ithome.com.tw/upload/images/20231002/20146555KTVBiKjVG0.png

上述步驟完成後,來到中央氣象局開放資料平臺之資料擷取API
頁面,內部有非常多資料可以使用,我這邊選擇使用"臺灣各鄉鎮市區未來1週天氣預報"做範例,讀者若對其他資料有興趣也可以使用喔!

https://ithelp.ithome.com.tw/upload/images/20231002/20146555uacrvyczBr.png

下面有各個參數可以調整

點擊右方的"Try it out"

https://ithelp.ithome.com.tw/upload/images/20231002/20146555xksrxKxO96.png

在Authorization的空位貼上剛剛複製的授權碼

https://ithelp.ithome.com.tw/upload/images/20231002/20146555V4ymv6ahQ0.png

假設不做任何篩選,直接點擊Execute的話,網頁可能會因為資料量過大而卡住,要過一段時間才能讀取完畢

等待一段時間後,下方會出現結果

https://ithelp.ithome.com.tw/upload/images/20231002/20146555t7y1Jp52IJ.png

查看Request URL的網址

https://opendata.cwb.gov.tw/api/v1/rest/datastore/F-D0047-091?Authorization=CWB-D5096651-492F-4FA3-9417-20718C76D916

這時我們新增了地區的限制,假設我想要新竹市的資訊,輸出後得到下列網址

https://opendata.cwb.gov.tw/api/v1/rest/datastore/F-D0047-091?Authorization=CWB-D5096651-492F-4FA3-9417-20718C76D916&locationName=%E8%87%BA%E4%B8%AD%E5%B8%82

可以發現多了locationName,可以以此做區域的控制

其他的篩選條件也可以發現只多了個變數

處理資料

這邊使用requests去向api請求資料,以下範例多了timezone的塞選資料,用意是固定資料的時間格式

import requests
import json
import warnings

from datetime import date, datetime, timezone, timedelta
from flask import Flask, request
from linebot.models import TextSendMessage
from linebot import LineBotApi, WebhookHandler, LineBotSdkDeprecatedIn30

def get_weather_info(location):
    #修正"台"與"臺"用字
    if location[0] == "台":
        location = "臺" + location[1:]
    #固定時間格式,timedelta是固定我們的時區
    time_now = datetime.now(tz=timezone(timedelta(hours=8))).strftime("%Y-%m-%d") + "T00:00:00"
    #此處記得修改自己的授權碼
    url = "https://opendata.cwb.gov.tw/api/v1/rest/datastore/F-D0047-091?Authorization=<你的授權碼>&locationName=&elementName=WeatherDescription&sort=time&timeFrom=" + time_now
    #根據api回傳的結果做處理
    response = requests.get(url).json()["records"]["locations"][0]["location"]
    city_list = ['新竹縣', '金門縣', '苗栗縣', '新北市', '宜蘭縣', '雲林縣', '臺南市', '高雄市', '彰化縣', '臺北市', '南投縣', '澎湖縣', '基隆市', '桃園市', '花蓮縣', '連江縣', '臺東縣', '嘉義市', '嘉義縣', '屏東縣', '臺中市', '新竹市']
    #輸出字串
    return_output = location + "天氣:\n"
    
    #確認輸入資料是否正確
    try:
        city_list.index(location)
    except ValueError:
        return ValueError
    
    #存放一週的天氣預報資料
    store_data = {}

    #取得當天的時間資訊
    for i in range(len(response)):
        if response[i]["locationName"] == location:
            response = response[i]["weatherElement"][0]["time"]
            break

    #取得當天的天氣預報資訊
    for i in range(0, len(response), 2):
        start_time = response[i]["startTime"].split(" ")[0]
        store_data[start_time] = response[i]["elementValue"][0]["value"]

    #將儲存的資料放入輸出字串
    for i in store_data:
        return_output += i + ":\n " + store_data[i] + "\n"

    return return_output


app = Flask(__name__)
@app.route("/", methods=['POST'])
def get_reply():
    #這邊記得修改
    api = LineBotApi('<你的Channel access token>')
    handler = WebhookHandler('<你的Channel secret>')
    body = request.get_data(as_text=True)
    json_data = json.loads(body)
    
    try:
        signature = request.headers['X-Line-Signature']
        handler.handle(body, signature)
        user_id = json_data['events'][0]["source"]['userId']
        text = json_data['events'][0]['message']['text']
        output = get_weather_info(text)
        api.push_message(user_id, TextSendMessage(text=output))
    except Exception as e:
        print(e)
    return 'OK'

def jobs1():
	app.run()
    
#--log=stdout, 將輸出送至終端機
#這邊記得修改
def jobs2():
    subprocess.run([
        "ngrok", 
        "http", 
        "5000",
        "--domain=<你的domain位置>", 
        "--log=stdout"
    ])
	
if __name__ == "__main__":
    #避免出現警告訊息
    warnings.filterwarnings("ignore", category=LineBotSdkDeprecatedIn30)
    #多執行緒工作
    jobs = []
    jobs.append(threading.Thread(target=jobs1))
    jobs.append(threading.Thread(target=jobs2))

    for i in range(len(jobs)):
        jobs[i].start()

結果如下圖

https://ithelp.ithome.com.tw/upload/images/20231002/20146555ouEnwfKyle.png

https://ithelp.ithome.com.tw/upload/images/20231002/20146555ddtonoVslT.png


上一篇
[Day 21] Day16 ~ 20複習
下一篇
[Day 23] 將油價調漲(降)的資訊發送給使用者吧
系列文
用30天打好Python、LineBot的基礎&基本應用30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言