転売ヤーを撲滅するリングフィットアドベンチャー入荷botの裏側

概要

リングフィットアドベンチャーがAmazonに定価で入荷したときにお知らせする
リングフィットアドベンチャー入荷bot
を作りました。
このbotの裏側を紹介します。
転売ヤーを撲滅します。

Image from iOS.jpeg

背景

リングフィットアドベンチャー、大人気ですね。
発売してから品薄状態が続いています。
この品薄は、転売ヤーによる転売を目的とした大量購入が背景にあります。
実際に、Amazonには定価(8778円)を大きく超えた価格(約12000円)での転売が数百点ほど存在しています。

転売ヤーに負けずに定価で買うには、入荷後いち早くポチる必要があります。
そこで、リングフィットの定価での入荷をお知らせする機能を作ろうと考えました。
まずは自分がリングフィットを手に入れることを目的に作りました。

一方で、この機能を公開することで、転売ヤーを撲滅することができるとも考えました。
転売ヤーの存在を消すことは難しいです。
しかし、この機能を使えば、転売ヤーから買わざるを得ない状況を打破することはできます。
一般購入者が転売ヤーと同じ土俵で戦える場を用意することで、転売ヤーから買う必要性がなくなり、結果的に転売ヤーを撲滅することにつながると考えました。

クリスマスプレゼントにリングフィットを贈りたいサンタクロースたち。
お年玉を握りしめてリングフィットを買いたい子どもたち。
ITのチカラで戦う私。

転売ヤーを撲滅します。

目的

  • 自分がリングフィットアドベンチャーを買う
  • サンタクロースや子どもたちがリングフィットアドベンチャーを買うチャンスを提供する
  • 転売ヤーを撲滅する

システム構成

転売ヤーを撲滅するシステム構成を説明します。

リングフィットqiita.png

botプログラムはAWS EC2上で動いています。
EC2にはpythonスクリプトとcrontabの設定ファイルがデプロイされています。

フロー

転売ヤーを撲滅するフローを説明します。

システムのジョブフロー

  1. cronが毎分、以下の処理を行うpythonスクリプトを実行します。
  2. selenium→chrome|Amazonのリングフィットアドベンチャーの商品ページにアクセスします。
  3. selenium|getしたhtmlをパースし、リングフィットの価格を取得します。
  4. python|リングフィットの価格が定価かどうかを判定します。
  5. selenium→chrome|定価の場合、twitterにログイン&ツイートします。

ユーザの利用フロー

  1. リングフィットアドベンチャー入荷botをフォローします。
  2. 通知をonにします。
  3. 入荷を知らせるツイートの通知を受け取ります。
  4. いち早くamazonにアクセスし、定価8778円のリングフィットをポチります。
  5. 数日でリングフィットが届きます。

ソースコード

転売ヤーを撲滅するソースコードを紹介します。

python & selenium & chrome

amazon.py
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
import json
import urllib.request
import datetime
import pytz

# リングフィットの商品ページのURL
url = 'https://www.amazon.co.jp/%E4%BB%BB%E5%A4%A9%E5%A0%82-%E3%83%AA%E3%83%B3%E3%82%B0%E3%83%95%E3%82%A3%E3%83%83%E3%83%88-%E3%82%A2%E3%83%89%E3%83%99%E3%83%B3%E3%83%81%E3%83%A3%E3%83%BC-Switch/dp/B07XV8VSZT/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&keywords=%E3%83%AA%E3%83%B3%E3%82%B0%E3%83%95%E3%82%A3%E3%83%83%E3%83%88&qid=1575977457&sr=8-1'

# seleniumからchromeを立ち上げ、リングフィット商品ページにアクセスする
options = webdriver.ChromeOptions()
options.add_argument('--headless')
driver = webdriver.Chrome(options=options)
driver.get(url)

# ツイートする関数
def tweet(message):
    # twitterアカウント
    account = 'ringfit_hoshii'
    password = 'hogehoge'
    # selenium起動
    options = webdriver.ChromeOptions()
    options.add_argument('--headless')
    driver = webdriver.Chrome(options=options)
    driver.set_window_size(height=877, width=1440)
    # ログインページを開く
    driver.get('https://twitter.com/login/')
    time.sleep(3)  # 動作止める
    # accountを入力する
    element_account = driver.find_element_by_class_name("js-username-field")
    element_account.send_keys(account)
    # パスワードを入力する
    element_pass = driver.find_element_by_class_name("js-password-field")
    element_pass.send_keys(password)
    # ログインボタンをクリックする
    element_login = driver.find_element_by_xpath('//*[@id="page-container"]/div/div[1]/form/div[2]/button')
    driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
    element_login.click()
    # ツイート
    time.sleep(3)
    driver.find_element_by_xpath('//*[@id="react-root"]/div/div/div/main/div/div/div/div[1]/div/div[2]/div[2]/div[1]/div/div/div[2]/div[1]/div/div/div/div/div/div/div/div[1]/div[1]/div/div/div/div[2]/div/div/div/div').send_keys(message)
    # time.sleep(1)
    driver.find_element_by_xpath('//*[@id="react-root"]/div/div/div/main/div/div/div/div[1]/div/div[2]/div[2]/div[1]/div/div/div[2]/div[2]/div/div/div[2]/div[3]').click()
    # ツイートの送信が完了するのを待つためのsleep
    time.sleep(2)
    driver.quit()
    return

