体育館予約を自動化しようとした話

この記事はFablic Advent Calendar 16日目の記事です。 http://qiita.com/advent-calendar/2016/fablic

こんにちは。Fablicのサーバーサイドエンジニア、Jamesです。 業務とは関係ないところでちょっとしたシステムを作ろうとした話をします。

今回を機にこのブログ開設しました。気が向いたときに色々発信してたいと思います。

概要

  • 手段と目的
  • 東京都某区の体育館予約を自動化したい
  • 東京都某区の体育館予約システムについて
  • Selenium WebDriverを使って体育館予約を自動化
  • ログインしてみる
  • そしていよいよ予約へ・・・

手段と目的

よく「テクノロジーは手段であり、目的ではない」という言葉を聞きますが、果たしてその通りにテクノロジーを使えている人はどれくらいいるでしょうか?実際に業務でバリバリプログラムを書いているエンジニアも、自分の身の回りのことにその技術を生かしきれていないことは多々あるのではないでしょうか。

かくいう私もそうです。「少しでも世の中を便利にしたい」という思いでエンジニアになったにも関わらず、業務以外で技術を活かすことがなかなかできずに「自分の周りのことも何も変えられないのはなんだかなー」と考え、そんな自分に悔しさを感じていました。

東京都某区の体育館予約を自動化したい

話は変わり、私は中学でバスケットボールを始めてから今でも週1程度で続けております。なかなかハードなスポーツですが、体を動かすのは気分転換になります。好きなプレイヤーは昔からKobeですが、Mike Millerみたいなサイズがあるシューターとか、最近ではKenneth Fariedが好きです。日本人だと筑波大学の馬場雄大選手に注目しています。GW大学の渡辺雄太選手も大注目ですね!Bリーグも開幕して、なかなかバスケットボールシーンは熱くなっております。

しかし、そのバスケットボールを日本でする上で避けては通れない問題が一つあります。 それは 体育館がなかなか取れない ということです。

様々なつながりで集まった仲間でチームを作って活動していたのですが、体育館を取ることができない事が原因でなかなか集まって一緒にバスケットボールをすることができませんでした。

しかし、この状況を打開すべく、ついに先日東京都の某区でチーム登録を済ませました。 それまで知り合いのチームに遊びに行かせていただいたり練習もせずに試合に出ていたりしたのですが、体育館が取れるようになると話は変わってきます。

私たちは、「これで自分たちの練習ができる!」と意気込んでいました。

しかし 体育館予約が全然できない のです。

原因としては

  • 予約はWeb上で行うことができるのだが、システムにアクセスできる時間が限られており、見れる人が少ない(9:00~22:00)
  • 予約できる日が限られている参考
  • 登録団体が多すぎて、空きが出ない

ということが挙げられます。

せっかくチーム登録をしたのに体育館が取れない! これはどうにかしなければいけないと思い、私はこう思いました。

「そうだ、ついに自分の技術力を役立てることができる」

Selenium WebDriverを使って体育館予約を自動化

今回はWeb上の動作を自動化するということで、Selenium WebDriverを使ってみることにしました。 Selenium WebDriverの概要に関しては、ここを参考にしてくだい。

Rubyが使える点と、ちょっと調べてみて参考記事が多そうだったので選びました。

ログインしてみる

いきなり予約する前に、まずは自分のチームでログインしなければなりません。 まずはトップ画面からログイン画面に遷移して、自分のチームのトップ画面に行くところまで作りたいと思います。

対象のシステムでは、

  • トップページ
  • 予約するするボタンをクリック
  • ログイン画面
  • ユーザー情報を入力
  • ログイン成功

という流れでログインできるので、この流れを自動化していきたいと思います

*Selenium WebDriverの使い方の基礎に関してはこの辺りの記事を参考にしました。 こうして、以下のようなスクリプトを作り、ログインした後、ログアウトすることに成功しました。

require 'selenium-webdriver'

class GymReserver
  TOP_PAGE = 'https://example.com' #トップページのURL

  # 以下アカウント情報
  ACCOUNT = 'XXXXX'
  PASSWORD = 'XXXXX'
  CODENUM = 'XXXXX'

  attr_accessor :driver

  def initialize
    @driver = Selenium::WebDriver.for :chrome
  end

  def login_execute
    click_reserve # トップページで「予約する」ボタンをクリックする
    login # ユーザー情報を入力し、ログイン

    driver.find_element(:id, 'FOOT1BTN').click #ログアウトボタンをクリック

    sleep 5  #  トップページで待機

    driver.close()
  end

  def click_reserve
    driver.get TOP_PAGE
    switch_to_iframe
    driver.find_element(:id, 'BB0').click
    puts 'succeeded to click button'
  end
    
  def switch_to_iframe
    iframe = driver.find_element(:name, 'iframe')
    driver.switch_to.frame(iframe)
  end

  def login
    fill_account_info
    driver.find_element(:id, 'loginID').click
  end

  def fill_account_info
    [ACCOUNT, PASSWORD, CODENUM].each { |info| click_each_char_button(info) }
  end

  def click_each_char_button(info)
    info.chars.each { |char| driver.find_element(:id, "KEY#{char}").click }
  end
end

GymReserver.new.login_execute

デモ動画です。

youtu.be

少しハマったポイントとしては、なぜかシステムがすべてiframeで出力されるようになっていた ために、画面遷移して際はiframeを指定して、そのiframeの中から要素を探してボタンを入力しなければならなかった点です。

  def switch_to_iframe
    iframe = driver.find_element(:name, 'iframe')
    driver.switch_to.frame(iframe)
  end

ユーザー情報を入力しているメソッドで一つ一つの文字を要素から探し出してそれぞれクリックしているのも、普通のformではなくiframe内のformを利用して入力しなければならなかったためです。

  def fill_account_info
    [ACCOUNT, PASSWORD, CODENUM].each { |info| click_each_char_button(info) }
  end

  def click_each_char_button(info)
    info.chars.each { |char|  driver.find_element(:id, "KEY#{char}").click }
  end

市町村が運営している予約システムはこのように素敵な仕様になっていることが多いので、皆さんもぜひ気をつけて下さい。

そしていよいよ予約へ・・・

ログインまで出来れば、あとは予約のパターンを調べて同じように操作をするだけです。 ついにこの瞬間が来ました。

自分のの技術が私自身のためだけでなく、ITからかけ離れているチームメイト達の役にも立つことができる

私は高まる緊張を抑えながら空いている施設を探して、予約パターンを探しました。 一つ一つ施設を検索して、空いている日を探しました。 一箇所でも予約できる施設を見つけれれば私の勝ちです。

しかしここに大きな落とし穴があったのです。













いや、どの体育館も空いてないやないかい (ツ)_/¯