20200215のNode.jsに関する記事は3件です。

Puppeteer Tips

Puppeteer?

読み方は「ぱぺてぃあ」。

Node.jsのライブラリでChromeを操作しDOMの要素を取得、ステータスコードを取得、レスポンスタイムを計測・・等々できます。
※Chromeのデベロッパーツールで見れる情報は(たぶん)全てpuppeteerで取得できる

Sample

「サイト内の各ページのタイトルが予測した値になっているか?」という自動テストをPuppeteerを利用して処理してみます。

大まかな流れは下記のようになります。

1. テストデータをCSVファイルから読み込み
2. 1行ずつループし、取得した値と予測した値を比較
3. 結果表示

Code

https://github.com/yusukeito58/puppeteer-template

$ tree -I node_modules
.
├── package.json
└── src
    ├── data
    │   └── title.test.csv
    ├── lib
    │   └── output.js
    └── test
        └── title          // テスト・処理内容応じてにディレクトリを切って、実行ファイル(index.js)と説明ファイル(README.md)を置くといい感じ
            ├── README.md  // テスト概要や実行方法を記載
            └── index.js   // 実行ファイル

index.js
const puppeteer = require('puppeteer');
const papa = require('papaparse');
const assert = require('assert');
const fs = require('fs');

const root = '../../'
const { showTestStart, showResult  } = require(root + 'lib/output');

// メイン処理
(async () => {
  console.time('Processing time');

  // テスト対象のサイト
  const domain = 'https://www.google.com';

  // テスト対象のデータ ※ Listを直接コードに書く、CSVから読み込む etc...
  file = fs.readFileSync(root + 'data/title.test.csv', 'utf8')
  dataList = papa.parse(file, {
    header: true,
    skipEmptyLines: true
  }).data;

  // カウンタ初期化
  let count = 0;
  // エラー一覧
  let errorList = [];

  // ブラウザ起動
  const browser = await puppeteer.launch();
  for (const data of dataList) {
    count += 1;

    // アクセス先のURLを生成
    const url = domain + data.path;

    // 進捗を表示
    showTestStart(url, count, dataList);

    // ページ生成
    const page = await browser.newPage();

    // JSやCSSの読み込みを無視
    await page.setRequestInterception(true);
    page.on('request', (interceptedRequest) => {
      if (url === interceptedRequest.url()) {
        interceptedRequest.continue();
      } else {
        interceptedRequest.abort();
      }
    });

    // テスト対象のURLにアクセス(返り値にresponseが返る)
    await page.goto(url);

    // ページタイトル取得
    const title = await page.title();

    try {
      // 予期された結果と比較
      assert.equal(title, data.title);
      console.log('' + 'Expected result');
    } catch (err) {
      console.log('' + 'Unexpected result');
      console.log(err.message);

      errorList.push(err.message);
    }
    console.log('\n');

    // ページ閉じる
    await page.close();
  }

  // ブラウザ閉じる
  await browser.close()

  showResult(errorList);
  console.timeEnd('Processing time');
})();

lib/output.js
exports.showTestStart = (currentUrl, index, urls) => {
  const color = '\u001b[44m\u001b[37m';
  const reset = '\u001b[0m';

  console.log(`${color} ?  ${currentUrl} | ${index} / ${urls.length} ${reset}`);
};

exports.showResult = (errorList) => {
  let msg = '';
  if (errorList.length === 0) {
    msg = '✅  ???Congratulation for passing!!???';
  } else {
    msg = '❌  Failed the test...?';
  }

  console.log('\n' + msg + '\n');
};

