概要
Pythonで天気予報BOTを作成してみた後編になる。
前編にてWEBスクレイピングで取得した天気予報情報をもとに、メッセージ配信を行う方法をまとめた。
また、グループチャットにBOTを招待してメッセージ送信する方法も紹介している。
概要 Pythonで定時刻になると天気予報BOTから今日と明日の天気予報が配信されるBOTを作成した。 今日と明日の天気予報情報は、WEBスクレイピングで自動収集する。 当記事では天気予報サイトから欲しい情報をWEBスクレイピング[…]
BOTメッセージ作成
前回作成した天気予報の辞書データが格納されたリストをもとに、メッセージを作成する。
BOT配信メッセージ
タイトルとメッセージリストを、用意したメッセージのひな形にバインドする。
Jupyter Lab
# メッセージタイトル
msg_title = "★" + weather_title + "★\n"
# BOTメッセージフォーマット
msg_format = """
====={0}=====
天気 : {1}
最高気温(C) : {2}
最低気温(C) : {3}
降水確率[00-06] : {4}
降水確率[06-12] : {5}
降水確率[12-18] : {6}
降水確率[18-24] : {7}
"""
msg = ""
for weather in weather_list:
msg += msg_format.format(
weather["date_info"],
weather["weather"],
weather["high_temperature"],
weather["low_temperature"],
weather["prob_midnight"],
weather["prob_morning"],
weather["prob_afternoon"],
weather["prob_night"]
)
bot_msg = msg_title + msg
print(bot_msg)
# 出力
#★福山市の今日明日の天気★
#
# =====今日 09月02日 (土)=====
# 天気 : 曇のち晴
# 最高気温(C) : 32℃
# 最低気温(C) : 25℃
# 降水確率[00-06] : ---
# 降水確率[06-12] : 20%
# 降水確率[12-18] : 20%
# 降水確率[18-24] : 10%
#
# =====明日 09月03日 (日)=====
# 天気 : 晴
# 最高気温(C) : 36℃
# 最低気温(C) : 24℃
# 降水確率[00-06] : 10%
# 降水確率[06-12] : 10%
# 降水確率[12-18] : 0%
# 降水確率[18-24] : 10%
# BOTメッセージフォーマット
msg_format = """
====={0}=====
天気 : {1}
最高気温(C) : {2}
最低気温(C) : {3}
降水確率[00-06] : {4}
降水確率[06-12] : {5}
降水確率[12-18] : {6}
降水確率[18-24] : {7}
"""
天気予報情報をバインドさせるためのメッセージフォーマット。
バインドさせたい箇所に{n}を連番で記述する。
msg = ""
for weather in weather_list:
msg += msg_format.format(
weather["date_info"],
weather["weather"],
weather["high_temperature"],
weather["low_temperature"],
weather["prob_midnight"],
weather["prob_morning"],
weather["prob_afternoon"],
weather["prob_night"]
)
formatメソッドの引数にバインドさせたい順で天気予報情報を設定する。
最終的に変数msgに戻り値を設定する。
msg_format変数に格納しているメッセージフォーマットは、formatメソッドを使用しても値が変更されない。
そのため、ループで回してformatメソッドを何回呼び出しても同じひな形を使用できる。
Pythonファイルに移動
これまでJupyter Labを使用して開発してきたが、実際にメッセージ配信をするファイルはPythonファイル(拡張子が.py)になる。
そのため、これまで記述してきた内容をPythonファイルに移植する。
尚、そのまま移植してもわかりづらいため、機能ごとにメソッドを作成して移植した。
main.py
import json
from linebot import LineBotApi
from linebot.models import TextSendMessage
import requests
from bs4 import BeautifulSoup
import re
# 設定情報読み込み
with open("settings.json", encoding="utf-8") as f:
res = json.load(f)
# 定数 ---------------
# チャネルアクセストークン
CH_TOKEN = res["CH_TOKEN"]
# ユーザーID
USER_ID = res["USER_ID"]
# 天気予報URL(福山)
URL = "https://tenki.jp/forecast/7/37/6710/34207/"
def get_page_info():
""" 読み込みページ情報取得 """
res = requests.get(URL)
html = res.text.encode(res.encoding)
soup = BeautifulSoup(html, 'lxml')
return soup
def get_wether_info(soup):
""" 今日明日の天気予報dict情報の取得 """
# 今日明日の天気リスト
weather_list = []
# 今日の天気------------------------------------
today_weather = {}
section = soup.find("section", "today-weather")
# 今日の日付
today_section = section.find("h3", "left-style").contents
today_weather["date_info"] = re.sub("\xa0", " ", f"{today_section[0]} {today_section[1].text}")
# 今日の天気
today_weather["weather"] = section.find("p", "weather-telop").text
# 最高気温
today_weather["high_temperature"] = section.find("dd", "high-temp temp").text
# 最低気温
today_weather["low_temperature"] = section.find("dd", "low-temp temp").text
# 降水確率
today_weather["prob_midnight"] = section.select('.rain-probability > td')[0].text
today_weather["prob_morning"] = section.select('.rain-probability > td')[1].text
today_weather["prob_afternoon"] = section.select('.rain-probability > td')[2].text
today_weather["prob_night"] = section.select('.rain-probability > td')[3].text
# 今日明日の天気リストの格納
weather_list.append(today_weather)
# 明日の天気------------------------------------
tomorrow_weather = {}
# 明日の天気セクション
section = soup.find("section", "tomorrow-weather")
today_section = section.find("h3", "left-style").contents
tomorrow_weather["date_info"] = re.sub("\xa0", " ", f"{today_section[0]} {today_section[1].text}")
# 明日の天気
tomorrow_weather["weather"] = section.find("p", "weather-telop").string
# 最高気温
tomorrow_weather["high_temperature"] = section.find("dd", "high-temp temp").text
# 最低気温
tomorrow_weather["low_temperature"] = section.find("dd", "low-temp temp").text
# 降水確率
tomorrow_weather["prob_midnight"] = section.select('.rain-probability > td')[0].text
tomorrow_weather["prob_morning"] = section.select('.rain-probability > td')[1].text
tomorrow_weather["prob_afternoon"] = section.select('.rain-probability > td')[2].text
tomorrow_weather["prob_night"] = section.select('.rain-probability > td')[3].text
# 今日明日の天気リストの格納
weather_list.append(tomorrow_weather)
return weather_list
def create_msg(weather_title, weather_list):
""" LINE BOTメッセージ作成 """
# メッセージタイトル
msg_title = "★" + weather_title + "★\n"
# BOTメッセージフォーマット
msg_format = """
====={0}=====
天気 : {1}
最高気温(C) : {2}
最低気温(C) : {3}
降水確率[00-06] : {4}
降水確率[06-12] : {5}
降水確率[12-18] : {6}
降水確率[18-24] : {7}
"""
msg = ""
for weather in weather_list:
msg += msg_format.format(
weather["date_info"],
weather["weather"],
weather["high_temperature"],
weather["low_temperature"],
weather["prob_midnight"],
weather["prob_morning"],
weather["prob_afternoon"],
weather["prob_night"]
)
bot_msg = msg_title + msg
return bot_msg
def main():
""" LINE BOTメイン処理 """
# 天気予報ページ情報取得
soup = get_page_info()
# ページタイトル
page_title = soup.title.text
m = re.search(".*天気", page_title)
weather_title = m.group(0) # 福山市の今日明日の天気
# 今日明日の天気予報情報
weather_list = get_wether_info(soup)
# LINE BOTメッセージ
msg = create_msg(weather_title, weather_list)
messages = TextSendMessage(text=msg)
line_bot_api = LineBotApi(CH_TOKEN)
line_bot_api.push_message(USER_ID, messages=messages)
if __name__ == "__main__":
main()
# LINE BOTメッセージ
msg = create_msg(weather_title, weather_list)
messages = TextSendMessage(text=msg)
line_bot_api = LineBotApi(CH_TOKEN)
line_bot_api.push_message(USER_ID, messages=messages)
作成したメッセージを最終的にLINEのAPIで送信する。
ターミナルを開いて、以下を実行してLINEに正しくメッセージが届くか確認すること。
ターミナル
python main.py
メッセージの定期実行
Git Hub Actionsを使用して、定刻になるとmain.pyを実行する仕組みを作成する。
前提
・VSCodeでGit連携できる状態であること
Git連携
Git Hub Actionsを利用するため、作成したLINE BOTファイルをGit Hubに連携する。
スケジュール実行
Git Hub Actionsにて、定期的にmain.pyを実行させるymlファイルを作成する。
ymlファイルの作成
main.yml
# ワークフロー名
name: chatbot
# 実行タイミング
# スケジュールの設定時刻はUTCとなる。
# 例えばUTC時刻を22時とすると、日本時間(JST: Japan Standard Time)の場合は翌7時になる。
on:
push:
#schedule:
# - cron: '0 22 * * *'
jobs:
build:
# Ubuntuの最新版環境内で処理を実行することを指定
runs-on: ubuntu-latest
# 実行する処理&コマンド指定
steps:
# リポジトリからチェックアウトして以下の処理を実行していく
- uses: actions/checkout@v2
- name: Set up Python 3.8
uses: actions/setup-python@v1
with:
python-version: 3.8
- name: Install dependencies
run: |
# pip更新
python -m pip install --upgrade pip
# 必要なパッケージインストール
pip install line-bot-sdk
pip install beautifulsoup4
pip install lxml
- name: Run script
run: |
# main.pyの実行
python main.py
#schedule:
# – cron: ‘0 22 * * *’
main.pyを実行するためのスケジュール設定箇所。
pushとしており、何らかのファイルがプッシュされたタイミングでLINE BOTファイルを実行する。
コメントアウトしている箇所は後で書き換える部分となる。
上記の意味は翌朝7:00にLINE BOTファイルを実行するという意味。※Git Hub Actionsの定期実行設定では必ずしも時刻通りにならないので注意
各数値の意味は以下となる。
・UTC22時(日本時刻:翌7:00)
・毎日(*)
・毎月(*)
・すべての曜日(*)
GitHub で特定のアクティビティが実行された時、スケジュールした時間、または GitHub 外でイベントが発生した時…
# 必要なパッケージインストール
pip install line-bot-sdk
pip install beautifulsoup4
pip install lxml
上記はGit Hub ActionsでLINE BOTファイルを実行する際に必要なパッケージとなる。
run: |
# main.pyの実行
python main.py
上記はGit Hub Actionsに実行させるファイル名になる。
今回はmain.pyを作成したので、そのファイル名を指定している。
ymlファイルのコミット(プッシュ)
ymlファイルをコミットする。
リモート側でのコミットはプッシュ扱いとなるため、これによりGit Hub Actionsがmain.pyを実行する。
ymlファイルをコミットして、LINE BOTメッセージが届いたら動作確認完了。
※buildに時間がかかるため、コミットして少しまてば届く
スケジュール設定
LINE BOTから問題なくメッセージが届いたなら、ymlファイルを以下のように修正して定期的にBOTメッセージを送信させるようにする。(翌7:00に自動でメッセージ配信を行う設定)
※Git Hub Actionsでは時間通りにメッセージが届かず、割と遅れてメッセージが届くので注意
main.yml
# ワークフロー名
name: test_chatbot
# 実行タイミング
# スケジュールの設定時刻はUTCとなる。
# 以下の例ではUTC時刻が22時のため、日本時間(JST: Japan Standard Time)だと翌7時になる。
on:
#push:
schedule:
- cron: '0 22 * * *'
(略)
動作確認
翌日に届いたメッセージが以下になる。
※7時ぴったりにはメッセージは届かない
補足
グループチャットにBOTを招待してメッセージ配信したいときは、グループチャットのIDを取得する必要がある。
以下はグループIDを取得して、グループチャットにてメッセージ配信する方法について紹介する。
BOT設定
複数人トークへの参加を許可する。
GASでグループID取得
Google Apps Scriptを使用してグループIDを取得する。
準備
BOTを招待するためのグループを作成する。
GASでグループIDの取得スクリプト作成
BOTをグループに招待した際にスプレッドシートにグループIDを書き込むスクリプトを作成する。
GASファイルに以下のコードを張り付ける。
GAS
function doPost(e){
var json = JSON.parse(e.postData.contents);
var gid = json.events[0].source.groupId;
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
sheet.getRange(1,1).setValue(gid);
}
URL取得
GASをデプロイしてURLを取得する。