Pythonで始めるDIY 第1章(1)寿司打を自動化してみよう!

Pythonで始めるDIY 第1章(1)寿司打を自動化してみよう!

パソコンでキーボードをカタカタと打つ練習をするのにあたって、一度は使ったことがあるのではないでしょうか?

寿司打(公式ページはこちら

最初の頃は3000円コースで損をしただとか、色々あったと思いますが、どうですか? 今やってみたら、10000円コースでも結構お得な結果が出てくるのではないでしょうか?

今回から、こいつを自動化してみたいと思います。

重要な追記(2020年7月30日)

寿司打WebGL版の正式なQ&Aを見ていただくとわかりますが、現在ではこうした自動化ツールの利用を禁止しているようです。

このサイトで書かれていることは「過去にはこんなことがあったんだな」程度で留めておいてください。

※注意※

以降はプログラミング言語Pythonを用います。Ruby on Railsやその他のプログラミング言語の場合は、ソースコードの書き方が異なります。でも、方針としてはほとんど変わらないので、作ってみたい方は、適度にググりながら、自分で組み立てていってください。

必要なもの

インストールの仕方については、各自ググってください。すぐに出てきます。

  • Python 3.x
  • pip
  • anaconda(こいつをインストールするには結構時間がかかります)
  • Pillow
  • Selenium
  • PyOCR
  • Chrome Driver

ちなみに私の環境

これを書いておかないと実行した時にLinuxやWindows系の人たちが困惑してしまうかもしれないので、一応書いておきます。

  • MacBook Air (13-inch, 2017)
  • macOS Mojave 10.14.4
  • Python 3.7.2
  • Conda 4.6.8

完成予定図

やっぱり、モチベを上げるには、完成予定図があるべきだと考えています。頭の中で構想を練るのも良し、絵を描いてみるのも良し。

ここでは実際に、私が作ったプログラムで動かしたときの様子を載せておきます。

こんな感じで、右にログが出力されて、左にブラウザーが開かれます。

高級モードで120秒を超えるほど、タイピングが速いです(最終的に自動化していることがOpenGL側でバレて、5分くらいで止まってしまうが)

ちなみに背景はHitenさんの絵です。Pixivとかで調べてください。

さあ、始めよう!

料理を作ってゆく感覚で説明してゆきます。

はじめの段階では、ディレクトリーやファイルの階層構造としては、以下のようになっています。ここから必要になれば、追加してゆきます。

編集するのはおおかた、main.pyに限ります。

autosushida
|--main.py
|--chromedriver

1. ブラウザーを起動させるコードを書く

まずはブラウザーを起動させてみたいと思います。

# main.py
from selenium import webdriver
# Chrome Driverのパス
driver_path = './chromedriver'
# ドライバーを開く
driver = webdriver.Chrome(driver_path)

これを書いたら保存して、以下を実行してみてください。

python main.py

これでドライバーを開くことができました!

でもちょっと待ってください!

実はこのままではあまりよろしくない事態が起きます。何かというと、このプログラム、ドライバーを閉じる動作が書かれていません。このままだと、ドライバーを開く動作に関わったメモリーの動作に不具合を生じさせてしまいます(これをメモリーリークという)。なので以下の文をコードの下に追加します。

# main.py
input("何か入力してください")

# ドライバーを閉じる
driver.close()
driver.quit()

これでメモリーリークを起こさずに済むようになりました。

これを実行してみると、ドライバーが開いた後、ターミナル上でEnterキーを打つと、ドライバーが閉じます。

次に、ドライバーに寿司打のウェブページを開かせます。プログラムを以下のように書き換えます。

# main.py
...
# ドライバーを開く
driver = webdriver.Chrome(driver_path)

# OpenGL版の寿司打を開く
target_url = 'http://typingx0.net/sushida/play.html'
driver.get(target_url)

input("何か入力してください")

# ドライバーを閉じる
driver.close()
...

これを実行すると、寿司打のページが開ましたね。

なお、2019年4月現在、寿司打にはFlashPlayer版とOpenGL版がありますが、FlashPlayerのサポートは2020年に終了してしまい、OpenGL版の方が都合が良いので、今回はOpenGL版のURLを指定しました。

2. クリック操作をさせる

次に、寿司打のページでスタートボタンのクリック操作をさせたいと思います。

OpenGL版寿司打がだいたい10秒間で読み込まれた後、こんな感じになりますね。

では、スタートボタンの座標をスクショなりをして調べてみてください。これはお使いのPCによります。

ちょっと見づらいかもしれませんが、左上端の部分を原点(0,0)とします。私の使用しているPCではだいたい600,386でした。

という風に、PC依存になるのはちょっと嫌なので、ウィンドウサイズを固定化したいと思います。コードを以下のように書き換えます(先に言わなくてごめんなさい)。

# main.py

...
# ドライバーを開く
driver = webdriver.Chrome(driver_path)

# ウィンドウサイズを固定
# +123としているのは、
# 「Chromeは自動テストソフトウェアによって制御されています。」
# という部分を考慮している
window = (500, 420+123)
driver.set_window_size(*window)

# OpenGL版の寿司打を開く
target_url = 'http://typingx0.net/sushida/play.html'
...

このように書き換えると表示画面は500×420に固定されます。ちょっと小さくてダサいかもしれませんが、これは後々に出てくる文字の読み取りの処理速度を上げるためです。

で、実行してみると、こんな感じになると思う。

見て分かる通り、表示がずれています。本当はこんな感じで表示させたいのに。

では、こんな感じで表示させるように書き換えましょう。

# main.py

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains

# Chrome Driverのパス
...

...
window = (500, 420+123)
driver.set_window_size(*window)

# OpenGL版の寿司打を開く
target_url = 'http://typingx0.net/sushida/play.html'
driver.get(target_url)

# 寿司打のゲーム画面をずらすために書く
target_xpath = '//*[@id="game"]/div'
webgl_element = driver.find_element_by_xpath(target_xpath)
actions = ActionChains(driver)
actions.move_to_element(webgl_element).perform()

input("何か入力してください")
...

これを実行すると、無事、ページが開くようになりました!

ここで「動作はしたけど、はて、driver.find_element_by_xpathとはなんぞや?」となった人もいることでしょう。

これはSeleniumに含まれているメソッドです。XPathというものを使って、表示画面をずらすために必要なので書いてあります。試しに、http://typingx0.net/sushida/play.htmlから、寿司打のページのHTMLを取得してみてください。

HTML内の<div class=”webgl-content”>となっている部分のXPathが上記のようになっているので、これに従って、表示画面をずらします。

Seleniumについて詳しく知りたい方は「Selenium python 使い方」とかとググってください。Qiitaあたりにたくさん説明が書かれてあると思います。

このとき、スタートボタンの座標は250,250くらいになると思います。私は256という数字が好きなので、スタートボタンの座標を250,256に固定化させて話を進めます。

# main.py

from time import sleep
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
...

...
actions = ActionChains(driver)
actions.move_to_element(webgl_element).perform()

# クリックする前にロード時間待機
sleep(10)

# スタートボタンの座標
center_x = 250
center_y = 256

# スタートボタンをクリックする
actions = ActionChains(driver)
actions.move_to_element_with_offset(webgl_element, center_x, center_y).click().perform()

print("スタートボタンをクリックしました。")
input("何か入力してください")
...

実行してみてください。スタートボタンが押されたら成功です!

無事、スタートボタンを押せましたね!

次回予告等

どうでしたか? 思い通りに動きましたか?

もし思い通りに動かなくて、ググってもよく分からなかったという人は、コメント欄に質問なりをしてください。答えられれば答えます。

今回は、スタートボタンを押すところまで実装できました。次回は、「お勧めコースをクリックさせて、文字を入力させる」を実装してゆきたいと思います。

ではまた。

今回までのコード例

ここまでで、コードは以下のようになります。参考にしてみてください。

# main.py

from time import sleep
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains

# Chrome Driverのパス
driver_path = './chromedriver'
# ドライバーを開く
driver = webdriver.Chrome(driver_path)

# ウィンドウサイズを固定
# +123としているのは
# 「Chromeは自動テストソフトウェアによって制御されています。」
# という部分を考慮している
window = (500, 420+123)
driver.set_window_size(*window)

# OpenGL版の寿司打を開く
target_url = 'http://typingx0.net/sushida/play.html'
driver.get(target_url)

# 寿司打のゲーム画面をずらすために書く
target_xpath = '//*[@id="game"]/div'
webgl_element = driver.find_element_by_xpath(target_xpath)
actions = ActionChains(driver)
actions.move_to_element(webgl_element).perform()

# クリックする前にロード時間待機
sleep(10)

# スタートボタンの座標
center_x = 250
center_y = 256

# スタートボタンをクリックする
actions = ActionChains(driver)
actions.move_to_element_with_offset(webgl_element, center_x, center_y).click().perform()

print("スタートボタンをクリックしました。")
input("何か入力してください")

# ドライバーを閉じる
driver.close()
driver.quit()

寿司打WebGL版の正式なQ&Aを見ていただくとわかりますが、現在ではこうした自動化ツールの利用を禁止しているようです。

このサイトで書かれていることは「過去にはこんなことがあったんだな」程度で留めておいてください。