[AtCoder]問題の入力と期待値を自動取得してみた

#はじめに
言語の勉強としてAtCoderのABC(AtCoder Beginners Contest)の問題を解いていました。
しかし問題ページにある複数の入力サンプルと出力サンプルをコピペして目視であってるかどうかを確認するのは面倒だなと思いました。
そこでpuppeteerによるスクレイピングで入力サンプルと出力サンプルを自動で取ってきて、自動でテストするスクリプトを書いてみました。
今回はpuppeteer部分でやったことなどを残しておきます。
#調査
問題ページのURLは、例えば第88回目のABCのB問題はhttps://atcoder.jp/contests/abc088/tasks/abc088_b
第160回目のABCのD問題はhttps://atcoder.jp/contests/abc160/tasks/abc160_b
となってます。
問題ページのURLは、

```
https://atcoder.jp/contests/<コンテスト名>/tasks/<コンテスト名>_<問題名>
```
で取れることがわかりました。
次に各問題ページ入力と出力例のDOM情報を調べてみます。
幸いどの問題でも例えば入力１では、

```
html > body > div#main-div.float-container > div#main-container.container > \
div.row > div.col-sm-12 > div#task-statement > span.lang > span.lang-ja > \
div.part > section > pre#pre-sample0
```
ここに配置されていることが確認できました。
`pre#pre-sample<数字>`の部分に関しては
`入力1` -> `pre#pre-sample0`
`出力1` -> `pre#pre-sample1` 
`入力2` -> `pre#pre-sample2`
`出力2` -> `pre#pre-sample3`
`入力3` -> `pre#pre-sample4`
`出力3` -> `pre#pre-sample5`
...
となっていました。
これだけの情報で十分です。
#コード例
`第xxx回目ABC` -> `abc<xxx>`
`A問題` -> `a`
として例えば第160回目ABCのA問題のサンプルを全部取得したい時は、

```sh
node get_sample.js abc160 a
```
とコマンドライン引数を与えることを前提として以下のようにコードが書けます。

```javascript
#!/usr/bin/env node
const puppeteer = require('puppeteer');
const fs = require('fs')
//ex; abc161
const contest_number=process.argv[2]
//ex; a,b,c..,f
const question=process.argv[3]
const atcoder_url="https://atcoder.jp/contests/" + contest_number + "/tasks/" + contest_number + "_" + question
(async () => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto(atcoder_url);
    const common_selector = 'html > body > div#main-div.float-container > div#main-container.container > div.row > div.col-sm-12 > div#task-statement > span.lang > span.lang-ja > div.part > section > '

    for(i = 0; ;i += 2){
        const in_selector=common_selector + `pre#pre-sample${i}`;
        const exp_selector=common_selector + `pre#pre-sample${i+1}`;
        //取得する前にそもそもデータが存在するかのチェックを行う
        const in_data =  await page.$(in_selector) != null? await page.$eval(in_selector, item => {return item.textContent;}) : null
        const exp_data = await page.$(exp_selector) != null? await page.$eval(exp_selector, item => {return item.textContent;}) : null
        if(in_data != null){
            const filepath = `./test/in${i/2 + 1}`
            fs.writeFile(filepath, in_data, (err, data) => {
                if(err) console.log(err);
            });
        }else{
            break;
        }
        if(exp_data != null){
            const filepath = `./test/exp${i/2 + 1}`
            fs.writeFile(filepath, exp_data, (err, data) => {
                if(err) console.log(err);
            });
        }else{
            break;
        }
    }
    await browser.close();
})();

```
これでカレントディレクトリの`test`というディレクトリに入力例(in〇)と出力例(exp〇)を保存できます。
このjsにshebangを記載しパスの通ったディレクトリに置いておくことでいつでも呼び出せるようにしておきます。
自動テストスクリプトの実装は省きますが、このjsにshebangを記載することで自動テストスクリプトをshellスクリプトで書いた場合、呼び出しも簡単になります。

```sh
get_sample.js <コンテスト名>　<問題名>
```
自動テストスクリプトではカレントディレクトリの`test/`に`in1`,`in2`,`in3`があることを確認し
あれば、プログラムのビルドし、これらを入力とし、実行。
それぞれの結果と`exp1`,`exp2`,`exp3`が一致するかは`diff`コマンドで確認できます。
#最後に
最近アルゴリズムの勉強もした方がいいのかなと思う今日この頃、、、AtCoderまずは緑色頑張ります！