LoginSignup
6
3

More than 1 year has passed since last update.

puppeteerでポイ活を自動化しよう

Last updated at Posted at 2021-12-20

Webサービスでポイントを貯める(ポイ活)ため毎日のようにブラウザを手動で操作していませんか?
そんなときはブラウザ操作を完全自動化して楽をしましょう!

ツールってほどではないですが参考にどうぞ。
もちろんポイ活以外の操作も同様に自動化できます。

ソース(動けばいいってコードなので汚さはご容赦を…):

サマリ

  • 「SBI証券のWebサイトを毎日チェックし、新規IPOがあれば申し込む」というのを自動化した。申し込んでも大抵落選するのでIPOチャレンジポイントが貯まる。
  • 「Puppeteerでブラウザ操作」するコードをApp Engineへデプロイし、cronで自動実行する。
  • 毎月のGCP代は無料枠のおかげで10円だが、頻繁に稼働する場合は料金に注意

自動化していること

  • (日次) SBI証券のIPOチェックと申し込み自動化
    • 本記事で説明します。
    • 例えば2020年はコロナ禍でも85社、つまり85回申し込めたので、手動ではさすがに面倒です。
  • (月次) 新生銀行のポイントプログラム自動エントリー
    • 毎月フォームからエントリーすることで月の実績に応じたTポイントなどが得られます。毎月入力させてタッチポイントを増やそうとしているのでしょうか…?とても面倒です。
    • 私はエントリーを自動化して毎月Tポイントを約30ポイント獲得しています:innocent:

開発

以下、コードは一部をピックアップしたものです。

主な使用技術

  • App Engine (スタンダード環境)
  • Node.js / JavaScript
  • Puppeteer (ブラウザ操作用Nodeライブラリ)

Puppeteerによるブラウザ操作

ブラウザの起動。puppeteer.launchでブラウザを起動しつつBrowserオブジェクトを取得します。
App Engineのより小さいインスタンスで動くよう、起動オプション(Chromiumの一覧はこちら)をいくつかつけてメモリの使用量を減らします。

apply_ipo.js
import puppeteer from "puppeteer";

export const applyIPO = async () => {
  const browser = await puppeteer.launch(
    {
      args: [
        '--no-sandbox',
        '--disable-setuid-sandbox',
        '-–disable-dev-shm-usage',
        '--disable-gpu',
        '--no-first-run',
        '--no-zygote',
        '--single-process',
      ],
    }
  );
  try {
    // ブラウザの操作内容を書く
    await _apply(browser);
  } catch(e) {
    console.log("error", e);
  } finally {
    browser.close();
  }
};

次に、ブラウザの操作内容です。

apply_ipo.js
const _apply = async (browser) => {
  console.log("start applying IPO");

  // タブの初期化
  const page = await browser.newPage();
  page.setViewport({width: 1280, height: 720});
  console.log("succeeded to launch browser");

  // ログイン。環境変数からの値取得方法は後述
  await page.goto("https://www.sbisec.co.jp/");
  await page.type("input[name=user_id]", process.env.SBI_USERNAME);
  await page.type("input[name=user_password]", process.env.SBI_PASSWORD);
  await page.click("input[name=ACT_login]");

  // 1度に5件まで連続して申し込む
  let n = 0;
  while (n < 5) {
    await page.waitForSelector("img", {timeout: 5000});
    await page.goto("https://m.sbisec.co.jp/oeliw011?type=21", { waitUntil: "domcontentloaded" });

    let canApply = true;
    await page.waitForSelector("img[alt=申込]", {timeout: 5000}).catch((e) => {
      if (!(e instanceof puppeteer.errors.TimeoutError)) {
        console.error(e);
      }
      canApply = false;
    });

    // これ以上申し込み不可なら終了
    if (!canApply) {
      console.log("no more BB");
      break;
    };
    await page.click("img[alt=申込]");

    // 100株で申し込み
    await page.waitForSelector("input[name=suryo]", {timeout: 5000});
    await page.type("input[name=suryo]", "100");
    await page.click("#strPriceRadio");
    await page.type("input[name=tr_pass]", process.env.SBI_TRADE_PASSWORD);
    await page.click("input[name=order_kakunin]");
    await page.waitForTimeout(1000);

    await page.click("input[name=order_btn]");
    console.log(`apply ${n}`);
    n += 1;
  }
}

補足

  • 単に waitForTimeout よりは waitForSelectorのほうが、操作したいDOM Objectを待つなら望ましいです
  • 結果をSlackに投稿したいならこんな感じです。

API用意

App Engineのcronから呼び出せるよう、API化します。

app.js
import express from "express";
import { applyIPO } from "./apply_ipo";

// .envから
環境変数を読み込み
import dotenv from "dotenv";
dotenv.config();

const app = express();

app.get('/apply-ipo', (_, res, next) => {
  console.log("requested");
  applyIPO().then(
    () => {
      res.status(200).send('OK').end();
      SERVER.close();
    }
  ).catch(next);
});

const PORT = process.env.PORT || 8000;
const SERVER = app.listen(PORT, () => {
  console.log(`App listening on port ${PORT}`);
  console.log('Press Ctrl+C to quit.');
});

module.exports = app;

.envは環境変数名とその値を書くので

SBI_USERNAME=hoge
...

などとなります。

App Engine

package.jsonにbuild / start を登録します。babelは割愛

package.json
{
  "scripts": {
    "build": "babel src -d ./dist/",
    "start": "node dist/app.js"
  },
  ...
}

appの構成ファイルは↓だけでOKです。F2以下だとメモリ不足でエラーになる場合がありました。

app.yaml
runtime: nodejs12
instance_class: F4
gcloud app deploy --version=1 --quiet

定期実行するには

cron.yaml
cron:
- description: Apply IPO on SBI Securities
  url: /apply-ipo
  schedule: every day 1:00
  timezone: Asia/Tokyo

などと書いて

gcloud app deploy cron.yaml --quiet

で登録します。

セキュリティの強化

申し訳程度ですが、cronスケジューラからのAPIコールのみ許可するfirewallを設定します。

gcloud app firewall-rules update default --action=deny
gcloud app firewall-rules create 1 \
--action=allow \
--source-range='10.0.0.1'
gcloud app firewall-rules create 2 \
--action=allow \
--source-range='0.1.0.1'

費用

ポイントを貯めるのにサーバー費用がかさんでしまっては本末転倒です。
幸いなことに、GCPにはかなりの無料枠があるので毎月10円と安く済んでいます。

失敗談

5分おきに、とあるチケットをキャンセル待ちチェックしてSlackに通知していたのですが、
解除し忘れて4,515円かかりました。

gcp_high_cost.png

App EngineはF2以上のインスタンスを常時稼働するとそれなりにかかるのでご注意ください。

また、急に料金が増えたときに気がつけるよう、予算アラートを設定しましょう!

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3