Paladinsの簡易レポートをDiscordに毎日送信するbotを作った
作ったもの
毎日Paladinsの戦績の簡易レポートをDiscordに送信する「Paladins Diary」。
仕組み
戦績を詳細に見ることができるPaladinsGuruという非公式のWebサイトから情報を取得してDiscordに送る。
そういう処理をPythonで書いて定期実行している。
それぞれの処理を実現させるための登場人物はAWS LambdaとSelenium。
SeleniumでWebスクレイピングして情報を取得し、
集計してDiscordに投げるスクリプトをAWS Lambdaにアップロードして定期実行している。
動かしているスクリプトの動きはこんな感じ。
txtファイルには以下3つを書いた。
- メンバー各々のPaladinsGuruのURL
- DiscordのWebHook URL
- 送信文のテンプレート
この辺は状況に応じて変わると思ったのでソースコードに触らなくても編集できるように外出ししておいた。実際、文面のテンプレートとか納得いかずにコロコロ変えたりした。
環境構築やLambdaでの定期実行、Webスクレイピング周りの実装については以下の記事を大いに参考にさせていただいた。
qiita.com
開発環境
AWS Cloud9を利用。
ブラウザで動くIDE。裏でEC2のインスタンスが動いている。
簡単に言うと、Linuxサーバーを借りてブラウザ経由で開発できる。
AWS初心者マンだけど、これはめちゃめちゃ便利だと思った。
どんなデバイスから開いても同一の環境が使えるので、病院の待ち時間にiPadでバグ一つ解決できたりした。
参考にさせていただいた記事に書いてあったので真似してCloud9で開発した。
テキストファイルを読み取る
読み取りPythonファイルとテキストファイルはこういう位置関係。
import os def get_text(): #pythonファイルと同じ階層に置いたconfigフォルダのパスを格納 path = os.path.dirname(__file__) + "/config/" #configフォルダ内にある送信メッセージのテンプレートを読み込み with open(path + "message_template.txt",encoding="utf_8") as f: message = f.read() #WebHook URLを読み込み with open(path + "webhook_url.txt",encoding="utf_8") as f: webhook_url = f.read() #名前とPaladinsGuruのURLを取得 with open(path + "user_list.txt",encoding="utf_8") as f: user_list = f.readlines() #ここをreadlinesにすると1行ずつlistに格納できる return message,webhook_url,user_list
たったこれだけ。超簡単。
参考にさせていただいた記事:Pythonでファイルの読み込み、書き込み(作成・追記) | note.nkmk.me
ブラウザを開いて戦績を読み取る
正直これはあんま理解せずに見様見真似で作ったので、ヤバいコードになっている自信ある。
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException import time import os date=[] elo=[] sr=[] champ=[] def webscrape(url): # webdriver settings options = webdriver.ChromeOptions() options.binary_location = os.path.dirname(__file__) +"/bin/headless-chromium" options.add_argument("--headless") options.add_argument("--no-sandbox") options.add_argument("--single-process") driver = webdriver.Chrome( #chromedriverのファイルパスを指定 executable_path=os.path.dirname(__file__) +"/bin/chromedriver", chrome_options=options ) driver.get(url) driver.implicitly_wait(60) # 要素が出現するまでの待機時間の上限 targetElement=driver.find_element_by_xpath('//*[@id="cw"]/section/section/div[2]/div/div[2]/div[2]/div[1]/div[1]') for i in range(2,26): date_check = driver.find_element_by_xpath('//*[@id="cw"]/section/section/div[2]/div/div[2]/div['+str(i)+']/div[1]/div[1]').text if 'HOURS' in date_check or 'MINUTES' in date_check: date.append(date_check) elo.append(driver.find_element_by_xpath('//*[@id="cw"]/section/section/div[2]/div/div[2]/div['+ str(i) +']/div[1]/div[2]/div[2]').text) sr.append(driver.find_element_by_xpath('//*[@id="cw"]/section/section/div[2]/div/div[2]/div['+ str(i) +']/div[1]/div[2]/div[3]').text) champ.append(driver.find_element_by_xpath('//*[@id="cw"]/section/section/div[2]/div/div[2]/div['+ str(i) +']/div[2]/div[2]/div[2]/div[1]').text) else: break return date,elo,sr,champ
//*[@id="cw"]/section/section/div[2]/div/div[2]/div['+str(i)+']/div[1]/div[1]みたいな部分はxpathといって、ブラウザの開発者ツールを使って取得できる。
HTMLの構造に従って法則性があるので、何箇所かxpathを比較して、変数にできる部分を見つけ無理やりループ処理に落とし込んだ。
また、マッチ時間にHOURSかMINUTESという文字が含まれていれば24時間以内のマッチということになるので、条件式に組み込んで24時間以内の情報を読み込むようにした。
ページの構造が変わればxpathも変わるし、文言が変われば条件式が使い物にならなくなる。
ページの仕様への依存度が高く、頻繁に更新されるサイトをスクレイピングしたいならコードの改修がその都度発生する。はっきり言ってだるい。
定期的に実行するスクリプトにWebスクレイピングは適していない。今回の学び。
参考にさせていただいた記事:
AWS Lambdaで動的サイトのwebスクレイピングをしてtwitterに投稿するbotを作った - Qiita
Seleniumで待機処理するお話 - Qiita
Selenium webdriverよく使う操作メソッドまとめ - Qiita
Discordに投げる
WebHookという仕組みを使って簡単にメッセージを送信できる。
とても簡単に扱えるので、一方的に送るだけのbotを作るときには便利。
import requests #message_sendには送信する文字列を入れておく #webhook_urlにはWebHook URLを入れておく main_content = { "content": message_send } requests.post(webhook_url,main_content)
参考にさせていただいた記事:discordのwebhookをpythonのrequestsのpostで使う | jibundex
ちなみにWebHook URLはサーバ管理画面から取得できる。
Lambdaに載せて定期実行する
大いに参考にさせていただいた記事の内容を丸々実行。
qiita.com
Paladinsの戦績がPaladinsGuru上で見えるようになるまで時間差があったので、身内のプレイが落ち着く朝方に送信して「昨日の分のレポート」とすることにした。
実行スケジュールを定義するCron式については謎の仕様に苦戦。
「cron(00 19 * * ? *)」とすれば毎日UTC19時(日本時間朝4時)に動くようにできた。
結果
こうなる。
ELO、SRは偉大なるPaladinsGuru様を眺めていて欲しいと思った情報だったので入れた。
管理者の方いわくELOは今後廃止する予定らしい。そうなると色々と対応が必要になる。