どうも。つじけ(tsujikenzo)です。今回は私の所属している「ノンプロ研」の2020年BT大会(忘年会のようなもの)の余興で発表したゲームについて書き留めていきたいと思います。
『ノンプロ研ガチャ』
ゲームの名前は『ノンプロ研ガチャ』と言いまして、ノンプロ研メンバーの自己紹介ページを開くと、1人のメンバーが登場するという内容です。
実はノンプロ研メンバー自己紹介ページは動的なWEBサイトになっておりまして、URLにアクセスする度に、ランダムに表示される仕様になっています。
(ブラウザのサイズによって表示される人数は変わります)
下準備
今回はPython、そしてクラウドで実行することのできるGoogle Colaboratoryでコーディングしていきたいと思います。Google ドライブの[+新規]をクリックすると、[その他]に[Google Colaboratory]がありますでしょうか。
このようなクラウドのエディタに直接書いていきます。(Google Colaboratoryについても、必要があれば記事にしたいと思います。)
記述したコードのファイルはGoogle ドライブに「○○.ipynb」形式で保存されますし、共同編集の際に起こる[競合]についても、ちゃんと競合内容を知らせてもらえるので別名保存や上書き保存の確認をすることができるので安心です。個人的な意見ですが、もうローカルでPythonを書くことはほぼ無くなりました。(書かないように心掛けています)
SeleniumとChromiumのインストール
動的なWEBサイトをスクレイピングするには、仮想ブラウザを操作して仮想ブラウザのHTMLを解析しようよという流れになります。Pythonで仮想ブラウザを操作するライブラリがselenium(セレニウム)です。
仮想ブラウザはfirefoxやIEやSafari対応など各社ブラウザに対応したドライバーが用意されておりますが、GoogleさんのChromeドライバーを使うことが多いようです。Google ColaboratoryではChromiumというChromeドライバーを使いますので下記のようにインストールをします。
#Chromiumとseleniumをインストール
!apt-get update
!apt install chromium-chromedriver
!cp /usr/lib/chromium-browser/chromedriver /usr/bin
!pip install selenium
日本語フォントのダウンロードもしておきます。
# 日本語フォントをダウンロードする。
!apt-get -y install fonts-ipafont-gothic
ライブラリのインポート
一つ一つの説明は割愛しますが、スクレイピングに便利なライブラリをインポートしていきましょう。
#SeleniumとBeautifulSoupのライブラリをインポート
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from bs4 import BeautifulSoup
import requests
#スクリーンショット用
import cv2
from google.colab.patches import cv2_imshow
webdriverの事前準備とブラウザサイズの変更
webdriverの詳細設定を事前に走らせておく必要があります。この時、ブラウザの表示サイズも指定しておきます。(ブラウザのサイズを変更することによって、メンバーを1名だけ表示させることができます。)
#上記事前準備とこれを走らせる必要あり
options = webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
driver = webdriver.Chrome('chromedriver',options=options)
driver.implicitly_wait(10)
# ブラウザサイズの変更
page_height = driver.execute_script('return document.body.scrollHeight')
driver.set_window_size(570,640)
ブラウザにアクセスする
さて、いよいよブラウザにアクセスする準備が整いましたが、URLリクエストを送ってみたところ、ドハマりしました💦Bodyは動的なページを生成してるので、HTMLソースは取れてうようなのですが、タグ情報が取れないのです💦
助けてノンプロ研!
10分迷ったらノンプロ研です。(実際は1時間ぐらい頑張ってましたが💦)Slackで相談したところ、「iframeだからじゃない?」「iframe回したらどうですか?」とのことで、コピペで動かせるコードを丸っと書いてもらいました。ノンプロ研メンバーはまぢで神です。(早い・短い・マウント取らない)
url = "URL"
driver.get(url)
iframe = driver.find_element_by_tag_name('iframe')
driver.switch_to.frame(iframe)
iframe = driver.find_element_by_tag_name('iframe')
driver.switch_to.frame(iframe)
取得したHTMLを見る限り、再エンコードすればいいのか?と思ってましたが、どうやらiframeに2回潜るという視点が必要だったようです。
.switch_to.frame(iframe)
知りませんでしたー。後で.prettify()メソッドで整形することで、綺麗なDOM操作ができます。
メンバーを一名表示(取得)する
さて、先頭のメンバーのbタグにメンバー名が入ってるようでしたので、こう書きました。
source = BeautifulSoup(driver.page_source, "html.parser").prettify()
soup = BeautifulSoup(source, "html.parser")
person = soup.find_all('h3')[0].find('b').text
取得できた値はこのようになってました。
\n カワムラ さん\n
おお👏👏👏
で、これでもまぁいいのですが、「あるメンバーだったら〇〇をする」という条件分岐を加えたいですね。
レジェンド達の存在
ノンプロ研のほぼ設立同時期から在籍してるメンバー6名と、2018年入会メンバー12名には特別な敬意を表したいと思っています(個人的な崇拝)ので、メンバー名をリストにリテラルに格納していきます。
そして今回はbタグの中身を整形(正規表現で一発でしょうけど。。)するより、listを検索条件として回した方が早いと思ったので、このようなコーディングをしてみました。
for i, name in enumerate(list):
if list[i] in person:
result = f'レジェンド{person}です。'
flag = False
if flag == True:
result = f'{person}です。'
flagはノンプロ研のペアプロでetau師匠に教わったけど中々使い道がないなぁって思ってたので、実践で使えてよかったです。
スクリーンショットを撮る
現在のブラウザの状況を標準出力に出して画面を確認しましょう。スクリーンショットは都度ドライブに保存したりもできますが、今回は割愛します。
# スクショ
filename = 'check.png'
downloaded = driver.save_screenshot('check.png')
img = cv2.imread('/content/' + filename, cv2.IMREAD_UNCHANGED)
cv2_imshow(img)
ゲーム開始!
ゲームを行う際は、まとめた関数を実行するだけです
print(get_member())
Google Colaboratoryはスマホからも実行することができるので、いつでも「ノンプロ研ガチャ」が引けますね!
まとめ
いかがでしたでしょうか。ノンプロ研内ではコードの全文を公開しておりますので、興味がある方は是非遊びにきてくだい。
この記事を書くにあたり、シンノユウキさんとkazuya sugimuraさんには大変お世話になりました。感謝申し上げます。