# Amazonのリングフィット商品ページから価格を取得&定価判定する関数
def get_price(xpath):
    price1 = driver.find_element_by_xpath(xpath)
    p1str = price1.text[1:]
    p1int = int(p1str.replace(',',''))
    now = datetime.datetime.now(pytz.timezone('Asia/Tokyo'))
    message2 = "【入荷】リングフィットアドベンチャーが" + str(p1int) + "円で売られています!" + " Amazon URL: " + url + " datetime: " + str(now)
    # 定価判定
    if p1int > 9000 or p1int < 5000:
        return
    tweet(message2)
    return

# リングフィットの商品ページにはxpathの出現パターンがいくつかあるので、そのパターン分だけ価格取得を試す。
xpaths = [
'//*[@id="unqualifiedBuyBox"]/div/div[1]/span',
'//*[@id="priceblock_ourprice"]'
]
for xpath in xpaths:
    get_price(xpath)

driver.quit()

seleniumでのツイート実行はこちらを参考にしました。
xpathによる要素の取得はこちらと同様な手段で行いました。

crontab

cron.conf
# seleniumが参照するchrome driverが/home/ec2-user/binにおいてあるので、pathに追加しておく
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/ec2-user/.local/bin:/home/ec2-user/bin
# 毎分pythonスクリプトを実行する
* * * * * python3 /home/ec2-user/ringfit/amazon.py

初期設定&デプロイ

転売ヤーを撲滅するサーバ設定とデプロイを説明します。

サーバ(EC2)の初期設定

# 必要なリソースのダウンロード&インストール
sudo yum -y update
sudo yum -y install https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm
# chromeのバージョンにあわせたdriverをダウンロードする。
# バージョン確認コマンド
# yum info google-chrome-stable
wget https://chromedriver.storage.googleapis.com/78.0.3904.105/chromedriver_linux64.zip
unzip chromedriver_linux64.zip -d bin/
sudo yum -y install python3
sudo pip3 install Selenium
sudo pip3 install pytz
sudo yum -y install ipa-gothic-fonts ipa-mincho-fonts ipa-pgothic-fonts ipa-pmincho-fonts

デプロイ

amazon.pycron.confをデプロイします(省略)。
ここでは/home/ec2-user/ringfitにデプロイしたとします。

cronの設定

# デプロイ先
deploy_dir="/home/ec2-user/ringfit"

# crontabの設定(リセットしてからcron.confを読み込ませています)
crontab -r
crontab -u ec2-user ${deploy_dir}/cron.conf

動作

こんな感じのツイートが、入荷中に毎分届きます。

スクリーンショット 2019-12-24 0.24.27.png

Q&A

なぜselenium経由なの?

Amazon
curlコマンドやpythonからアクセスすると弾かれるため、seleniumでchromeを立ち上げ、一般ユーザと同じ手続きでアクセスする方法を取りました。

twitter
twitterの開発者アカウントを取得したのですが、なぜかAppを作る権限がありませんでした。
ぐぐると、twitterアカウントに電話番号を登録するといける、みたいな記述があったのですが、今度はなぜか電話番号の登録ができず、、、
あきらめてseleniumでchromeを立ち上げ、以下略です。

転売ヤーにとっても便利な機能では?

その通りです。しかし、転売ヤーたちはこのような機能を自前で持っているのが現状だと思います。
このbotの意義は、一般購入者が転売ヤーと同じ土俵に立てるようにすることです。

改善点

  • Amazonからの価格取得が本当に適当なxpathなので、もう少しロバストな方法を考えたいです。
  • EC2のコストがかかるので、無料枠の範囲で動かせるLambdaなどに移行したいです。
  • twitter APIをちゃんと使いたいです。

本音

  • ツイートに貼るリンクをアフィリエイトリンクにして、多少の収入を得たいです。せめてAWS代くらい、、、
  • あっという間に1000フォロワーくらい行くと思っていました。複数のアカウントやfacebookで宣伝しました。「リングフィット 売ってない」や「リングフィット 欲しい」で検索をかけて、毎日手動いいねで営業しました。その結果、増えません。50も行きません。全く増えません。なぜか?リングフィットを買えたら、もうbotは用済みなのでフォローを外されるからです。1分に1回の通知のうるささが後押ししています。裏を返せば、利用者がちゃんとリングフィットを買えているということなので喜ばしい反面、多くの人々にこの機能を提供して、転売ヤーを撲滅するという目的からは遠ざかっています。
  • フォロワーを増やすために、リングフィット以外の商品のお知らせも検討しましたが、例えばリングフィットが欲しいからといってAirPodsが欲しいわけではなく、興味のない商品のお知らせはノイズにしかならないと思いやめました。

おわりに

最後までお読みいただきありがとうございました。
初Qiita投稿でした。
これからも(もう少し真面目に)記事を書いていこうと思いますので、どうぞよろしくお願いします。

リングフィットアドベンチャーをまだ手に入れていない方、よかったらご利用ください↓
リングフィットアドベンチャー入荷bot

定価で買えたリングフィットアドベンチャー↓
EL6HPVZVAAAN7zX.jpeg

感謝の声↓
Image from iOS (1).jpeg

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account