現在ルームシェアをしており、電気代など毎月の公共料金をルームメイトのいるLINEグループに通知するプログラムを作成しました。
公共料金の明細をLINEボット通知
今回のプログラムを実行すると以下のような通知がLINEに届きます。
MoneyFowardの明細の情報を取得してLINEボットで通知しています。
※電気のメーターが二つある特殊な家のため、電気代の項目が二つあります。
※水道代は2ヶ月に1回の請求のため、ここでは0円になっています。
※ガス代に関しては月末時点でMoneyFowardに明細が反映されていなかったため、0円になっています。
スクレイピング実装
必要に応じて以下をインストールします。(2021/11/8 更新)
$ brew install chromedriver
$ brew install oath-toolkit
$ pip3 install python-dotenv
$ pip3 install line-bot-sdk
$ pip3 install selenium
$ pip3 install webdriver-manager //追加
main.pyの実装
main.pyファイルを作成し、スクレイピング処理を実装します。(2021/11/8 更新)
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager #追加
from time import sleep
from linebot import LineBotApi
from linebot.models import TextSendMessage
from dotenv import load_dotenv
import os
import subprocess
import re
import datetime
# Load .env file
load_dotenv()
# Get environment variables
CHANNEL_ACCESS_TOKEN = os.environ['CHANNEL_ACCESS_TOKEN']
DESTINATION_LINE_ID = os.environ['DESTINATION_LINE_ID']
EMAIL = os.environ['EMAIL']
PASSWORD = os.environ['PASSWORD']
TWO_STEP_AUTHENTICATION_SETTING_CODE = os.environ['TWO_STEP_AUTHENTICATION_SETTING_CODE']
line_bot_api = LineBotApi(CHANNEL_ACCESS_TOKEN)
def get_monthly_bills():
# Define browser
options = webdriver.ChromeOptions()
options.add_argument("--headless")
options.add_argument("--disable-gpu")
options.add_argument("--hide-scrollbars")
options.add_argument("--single-process")
options.add_argument("--ignore-certificate-errors")
options.add_argument("--window-size=880x996")
options.add_argument("--no-sandbox")
options.add_argument("--homedir=/tmp")
options.add_argument(
f'user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36')
browser = webdriver.Chrome(
ChromeDriverManager().install(), #追加
options=options
)
print('start login')
# Jump to email sing-in page
url = 'https://id.moneyforward.com/sign_in/email'
browser.get(url)
sleep(3)
# Enter email
elem_loginMethod = browser.find_element_by_xpath(
'/html/body/main/div/div/div/div/div[1]/section/form/div[2]/div/input')
elem_loginMethod.send_keys(EMAIL)
# Jump to password input page
elem_login = browser.find_element_by_xpath(
'/html/body/main/div/div/div/div/div[1]/section/form/div[2]/div/div[3]/input')
elem_login.click()
sleep(3)
# Enter password
elem_password = browser.find_element_by_xpath(
'/html/body/main/div/div/div/div/div[1]/section/form/div[2]/div/input[2]')
elem_password.send_keys(PASSWORD)
# Jump to tow-step-authentication page
elem_login = browser.find_element_by_xpath(
'/html/body/main/div/div/div/div/div[1]/section/form/div[2]/div/div[3]/input')
elem_login.click()
sleep(3)
# Receive auth code for tow-step-authentication
two_step_authentication = ['oathtool', '--totp',
'--base32', TWO_STEP_AUTHENTICATION_SETTING_CODE]
auth_code = re.findall(
r'\d+', subprocess.check_output(two_step_authentication).decode('utf-8'))
# Enter auth code
elem_auth_number = browser.find_element_by_xpath(
'/html/body/main/div/div/div/section/div[1]/section/form/div[2]/div/div[1]/input')
elem_auth_number.send_keys(auth_code[0])
# Jump to already-logged-in servise list page by Money Foward ID
elem_auth = browser.find_element_by_xpath(
'/html/body/main/div/div/div/section/div[1]/section/form/div[2]/div/div[2]/input')
elem_auth.click()
sleep(3)
# Jump to Money Forward ME account select page
elem_money_forward = browser.find_element_by_xpath(
'/html/body/main/div/div/div/div[1]/div/ul/li/a')
elem_money_forward.click()
sleep(3)
# Jump to Money Forward ME Top page
elem_choose_account = browser.find_element_by_xpath(
'/html/body/main/div/div/div/div/div[1]/section/form/div[2]/div/div[2]/input')
elem_choose_account.click()
sleep(3)
# Jump to Money Forward ME household expenses page
elem_household_expenses = browser.find_element_by_xpath(
'//*[@id="header-container"]/header/div[2]/ul/li[2]/a')
elem_household_expenses.click()
print('start getting bills')
last_month = datetime.datetime.now().month - 1
text = f'{last_month}月の公共料金\n'
bills = [{'title': '電気代: ', 'service': '楽天でんき'},
{'title': 'ガス代: ', 'service': '楽天ガス'},
{'title': '水道代: ', 'service': '水道利用'},
{'title': '通信費: ', 'service': '楽天ブロードバンド'}]
# Create text content to send to LINE
text = calc_bills(browser, bills, text)
messages = TextSendMessage(text=text)
# Push massages to LINE
line_bot_api.push_message(DESTINATION_LINE_ID, messages=messages)
print('everything done')
browser.close()
def calc_bills(browser, bills, text):
total = 0
for bill in bills:
span_tags = browser.find_elements_by_xpath(
f'//span[contains(text(), "{bill["service"]}")]')
# Add only available bills to text
if not span_tags:
text += f'\n{bill["title"]}0円'
else:
for span_tag in span_tags:
price = span_tag.find_element_by_xpath('../../../td[4]')
print(price.text)
text += f'\n{bill["title"]}{price.text[1:]}円'
total += int(price.text[1:].replace(',', ''))
# Get comment depending on how much money you have spent on utitlities
comment = get_comment(total)
total = f'\n\n合計: {total}円\n{comment}'
text += total
print(text)
return text
def get_comment(total):
comment = ''
if(total > 50000):
comment += '富豪。'
elif(total > 40000):
comment += '贅沢してんじゃないの!?節約しよ!'
elif(total > 30000):
comment += 'もうちょっと出費抑えたき!'
elif(total > 20000):
comment += 'ぼちぼちの出費やね'
elif(total > 10000):
comment += '4桁円まで削ろう'
else:
comment += '前人未到の域、節約の神。'
return comment
get_monthly_bills()
.envファイルの設定
.envファイルに環境変数を定義します。
CHANNEL_ACCESS_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
DESTINATION_LINE_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
EMAIL=xxxxxxxxxxxxxxxxxxx
PASSWORD=xxxxxxxxxxxxxxxxxxxxx
TWO_STEP_AUTHENTICATION_SETTING_CODE='xxxx xxxx xxxx xxxx xxxx'
※1 LINEボットの作成、groupIdやuserId取得の方法が分からない方は以下の記事を参照すると良いかもしれません。
LINE BOTを作ってMessaging API でグループにメッセージを送る
※2 以下のステップでニ段階認証を突破するのに必要な20桁の英字を取得します。
1. MoneyFoward ME にログインする。
2. トップ画面の「設定」をクリックする。
2. アカウント設定 >> ログイン設定の「こちら」のリンクをクリックする。
3. 「二段階認証の設定」をクリックする。
4. 「有効にする」をクリックする。(既に有効になっている方は、一度無効にする必要があります)
5. 「コードを入力して設定」の欄に記載されている20字の英字をメモしておく。(画面の指示に従い、実際に認証アプリをダウンロードして二段階認証の設定を完了する。)
6. 5.でメモした20桁の英字を.envファイルの以下の環境変数の値にコピペする。
TWO_STEP_AUTHENTICATION_SETTING_CODE='xxxx xxxx xxxx xxxx xxxx'
以上の設定を行い、ターミナル上で python3 main.py
を実行するとプログラムが走ります。
取得金額はターミナル上でも確認できるので、LINEに通知する必要がない場合は以下のソースコードをコメントアウトしていただければと思います。
line_bot_api.push_message(DESTINATION_LINE_ID, messages=messages)
ソースコードのアレンジ
以下のソースコードを修正することでスクレイピング対象の明細を変更できます。
last_month = datetime.datetime.now().month - 1
text = f'{last_month}月の公共料金\n'
bills = [{'title': '電気代: ', 'service': '楽天でんき'},
{'title': 'ガス代: ', 'service': '楽天ガス'},
{'title': '水道代: ', 'service': '水道利用'},
{'title': '通信費: ', 'service': '楽天ブロードバンド'}]
text
変数、title
プロパティに設定している文字列はLINE上で表示するテキストの内容です。好きなように変更できます。
serive
プロパティに設定しているのはMoneyFoward上の明細の内容列の値です。
私は楽天でんきを使用しているので、service
プロパティに「楽天でんき」を設定しています。明細の内容列の値に「楽天でんき」という文字列が含まれる明細の金額を取得するようになっています。
使用しているインフラサービスが異なれば、ソースコード上のservice
プロパティの値も変更する必要があります。
LINEボットに送信するコメントも以下のメソッドの中身を修正することで自由に変えられます。
def get_comment(total):
comment = ''
if(total > 50000):
comment += '富豪。'
elif(total > 40000):
comment += '贅沢してんじゃないの!?節約しよ!'
elif(total > 30000):
comment += 'もうちょっと出費抑えたき!'
elif(total > 20000):
comment += 'ぼちぼちの出費やね'
elif(total > 10000):
comment += '4桁円まで削ろう'
else:
comment += '前人未到の域、節約の神。'
return comment
あとがき
pythonはほとんど触ったことないので大変でしたが、スクレイピングで取得した情報をLINEで通知するとこまで実装できて良かったです。
今後の展望
- 例外処理の追加
- AWSにホスティングして、月末のLINE通知を自動化する
- LINE上からスクレイピングする明細の月と取得項目を設定できるようにする
現時点では手動でプログラムを実行している(しかも月末を狙って実行せねばならない)ので、少し手間がかかります。早くAWSにホスティングして作業を自動化したいところ。というかそれができないとあまり意味ない。。。
何はともあれ、今回の記事が少しでもどなたかの参考になれば幸いです。
参考URL
XPathって何? 取得方法など分かりやすく解説 – WinActor
Python+Seleniumで2段階認証(6桁のパスコード)を突破する全手順
【日記】Seleniumである特定の文字列の近くにある要素を取得する
PythonでLINE Bot APIを使ってプッシュ通知を実装する
コメント
[…] 二段階認証をMoneyFoward ME 側で設定して下さい。こちらのページが参考になります。 […]