環境構築なしでブラウザの自動操作を行う

# 環境構築なしでブラウザの自動操作を行う

## はじめに
ブラウザの自動操作といえばSeleniumとか、UWSCとかが有名です。
ですがSeleniumは環境構築がだるかったり、既存起動ブラウザにアタッチできなかったり、面倒ごとが多いです。
UWSCは操作がアナログすぎてしんどいです。
結局は、javascriptを直接実行するのが環境構築なしでできて動かすのも気軽で一番楽だったりします。

## やる目的
最近、JCBプレモカードを買うと10%得するキャンペーンがあったので、JCBプレモカードを買いました。
それにともない、アマゾンギフト券注文して、カード番号を入力する作業をやらなきゃいけません。
今回はそれを自動化します。
キャンペーン詳細: [JCBプレモカードのキャッシュバックキャンペーンで19.1%還元！Amazonギフト券がオススメ](http://monimonita333.hatenablog.com/entry/JCB-premo)

![20170808182941.png](https://qiita-image-store.s3.amazonaws.com/0/131295/8cfbf423-2125-10bd-b879-ad5e9d92e884.png)

## 自動化すること
* アマゾンギフト券を自動で購入する
* Gmailに届いたメールの支払いページURLを抽出する
* 支払いページにアクセスしたら自動でカード番号を入力し、支払いをする

## [その１]アマゾンギフト券を自動で購入する
chrome拡張「tampermonkey」を使って、アマゾンギフト券チャージタイプページ移動→3000円と入力→コンビニ払いを選択→注文 という画面動作を自動化します。
### 手順
1. [前提] Chromeをインストールしている
1. [おすすめ] 誤ってクレジットカード払いしないように、アマゾンからクレジットカード情報を消しておくことをおすすめします
1. chrome拡張 [tampermonkey](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo)をChromeに入れる
    * javascriptを好きなページで実行してくるchrome拡張です。
1. tampermonkeyメニューの「新規スクリプトを追加」を開く  
    ![Screenshot_79.png](https://qiita-image-store.s3.amazonaws.com/0/131295/9a510023-0caa-a9dd-b600-5d9bb3d297c8.png)
1. [amazon-auto-buy-gift.js](https://github.com/sagami1991/amazon-premo-tampermonkey-script/blob/master/amazon-auto-buy-gift.js)の内容をそのままtampermonkeyのエディタに張り付けて、左上ボタンで保存
    * エディタに警告が出ますが、tampermonkeyのエディタが古いだけなので気にしなくていいです
    * 金額は3000円に設定してます。`input.value = "3000";`のところを変更すれば変わります。
    * 超省いたコードの解説をすると、`document.querySelector(セレクタ)`でタグ要素を取得して値セット（`element.value`）と、クリック（`element.click()`）を繰り返してます。  
    7行目の// @match の部分は、どのURLで実行するかという記述です。
1. [アマゾンギフト券購入ページ](https://www.amazon.co.jp/gp/gc/create/)に移動する
    * 移動すると画面が動き自動購入が始まります。特に回数制限処理も入れてません。
    * スクリプトの停止と有効を切り替えるにはtampermonkeyメニュー右上の[ON]をクリックします。  
        ![Screenshot_84.png](https://qiita-image-store.s3.amazonaws.com/0/131295/a1e128fa-61ff-0473-9344-b4765f12d55e.png)
        * メニュー左上の有効ボタンはtampermonkey全ての有効無効なので、こっちを使用すると、別ページで有効にしたときまた勝手に動いてしまいます。
    * tampermonkeyのダッシュボードページでもスクリプトが無効になってるか確認できます
    ![Screenshot_83.png](https://qiita-image-store.s3.amazonaws.com/0/131295/dfb77490-f911-c14c-30d8-6301f1983537.png)
1. 必要な分買い終えたら終わり
    * 購入を終えたら、tampermonkeyのダッシュボードページでスクリプトを削除しておくと安全です。

## [その２]Gmailに届いた支払いページURLを抽出する
スプレッドシートとGoogleAppScriptを使って、GmailにAmazonから届いた大量の支払案内メールGmail中のページURLを抽出し、スプレッドシートに自動挿入します。
### 手順
1. Gmailの画面で、ラベルボタンで「amazon-premo」という名前のラベルを作成します  
    ![Screenshot_86.png](https://qiita-image-store.s3.amazonaws.com/0/131295/1fd31089-71fe-5abe-8d0b-25c8d47e567c.png)

1. amazonから届いたお支払番号のお知らせメールに作ったラベルを付けます
    * 「label:unread お支払い番号」とかで検索すると絞り込みが楽です
    * メールを一括チェックしてラベルにドラッグアンドドロップか、ラベルボタンメニューで付けれます  
    ![Screenshot_87.png](https://qiita-image-store.s3.amazonaws.com/0/131295/9a696776-1e41-4f34-c7af-572533030dbc.png)
1. [スプレッドシートページ](https://www.google.com/intl/ja_jp/sheets/about/)を開き、新しいスプレッドシートを作成します
1. スプレッドシートの[ツール] -> [スクリプトエディタ]でスクリプトエディタを開きます。 
1. スクリプトエディタに [grep-mail-url-google-script.gs](https://github.com/sagami1991/amazon-premo-tampermonkey-script/blob/master/grep-mail-url-google-script.gs)の内容を張り付けて、[実行]ボタンで実行します。
    * スクリプトからGmailを読もうとするのでセキュリティ画面が出るので、許可してあげます。
    ![Screenshot_75.png](https://qiita-image-store.s3.amazonaws.com/0/131295/ad4d781b-8645-3fcd-83e8-99df358ed502.png)  
    ![Screenshot_76.png](https://qiita-image-store.s3.amazonaws.com/0/131295/02e2b6d7-17bf-6034-88e4-4b2cf4469a1d.png)  
    ![Screenshot_77.png](https://qiita-image-store.s3.amazonaws.com/0/131295/eac9c9b0-50a5-1b1f-0b60-a90e42380e9e.png)
    * スクリプト実行が終わったら[アカウント許可ページ](https://myaccount.google.com/permissions)で許可したものをオフっておくと安全です。
    * `var row = 2;`の部分を変えれば、最初の挿入行を変えれます。
    * ざっくりコードの説明をすると、作成したラベル「amazon-premo」のメールを全取得し、URLを正規表現で抽出してスプレッドシートに張り付けてます。
1. 実行したあと、スプレッドシートのページにずらーっと支払いページURLが挿入されます。  
![Screenshot_88.png](https://qiita-image-store.s3.amazonaws.com/0/131295/04a5872d-b40f-5826-10ad-164473b034e1.png)
1. 完了

## [その3]支払いページにアクセスしたら自動でカード番号を入力し、支払いをする作業を自動化
先ほどのchrome拡張「tampermonkey」を使って、
支払いページ移動→JCBプレモカード選択→カード番号入力→支払い　の動作を自動化します
### 実装的な処理の流れ詳細
javascriptでページ遷移するときに変数を渡す方法は難しいので以下の方法をとります。

1. 支払いページのURLのハッシュタグにカード番号を持たせて支払いページに移動する
1. javascriptでURLのハッシュタグを取得しSessionストレージに格納
1. カード番号入力画面に遷移
1. sesisonストレージからカード番号を取り出し入力

URLにカード情報を乗せるのはセキュリティ的にやや危険ですが、
URLのハッシュタグ以降の文字はサーバーに送られないので、サーバーのログに残ることはないです。
sessionストレージもブラウザで管理しているもので、タブを閉じると破棄されます。
サクっと作りたいのが目的なので、この方法で画面遷移先にもカード情報をjavascriptで扱えるようにします。
ただ、ハッシュタグ以降のURLはブラウザの履歴には残るので、神経質な人はシークレットウインドウで作業するとよいです。
### 手順
1. 先ほどのスプレッドシートを使って、URLにハッシュタグとカード番号を結合します
    1. 列Aにカード番号、列Bにカード認証番号を入力していきます。  
        ![000154.png](https://qiita-image-store.s3.amazonaws.com/0/131295/7823df3c-9064-58a6-e027-65a8aa548e4f.png)  
        1. ここはさすがに手動です。一番大変な作業です。
        1. JCBプレモカードのカード番号は上8桁は固定なので、下8桁だけ入力します。
        1. 先頭が0だと消えてしまうので、番号入力列を選択し、書式を「書式なしテキスト」にしておきます  
        ![Screenshot_89.png](https://qiita-image-store.s3.amazonaws.com/0/131295/656e02cc-3b9a-6ad5-4e0f-7666ac45449a.png)
        * [おまけ]カード番号の入力ミスを防ぐため、入力したキーの番号を音で鳴らすようにします
            1. スプレッドシート画面上でF12を押し、開発者ツールを開きます。
            1. Consoleを開き、[sound-number.js](https://github.com/sagami1991/amazon-premo-tampermonkey-script/blob/master/sound-number.js)の内容をConsoleに貼り付けて、エンターを押します。（音声を文字列化していて500KBぐらいあるので、貼り付けもエンターも数秒ラグがあります）
            1. 開発者ツールを閉じ、スプレッドシートでIMEが半角英数状態で数値を入力すると数字音が出るようになります
    1. 列Dをハッシュタグとカード番号付きURL列にするので、列Dの2行目に`=C2 & "#" & "(カード番号上8桁)" & A2 & "," & B2`入力をし、オートフィルで下まで自動入力します。
    1. [おまけ]列Eをチェック列にしたい場合、列Eのデータの入力規則を開き、条件「✔,＿」で、チェックをつけれるようにします。  
        ![Screenshot_92.png](https://qiita-image-store.s3.amazonaws.com/0/131295/825a3b80-acb1-6d0f-7082-90113963c5ad.png)  
        ![Screenshot_91.png](https://qiita-image-store.s3.amazonaws.com/0/131295/4fd5adc1-20c2-629c-ab04-0d42fcd5e39d.png)
    * [おまけ2]チェック時の行ハイライトは以下の設定でできます  
        ![Screenshot_93.png](https://qiita-image-store.s3.amazonaws.com/0/131295/8b07461e-a6ca-e60a-c645-e095608281a8.png)
1. **追記 「すでに支払いが完了～」みたいなのが出るバグ回避方法を最後に追記してます** 
1. tampermonkeyメニューの「新規スクリプトを追加」を開き、 [amazon-jcb-premo-auto-input.js](https://github.com/sagami1991/amazon-premo-tampermonkey-script/blob/master/amazon-jcb-premo-auto-input.js)
の内容を貼り付けして保存します。
1. あとは、スプレッドシートの結合したURLを開いていくだけで、自動で番号入力して支払いまで画面が動いてくれます。
    * たまに「すでに支払いが完了～」みたいなエラーが出ますが、2chでも報告されている手動でやっても出るアマゾン側の前処理のラグによりバグです。もう一度やると普通に支払いできます。
    * 終わったら、tampermonkeyダッシュボードで、スクリプトの無効か削除を忘れずにして、スプレッドシートの管理も気をつけてください。
1. 終わり

## 2017/10/9 追記 支払い画面で「すでに支払い完了～」を回避する方法
要はcookieを削除すればいいんですが、httponlyなcookieはjavascriptから操作できないので、seleniumやらなんやら、ブラウザの外側から操作する必要があります。
ということで環境構築が面倒ですが、seleniumに似たpuppeteerという、chrome自動操作ツールを使って操作します。

1. nodejsをインストールします。バージョンは自由です
   * https://nodejs.org/ja/
1. 適用なフォルダをどこかに作成します。
2. フォルダの中で以下のコマンドを実行します

```bash
npm i -S puppeteer node-notifier @types/puppeteer 
```

3. フォルダの中にmain.jsというjavascriptファイルを作成し、以下の内容を張り付けます

```javascript:main.js
//@ts-check
const puppeteer = require("puppeteer");
const notifier = require("node-notifier");
const json =  /** @type {{cards: string[][]}} */ (require("./numbers.json"));
const wait = ( /** @type {number} */ second) => new Promise((resolve) => setTimeout(() => {
    resolve();
}, second * 1000));
(async () => {
    const browser = await puppeteer.launch({headless: false});
    const page = await browser.newPage();
    await page.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36");
    let index = 0;
    page.on("load", async (e) => {
        const url = await page.url();
        await wait(0.3);
        if (url.includes("https://link3.kessai.info/JLP/JLPcon")) { // 最初の画面
            // プレモカードで決済をクリック
            await page.click("#anchorsel16");
        } else if (url === "https://link3.kessai.info/JcbPremoWeb/Payment") { // カード情報入力画面
            await page.focus("input[name=cardno]");
            await page.type(json.cards[index][0]);
            await page.focus("input[name=authno]");
            await page.type(json.cards[index][1]);
            await page.evaluate(() => {
                document.querySelector("form[name=premo]").submit();
            });
        } else if (url.includes("errorcode=")) {
            notifier.notify({message: "エラーページ"});
        } else if (url === "https://link3.kessai.info/JcbPremoWeb/PayPremo") { // 決済完了ページ
            console.log(index);
            await page.screenshot({path: `./screenshot_20171009_${index}.png`, fullPage: true});
            if (index === json.cards.length - 1) {
                console.log("終わり");
                return;
            }
            index += 1;
            /** @type {{name: string}[]} */
            const cookies = await page.cookies();
            await page.deleteCookie(...cookies);
            await page.goto(json.cards[index][2]);
        }
    });
    await page.goto(json.cards[index][2]);
})();

```

4. カード情報のファイル `numbers.json`というファイルを作成します
配列の一番目がカード番号、2番目が暗証番号、3番目が決済URLです。
このJSONはスプレッドシートの情報を使い、正規表現なりスプレッドシートの文字列結合なりでがんばって作ってください。

```json:numbers.json
{
    "cards": [
        ["33371502xxxxxxx","xxxxxxxx","https://link3.kessai.info/JLP/JLPcon?xxxxxxxx"],
        ["33371502xxxxxxx","xxxxxxxx","https://link3.kessai.info/JLP/JLPcon?xxxxxxxx"]
    ]
}
```

5. フォルダ上でコマンド`node main.js`を実行すると、numbers.jsonのカードすべての決済が順番に行われます。決済結果画面が`./screenshot_20171009_${index}.png`に保存されます。
もう一度実行するときは適当にスクリーンショットのファイル名変更してください。
 
## さいごに
アマゾンギフト券を現金化する方法を教えてください。