#背景
ちはっす。DAIです。
会社の同僚と自宅でよく作業会をやっています。音楽聞いて、料理食べて、あとはただコツコツとみんなでそれぞれが勉強するだけなのですが、とてもQOLが高いです。
#やりたいこと
チケットを譲ってくれる人を見つけられるサイトがあるそうです。ここの最新情報を毎日自動でSlackで更新するようにします。
おけぴチケット救済サービス(定価以下限定チケット掲示板)
ミュージカルや演劇、劇団四季、宝塚歌劇、クラシック、コンサート、ライブ等の空席救済を目的とした観劇鑑賞生活応援サイトです
そのサイトでは、自分の見たいタイトルの舞台の名前を入れると、そのチケットを渡してくれる人を探すことができるそうなのですが、毎日検索して確認するのが面倒だそうなので、これを毎日プログラムが取得して、まとめたデータをSlackに通知してくれたら便利じゃね?ってはなしになり、作ってみました。
処理の流れとしては
- サイトにSeleniumにアクセス
- Seleniumで検索語句を入力
- Seleniumで検索結果を取得
- それぞれの新着情報を見やすく一つのレポートにまとめる
- Slackで通知
- これを毎日自動化する
といった感じです。なのでこの技術を使えると、実質すべてのWeb上のデータをSlackで通知させられると思うので、便利です。
#TODO
- Cloud9上でSelenium・自動化プログラムのセットアップ
- SlackのWebhook登録を行う
- SlackのURLを取得
- コーディング
- 完成コード
- HerokuでDeployして自動化する
# Cloud9上でSeleniumのセットアップ
Cloud9上でSeleniumを使う方法に関しては、こちらの記事にまとめました。
【Python】Seleniumの定期処理をHerokuから実行するための環境構築方法 - Cloud9編-
一回Cloud9上でSeleniumの環境構築をする方法をまとめておきたいと思います。だれでも5分以内に同じ動作で、Cloud9上でSeleniumが利用できる環境をセットアップできることを目指します。
# Slackに登録してWebhookの登録を行う
まず最初に、Slackに登録します。
Where work happens
Slack is where work flows. It's where the people you need, the information you share, and the tools you use come together to get things done.
SlackでIDが登録されると、最初にデフォルトのチャットルームが作成されます。今度は、チャンネルを作成してください。そして、チャンネルを選択し、Add Incoming WebHooks integrationを押します。こんな感じで、WebhookのURLを取得してください。このURLに存在する部屋に、Slackでポストすることができるようになります。詳しくは下記のURLがとてもわかりやすいので、ご参考に。
SlackのWebhook URL取得手順 - Qiita
SlackのWebhook URLを取得するまでの手順を示す。 ChatにWebサービスの結果を通知するChatOps的なことをしたい。 通知先のChatツールとしてSlackを使用する Download Apps | Slack
# コーディング
さて、ここまで来ればあとは実装です。前半ではSeleniumでWebスクレイピングを行い、情報を抜き取ります。後半では、抜き取ったデータを加工して、Slackに投げます。とりあえず、完成コードをおいておきます。コードを書いていた当時、なぜか日本語変換ができないままコードを書いていたので、コメントは英語ですすいません。
from selenium import webdriver import time import requests # for slack import json # for slack #Phanttomjs Driverを入手 browser = webdriver.PhantomJS() # DO NOT FORGET to set path #TODO set search query query = "ワンピース" # set search query here url = "http://okepi.net/top.aspx" #TODO access to targeted URL print("Successfully accessed to the targeted page! title:{}".format(browser.title)) #TODO insert query into #txtTopSearch queryField = browser.find_element_by_xpath("//*[@id='txtTopSearch']") # This x-path locates in search form queryField.send_keys(query) #Insert query message into search form #TODO submit and load page submitButton = browser.find_element_by_xpath('//*[@id="btnTopSearch"]') submitButton.click() browser.implicitly_wait(5) # seconds print("Submit completed. now at:{}".format(browser.title)) #TODO retrieve ticket data tables = browser.find_elements_by_class_name("tbl_j") # print("Number of table:{}".format(len(tables))) #TODO insert data that you want from http://okepi.net/all.aspx?key=performance&value=%83%8F%83%93%83s%81[%83X contents = [] #Stores each content later for table in tables: try: print("#######################") title = table.find_element_by_class_name("goleft") #without this, title retrieves web-element object print("title:{}".format(title.text)) titleURL = title.find_element_by_css_selector("a").get_attribute("href") print("titleURL:{} ".format(titleURL)) datetime = table.find_element_by_class_name("pdatetime").text print("datetime:{} ".format(datetime)) price_place = table.find_element_by_class_name("price").text print("price_place:{} ".format(price_place)) #TODO make summary of each data content = title.text + "\n" + datetime + "\n" + price_place + "\n" + titleURL + "\n" + "###################\n" print(content) contents.append(content) # insert contents so slack can post only once except Exception as e: #error message appears when something goes wrong print(e) continue #TODO create summary for slack summary = "==============" + query + "の新着情報!================\n" # for content in contents: summary = summary + content print(summary) # This summary is sent to slack finally #TODO post summary on slack slackURL = "[your slack app URL]" # https://api.slack.com/custom-integrations requests.post(slackURL, data = json.dumps({ 'text': summary, # text that you will post 'username': u'Musical Reminder', # username (whatever name you want is fine) 'icon_emoji': u':ghost:', # profile emoji 'link_names': 1, # enables mention }))
で、これを実行するとこんな感じの結果となります。
さて、コードの説明をします。
from selenium import webdriver
import time
import requests # for slack
import json # for slack
seleniumは、仮想のウェブブラウザを起動して、人が操作ができるようなモジュールです。timeは時間を操作するモジュールで、requestsはslackにデータを送るためのモジュール、jsonは、データをJSON形式に変換できるモジュールです。
#Phanttomjs Driverを入手
browser = webdriver.PhantomJS() # DO NOT FORGET to set path
#TODO set search query
query = "ワンピース" # set search query here
url = "http://okepi.net/top.aspx"
#TODO access to targeted URL
browser.get(url)
print("Successfully accessed to the targeted page! title:{}".format(browser.title))
#TODO insert query into #txtTopSearch
queryField = browser.find_element_by_xpath("//*[@id='txtTopSearch']") # This x-path locates in search form
queryField.send_keys(query) #Insert query message into search form
browser.get(url)はurlにアクセスすることができるようなメソッドですね。
queryField = browser.find_element_by_xpath("//*[@id='txtTopSearch']")
これは、HTMLをX-Pathを取得します。このパスは、実際HTMLでいうところの検索フォームを指定しています。
queryField.send_keys(query)
さきほど指定した検索語句を、そのフォームの中に入れることができます。
#TODO submit and load page
submitButton = browser.find_element_by_xpath('//*[@id="btnTopSearch"]')
submitButton.click()
print("Loading...")
browser.implicitly_wait(5) # seconds
print("Submit completed. now at:{}".format(browser.title))
#TODO retrieve ticket data
tables = browser.find_elements_by_class_name("tbl_j") #<table class = "tbl_j">
print("Retrieved tables data")
print("Number of table:{}".format(len(tables)))
submitButton = browser.find_element_by_xpath('//*[@id="btnTopSearch"]')
submitButton.click()
次に、HTMLから検索ボタンがあるところをX-pathで取得しsubmitButtonに代入します。そして、そのボタンをclickします。こうすることで、指定した語句で検索された結果のページが表示されます。
さらに、HTMLでクラスの名前が"tbl_j"を取得することで、合致するすべてのクラスの要素を取得することができます。その後取得したデータ数を表示していますね。
contents = []
for table in tables:
try:
print("#######################")
title = table.find_element_by_class_name("goleft") #without this, title retrieves web-element object
print("title:{}".format(title.text))
titleURL = title.find_element_by_css_selector("a").get_attribute("href")
print("titleURL:{} ".format(titleURL))
datetime = table.find_element_by_class_name("pdatetime").text
print("datetime:{} ".format(datetime))
price_place = table.find_element_by_class_name("price").text
print("price_place:{} ".format(price_place))
#TODO make summary of each data
content = title.text + "\n" + datetime + "\n" + price_place + "\n" + titleURL + "\n" + "###################\n"
print(content)
contents.append(content) # insert contents so slack can post only once
except Exception as e:
print(e)
continue
次。その取得した複数のテーブルをfor文で回しています。そして、それぞれのチケット情報のタイトル、講演日付、チケットの値段、URLを取得し、それぞれのデータを結合して、contentの中に代入します。最後に、最初に空でつくったcontentsの中に代入します。こうすることで、それぞれの情報をまとめたcontentをcontentsの配列を作成します。
summary = "==============" + query + "の新着情報!================\n"
for content in contents:
summary = summary + content
print(summary)
最後に、一つ一つ取得したデータを、レポート用にまとめます。summaryの中に、それぞれのcontentを作成することで、まとめたレポートを作成することができます。
slackURL = "your slack url" # https://api.slack.com/custom-integrations
requests.post(slackURL, data = json.dumps({
'text': summary, # 投稿するテキスト
'username': u'Musical Reminder', # 投稿のユーザー名
'icon_emoji': u':ghost:', # 投稿のプロフィール画像に入れる絵文字
'link_names': 1, # メンションを有効にする
}))
そんなこんなで実行すると、Slackではこんな感じで出力されます!
# 自動化
自動化に関しては、少しセットアップが必要になるので、以下の記事を読んでやってみてください!【Python】Seleniumの定期処理をHerokuから実行するための環境構築方法 - Cloud9編-
一回Cloud9上でSeleniumの環境構築をする方法をまとめておきたいと思います。だれでも5分以内に同じ動作で、Cloud9上でSeleniumが利用できる環境をセットアップできることを目指します。
【Pythonで定期処理】 Cloud9を利用して、Seleniumでherokuから定期実行する
Python Cloud9を利用して、定期処理をherokuから行う Cloud9というIDEを利用して、PythonからSeleniumを利用し、あるサイトでいいねを自動化するプログラムを作った。 これを定期実行を行いたい。検索してみると、crontabを使えば定期実行ができるようだが、cloud9上では実行できないらしい。 ほかの代替案を考えたとき、 heroku ...
$ heroku addons:create scheduler:standard
$ heroku addons:open scheduler #URLをひらいてコードを設定
参考
【Python】Cloud9でSeleniumのセットアップを5分で行うためのチュートリアルhttp://review-of-my-life.blogspot.jp/2017/09/selenium-5-minutes-setup-on-cloud9.html
【Python】Seleniumの定期処理をHerokuから実行するための環境構築方法 - Cloud9編-
一回Cloud9上でSeleniumの環境構築をする方法をまとめておきたいと思います。だれでも5分以内に同じ動作で、Cloud9上でSeleniumが利用できる環境をセットアップできることを目指します。
>Python データ分析入門マニュアルに戻る
■入門マニュアル
〇 Python データ分析入門マニュアル - 実例を使ってWebスクレイピングからデータビジュアライゼーションまでやってみよう
〇 Pythonの環境構築でもう悩まない!初心者でも絶対にできるクラウドを使った環境構築方法!
■スクレイピング
〇 Python初心者が3カ月でWebスクレイピングができるようになるために必要な知識
〇 【Python】スクレイピング→データ収集→整形→分析までの流れを初心者向けにまとめておく ~Pythonに関するはてな記事を10年分スクレイピングし、Pythonトレンド分析を実際にやってみた~
■データビジュアライゼーション
〇 【Python初心者向け】データの取得・操作・結合・グラフ化をStep by Stepでやってみる - pandas, matplotlib -
■定期処理