data/title.test.csv
url,title
/,Google
/search/howsearchworks/,Google Search - Discover How Google Search Works
package.json
{
  "name": "puppeteer-template",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "directories": {
    "test": "test"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "yusukeito58",
  "license": "MIT",
  "dependencies": {
    "assert": "^2.0.0",
    "papaparse": "^5.1.1",
    "puppeteer": "^2.1.1"
  }
}

実行

サンプルで用意したプログラムを実行してみます。

$ git clone https://github.com/yusukeito58/puppeteer-template.git

$ npm i
$ cd src/test/title

# 実行
$ node index.js

結果

うまくいくと下記のように表示されます。
スクリーンショット 2020-02-15 19.37.17.png

Tips

個人的に利用頻度が多い処理をまとめておきます。

特定要素の有無を判定

例. 検索ボタンがあるか確認

// 要素を取得
const hasElement = async (page, selector) => {
  const item = await page.$(); // selectorを引数で受け取るようにするともっと汎用的に使える
  if (item) {
    return true;
  } else {
    return false;
  }
};

(async () => {   
  for (...) {
    :
    const selector = 'center > input.gNO89b';
    ret = await hasElement(page, selector);
    :
  }
}();

UserAgentを指定

例.デバイスを「iPhone X」に設定

const devices = require('puppeteer/DeviceDescriptors');
// テストデバイス
const device = devices['iPhone X'];

(async () => {   
  for (...) {
    :
    // デバイス設定
    await page.emulate(device);
    :
  }
}();

JSやCSSなどの読み込みを無視

処理速度が数倍違ってきます。

(async () => {   
  for (...) {
    :
    await page.setRequestInterception(true);

    page.on('request', (interceptedRequest) => {
    if (url === interceptedRequest.url()) {
        interceptedRequest.continue();
    } else {
        interceptedRequest.abort();
    }
    });
    :
  }
}();

テストデータをCSVファイルから取得​

url,title
/,Google
/search/howsearchworks/,Google Search - Discover How Google Search Works
const papa = require('papaparse');
const fs = require('fs');
:

(async () => { 
  :
  // テストデータ取得
  file = fs.readFileSync(root + 'data/title.test.csv', 'utf8')
  dataList = papa.parse(file, {
    header: true,
    skipEmptyLines: true
  }).data;
  :
)();

非同期に対象ページにアクセス​

データが大量にあった場合、直列的に対象ページにアクセスすると処理時間を要するため、非同期に同時処理すると処理時間が短縮されます。

安定しないケースもあるので、検証が必要かも知れません。(もっと良い方法ありそう)

例. 大量の対象URLが全て正常(ステータスコードが200である)か確認する

// ステータスコードを取得
const getStatusCode = (browser, url) => {
  return new Promise(async (resolve) => {
    const page = await browser.newPage();

    await page.setDefaultNavigationTimeout(0);

    await page.setRequestInterception(true);
    page.on('request', (interceptedRequest) => {
      if (url === interceptedRequest.url()) {
        interceptedRequest.continue();
      } else {
        interceptedRequest.abort();
      }
    });

    const response = await page.goto(url);

    // ステータスコードを返却(true:200台)
    const result = {
      'url': url,
      'status': response.ok()
    }

    await page.close();

    resolve(result);
  })
}

:

(async () => { 
  :
  for (let url of urls) {
    allResponse.push(getStatusCode(browser, url))  // あまり大量だとPCがが唸る・・・
  }

  errors = await Promise.all(allResponse) // 非同期実行
    .then(results => {
       // エラーとなった情報だけにフィルタリング
       return results.filter(result => !result.status)
  })
  :
)();

配列を〇〇個ごとに分割

Puppeteerから少し外れますが、前項の非同期処理を行う前処理です。同時実行する個数に調整する際に利用します。

const divideArrIntoPieces = (arr, n) => {
  let arrList = [];
  let idx = 0;
  while(idx < arr.length){
    arrList.push(arr.splice(idx, idx + n));
  }
  return arrList;
}

(async () => {
  :
  urlList = divideArrIntoPieces(allUrl, 10);

  for (let urls of urlList) {
    for (let url of urls) {
      :
    }
  }
}();

画像を保存​

// ダウンロード対象
imgUrl = 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png';

// ローカルの保存先
const imgPath = './images/image.jpg';

// 画像ダウンロード
const viewSource = await page.goto(imgUrl);

// ローカルに保存
fs.writeFileSync(imgPath, await viewSource.buffer());

参考​

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nvm環境でnpm自体を一発アップデートするコマンド

コマンド一発でnpm自身を最新化する方法

nvm環境でnpm自体のアップデートがうまく行かなかったのでコマンド一発で成功する方法を公開しときます。
たぶん、WindowsやMac両方動くと思います。

やり方

以下、bashコマンドラインで実行してください。
(ヒアドキュメント使ってるのでcatから下部のEOFまでコピーしてね)

$ cat <<EOF > npm_update.sh && chmod +x npm_update.sh && ./npm_update.sh && rm -rf ./npm_update.sh
#!/usr/bin/bash
cd "$PROGRAMFILES"/nodejs
rm npm npx npm.cmd npx.cmd
mv node_modules/npm node_modules/npm2
node node_modules/npm2/bin/npm-cli.js i -g npm@latest
rm -rf node_modules/npm2/
EOF

やってる事

実行ファイル生成

実行権限を付与

実行してnpmアップデート

削除して終了

上記にたどり着くまでの失敗

失敗1:「npm install -g npm」を実行してみた

まずは正攻法のやり方として「$ npm install -g npm」を実行してみると以下エラー。

C:\WINDOWS\system32>npm install -g npm
npm ERR! code EEXIST
npm ERR! path C:\Program Files\nodejs\npm.cmd
npm ERR! Refusing to delete C:\Program Files\nodejs\npm.cmd: is outside C:\Program Files\nodejs\node_modules\npm and not a link
npm ERR! File exists: C:\Program Files\nodejs\npm.cmd
npm ERR! Remove the existing file and try again, or run npm
npm ERR! with --force to overwrite files recklessly.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\inukujira\AppData\Roaming\npm-cache\_logs\2020-02-15T00_43_01_988Z-debug.log

失敗2:「npm-windows-upgrade」を実行してみた

「npm-windows-upgrade」というモジュールを使えばいいというissueを見つけたのでインストールして実行してみた。
しかしうまくいかず...。

C:\WINDOWS\system32>npm-windows-upgrade
npm-windows-upgrade v6.0.1
? Which version do you want to install? 6.13.7
Checked system for npm installation:
According to PowerShell: C:\Program Files\nodejs
According to npm:        C:\Program Files\nodejs
Decided that npm is installed in C:\Program Files\nodejs
Upgrading npm... -

Upgrading npm (fallback method)... \

You wanted to install npm 6.13.7, but the installed version is 6.13.4.

A common reason is an attempted "npm install npm" or "npm upgrade npm". As of today, the only solution is to completely uninstall and then reinstall Node.js. For a small tutorial, please see https://github.com/felixrieseberg/npm-windows-upgrade#usage.

Please consider reporting your trouble to https://aka.ms/npm-issues.

Debug Information:

node: 12.16.0 | v8: 7.8.279.23-node.31 | uv: 1.34.0 | zlib: 1.2.11 | brotli: 1.0.7 | ares: 1.15.0 | modules: 72 | nghttp2: 1.40.0 | napi: 5 | llhttp: 2.0.4 | http_parser: 2.9.3 | openssl: 1.1.1d | cldr: 35.1 | icu: 64.2 | tz: 2019c | unicode: 12.1 | os: win32 x64
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nvm環境のnpm自体をコマンド一発で最新化する方法

コマンド一発でnpm自身を最新化する方法

nvm環境でnpm自体のアップデートがうまく行かなかったのでコマンド一発で成功する方法を公開しときます

やり方

以下、bashコマンドラインで実行するだけの簡単なお仕事
ヒアドキュメント使ってるのでcatから下部のEOFまでコピーしてね
(Macの場合は「$PROGRAMFILES」環境変数とれないので書き換えればOK)

$ cat <<EOF > npm_update.sh && chmod +x npm_update.sh && ./npm_update.sh && rm -rf ./npm_update.sh
#!/usr/bin/bash
cd "$PROGRAMFILES"/nodejs
rm npm npx npm.cmd npx.cmd
mv node_modules/npm node_modules/npm2
node node_modules/npm2/bin/npm-cli.js i -g npm@latest
rm -rf node_modules/npm2/
EOF

やってる事

実行ファイル生成

実行権限を付与

実行してnpmアップデート

削除して終了

上記にたどり着くまでの失敗

失敗1:「npm install -g npm」を実行してみた

まずは正攻法のやり方として「$ npm install -g npm」を実行してみると以下エラー。

C:\WINDOWS\system32>npm install -g npm
npm ERR! code EEXIST
npm ERR! path C:\Program Files\nodejs\npm.cmd
npm ERR! Refusing to delete C:\Program Files\nodejs\npm.cmd: is outside C:\Program Files\nodejs\node_modules\npm and not a link
npm ERR! File exists: C:\Program Files\nodejs\npm.cmd
npm ERR! Remove the existing file and try again, or run npm
npm ERR! with --force to overwrite files recklessly.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\inukujira\AppData\Roaming\npm-cache\_logs\2020-02-15T00_43_01_988Z-debug.log

失敗2:「npm-windows-upgrade」を実行してみた

「npm-windows-upgrade」というモジュールを使えばいいというissueを見つけたのでインストールして実行してみた。
しかしうまくいかず...。

C:\WINDOWS\system32>npm-windows-upgrade
npm-windows-upgrade v6.0.1
? Which version do you want to install? 6.13.7
Checked system for npm installation:
According to PowerShell: C:\Program Files\nodejs
According to npm:        C:\Program Files\nodejs
Decided that npm is installed in C:\Program Files\nodejs
Upgrading npm... -

Upgrading npm (fallback method)... \

You wanted to install npm 6.13.7, but the installed version is 6.13.4.

A common reason is an attempted "npm install npm" or "npm upgrade npm". As of today, the only solution is to completely uninstall and then reinstall Node.js. For a small tutorial, please see https://github.com/felixrieseberg/npm-windows-upgrade#usage.

Please consider reporting your trouble to https://aka.ms/npm-issues.

Debug Information:

node: 12.16.0 | v8: 7.8.279.23-node.31 | uv: 1.34.0 | zlib: 1.2.11 | brotli: 1.0.7 | ares: 1.15.0 | modules: 72 | nghttp2: 1.40.0 | napi: 5 | llhttp: 2.0.4 | http_parser: 2.9.3 | openssl: 1.1.1d | cldr: 35.1 | icu: 64.2 | tz: 2019c | unicode: 12.1 | os: win32 x64
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む