目的
- LINE Botに慣れる。
- Pythonを用いて位置情報を送信すると天気予報を返信してくれるbotを作成する。
注意!
LINEmessagingAPIもスクレイピングも初めてやったのでプログラムに変なとこがある可能性が高いです。(特にスクレイピングに関しては)
改善点などあったら是非コメントしてください!
参考
下記のサイト、記事を参考にさせていただきました。ありがとうございます。
PythonとLINE APIとHerokuでBOTを作る[Python編]
--LINE Botとスクレイピングを組み合わせるのに参考にさせていただきました。
Messaging API リファレンス
--公式のリファレンスです。
LINE初心者!-LINE Botでオウム返し編-
PythonでLINE Botを作ってみた
Python, LINE APIを使ってbotを作成する
--1番最初に何からやったら良いか分からないときに非常に参考になりました。
イメージマップを使って終電に乗り遅れないボットを作りました
--位置情報を使ったBotを作るのに参考になりました。
Beautiful soupでの スクレイピング基礎まとめ[初学者向け]
PythonとBeautiful Soupでスクレイピング
--Pythonでスクレイピングをするのに参考になりました。
開発環境
- OS X 10.14.6
- Python 3.6.6
- Flask 1.1.1
- Line-bot-sdk 1.14.0
- Heroku
前提
- Line Developerに登録済み
- Herokuに登録済み
ファイル構成
Line_bot
├ main.py
├ scrape.py
├ runtime.txt
├ requirements.txt
└ Procfile
ソースコードとその説明
from flask import Flask, request, abort
from linebot import (
LineBotApi, WebhookHandler
)
from linebot.exceptions import (
InvalidSignatureError
)
from linebot.models import (
MessageEvent, TextMessage, TextSendMessage, LocationMessage
)
from linebot.exceptions import LineBotApiError
import scrape as sc
import urllib3.request
import os
app = Flask(__name__)
YOUR_CHANNEL_ACCESS_TOKEN = os.environ["YOUR_CHANNEL_ACCESS_TOKEN"]
YOUR_CHANNEL_SECRET = os.environ["YOUR_CHANNEL_SECRET"]
line_bot_api = LineBotApi(YOUR_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(YOUR_CHANNEL_SECRET)
@app.route("/callback", methods=['POST'])
def callback():
signature = request.headers['X-Line-Signature']
body = request.get_data(as_text=True)
app.logger.info("Request body: " + body)
try:
handler.handle(body, signature)
except InvalidSignatureError:
abort(400)
return 'OK'
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
text = event.message.text
if '位置情報' in text:
line_bot_api.reply_message(
event.reply_token,
[
TextSendMessage(text='位置情報を教えてください。'),
TextSendMessage(text='line://nv/location')
]
)
@handler.add(MessageEvent, message=LocationMessage)
def handle_location(event):
text = event.message.address
result = sc.get_weather_from_location(text)
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text=result)
)
if __name__ == "__main__":
port = int(os.getenv("PORT", 5000))
app.run(host="0.0.0.0", port=port)
「位置情報」という文字列の入ったテキストをbotが受信すると、「位置情報を教えてください」という返信とともに位置情報の選択画面へのURLを送信するようにしました。
詳しくはLINE DeveloperのドキュメントであるLINE URLスキームを使うを参照してください。
import requests
from bs4 import BeautifulSoup
import re
# 位置情報からその日の天気を返す
def get_weather_from_location(original_location):
# 住所の中から郵便番号を抽出する
location = re.findall('\d{3}-\d{4}', original_location)
# 1回目のスクレイピングでは住所を検索し、候補から取ってくる
url = "https://weather.yahoo.co.jp/weather/search/?p=" + location[0]
r = requests.get(url)
soup = BeautifulSoup(r.text, 'html.parser')
content = soup.find(class_="serch-table")
# 2回目のスクレイピングで用いるURLを得る
location_url = "http:" + content.find('a').get('href')
r = requests.get(location_url)
soup = BeautifulSoup(r.text, 'html.parser')
content = soup.find(id='yjw_pinpoint_today').find_all('td')
info = []
for each in content[1:]:
info.append(each.get_text().strip('\n'))
# 時間
time = info[:8]
# 天気
weather = info[9:17]
# 気温
temperature = info[18:26]
# 上の3つの情報を合わせる
weather_info = [(time[i], weather[i], temperature[i]) for i in range(8)]
result = [('{0[0]}: {0[1]}, {0[2]}°C'.format(weather_info[i])) for i in range(8)]
result = ('{}\nの今日の天気は\n'.format(original_location) + '\n'.join(result) + '\nです。')
return result
scrape.pyのget_weather_from_locationでは受け取った位置情報のうち郵便番号を抜き出してyahoo天気からその日の天気予報を取ってくるようにしています。
python-3.6.6
Flask==1.1.1
line-bot-sdk==1.14.0
beautifulsoup4==4.7.
urllib3==1.24.1
soupsieve==1.6.1
もしかしたら不要なものも書いているかもしれません。
ご指摘ください。
ソースコードはここにおいてます。
https://github.com/RY908/Weather_bot
ハマったポイント
Line botとはあんまり関係ないのですが、スクレイピングするのが初めてだったのでスクレイピングにかなり時間がかかりました笑
結果
今後はプッシュ通知などを使って改良していきたいです!