愚痴
テスト自動化とか定形作業自動化とかでヘッドレスChromeに対してPuppeteerでサクッと行うことが増えてきました。
決まった日に社内イントラネット上のとあるWebアプリケーションのクソ面倒くさい操作のGUIを操って帳票作成のスケジュール設定。さらに別の決まった日に作成された帳票をこれまたクソ面倒くさくてつまらないクソGUIを操作してダウンロード。特定のフォルダに保存する。そんなクソみたいで「シット!」と舌打ちしたくなるような定型事務作業があります。
これが実に筆舌に尽くしがたいほど個人的に特にクソつまらないのです。そもそもマウスを使ってちんたら画面操作をしたくないのです。本来イントラネット上のとあるWebアプリケーションがすべてバッチでやってくれればよいのです。GUIで人間にやらせようとする魂胆があざとい。
今までは私の担当分はSelenium Webdriverを使ってしのいできました。しかし、ブラウザの画面GUIが自己主張が強すぎてうっとうしいものです。
でも、ヘッドレスChromeなら裏で静かにこっそりつまらない定型事務作業をもくもくと地下労働者のように行ってくれます。これはなかなか楽しい。例えて言うなら『王侯貴族が奴隷をこき使って優雅に暮らす』そんな主従関係の現代IT版みたいな感じでしょうか・・・👈たとえが悪すぎます・・・(-_-メ)
そんなクソ定型作業も世の流行なのでRPAで自動化とかいうことになってRPAツールにクソつまらない設定パラメータを与えてロボ吉で動かすようになってきました。クソつまらない単なる作業はロボ吉にまかせる。それはそれで素晴らしいことです。
でもでも、このRPAなるロボ吉。いかにも「私やってます。ニカッ( ^)o(^ )」的な自己主張が強すぎてどうにも腹立たしい。少しイラッとするのです。「こちとら、裏でこっそりバッチ君が好きなんじゃあ~~(笑)」。ということでPuppeteerでヘッドレスChromeする。個人的にツボです。
そこで、個人的なメモを以下に書きなぐります。間違いや勘違いが多々あってもとりあえず、ここにしばらくメモリます。
Install
Seq. | すること | URL / コマンド | 備考 |
---|---|---|---|
1 | Git Bash(GitのMINGW)をInstallする | https://gitforwindows.org/ | [Download]ボタンを押す |
2 | Node.jsをInstallする | https://github.com/nullivex/nodist/releases | Windows:NodistSetup-v0.8.8.exe Linux/MacOS:Source code(tar.gz) |
3 | Git Bash(MINGW)を起動 | ||
4 | Node.jsのバージョンを確認 | $ nodist -v | |
5 | パッケージ管理ツールnpmのバージョンを確認 | $ npm -v | vX.X.X形式で表示される |
6 | パッケージ管理ツールYarnのInstall | $ npm install -g yarn --scripts-prepend-node-path | Facebook発のパッケージ管理ツールYarnをnpmを使ってInstall |
7 | Node.jsのInstall可能なバージョン一覧を確認する | $ nodist dist | |
8 | Node.jsの最新のLTS(Long-term Support)を確認する | https://nodejs.org/ja/ | [XX.XX.X LTS 推奨版]を確認 |
9 | Node.jsをバージョンアップする | $ nodist 10.15.2 | Installing 10.15.2 Installation successful. と表示されれば成功 |
10 | プロジェクト用のディレクトリを作成 | $ mkdir kusoprj | $cd kusoprj |
11 | package.jsonを作成 | $ winpty yarn.cmd init -y | yarn init vX.X.X と表示される |
12 | PuppeteerのInstall | $ yarn add puppeteer |
{
"name": "sample",
"version": "1.0.0",
"main": "index.js",
"license": "MIT"
}
{
"name": "sample",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"puppeteer": "^1.12.2"
}
}
基本Sequence
Seq. | すること | 備考 |
---|---|---|
1 | Puppeteer利用設定 | |
2 | エミュレート対象デバイス指定設定 | 省略可 デバイスをエミュレートする場合のみ必要 |
3 | エミュレート対象デバイスの指定 | 省略可 デバイスをエミュレートする場合のみ必要 |
4 | 非同期のラムダ関数定義 | |
5 | ブラウザオブジェクト生成 | |
6 | 新規ページ生成 | |
7 | エミュレート対象デバイスを呼び出し | 省略可 デバイスをエミュレートする場合のみ必要 |
8 | ページの遷移 | |
9 | ページへの操作 | |
10 | ブラウザオブジェクトを閉じる |
const puppeteer = require('puppeteer');
//const devices = require('puppeteer/DeviceDescriptors');
//const iPhone = devices['iPhone X'];
(
async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
//await page.emulate(iPhone);
await page.goto('https://www.tesla.com/jp/');
await page.screenshot( { path: 'teslaHomePage.png', fullPage: true } );
await browser.close();
}
)();
実行
どこの会社にもあるWindows10 Pro クソ事務作業用端末に、あらかじめNode.jsとPuppeteerライブラリ、そうして実行プラットフォームであるGit-Bash(古くからあるMINGW)を仕込んでおく
-
Git-Bash(MINGW64)起動
-
スケジュール実行
最初のうちは手打ち。安定してきたら定期定時バッチ処理に移行。
$ cd '//fileserver/クソ帳票/Tool'
$ node schedule_kuso.js
- ダウンロード実行
最初のうちは手打ち。安定してきたらこちらもダウンロード日にバッチ処理に移行。
$ cd '//fileserver/クソ帳票/Tool'
$ node download_kuso.js
外部操作
操作 | 操作対象オブジェクト | メソッド | パラメータ |
---|---|---|---|
スクリーンショット | page | screenshot() | JSONオブジェクト |
URL遷移 | page | goto() | URL |
JSONオブジェクト
意味 | パラメータ名 | 値 |
---|---|---|
スクリーンショットファイルの保存パス | path | パス |
全画面取得 | fullPage | する:true しない:false |
await page.screenshot( { path: 'hoge.png', fullPage: true } );
await page.goto('https://www.tesla.com/jp/');
認証
種類 | 操作対象オブジェクト | メソッド | パラメータ1 |
---|---|---|---|
BASIC認証 | page | setExtraHTTPHeaders() | JSONオブジェクト |
JSONオブジェクト
意味 | パラメータ名 | 値 |
---|---|---|
認証の種類 | Authorization | `Basic ${new Buffer(${ユーザ定数}:${パスワード定数} ).toString(認証方式定数}` |
await page.setExtraHTTPHeaders(
{
Authorization:
`Basic ${new Buffer(`${'Hoge Foo'}:${'somepass'}`).toString('base64')}`
}
);
DOM操作
操作 | 操作対象オブジェクト | メソッド | パラメータ1 | パラメータ2 |
---|---|---|---|---|
実行ボタンクリック | page | click() | input[type="submit"] | - |
アンカークリック | page | click() | a[href^="遷移先指定の最初の文字列"] | - |
特定要素クリック | page | $$eval() | selector | タグ名 => タグ名.filter( タグ名 => タグ名.textContent === '要素内文字列' ))[0].click() } |
値入力 | page | type() | タグ名[属性="属性値"] | 入力値 |
プルダウンメニュー選択 | page | select() | #selectタグのid名 | optionタグのvalue属性 |
プルダウンメニュー選択 | page | select() | #select[name="name属性名"] | optionタグのvalue属性 |
ラジオボタン選択 | page | click() | #inputタグのid名 | - |
<input type="submit" value="SEND">
.......
await page.click( 'input[type="submit"]' );
<a href=index.jsp?user=hogekun>Click!</a>
.......
await page.click( 'a[href^="index.jsp?user="]' );
<div class="maker">
<button>Click Me!</button>
<a href="index.php?maker=Tesla">Click Me!</a>
<div>Please Click upper 2 items!</div>
</div>
.......
(
await page.$$eval(
selector,
a => a.filter(
a => a.textContent === 'Click Me!'
)
)
)[0].click();
<input type="text" name="hoge" id="baa">
.......
await page.type( 'input[name="hoge"]', 'Hoge' );
await page.type( 'input[id="baa"]', 'Baa' );
<select id="maker" name="maker">
<option value="Tesla">Tesla</option>
<option value="TOYOTA">TOYOTA</option>
<option value="BMW">BMW</option>
</select>
.......
await page.select('#maker', 'Tesla');
await page.select('select[name="maker"]', 'BMW');
<input type="radio" name="maker" value="Tesla" id="Tesla">Tesla
<input type="radio" name="maker" value="TOYOTA" id="TOYOTA">TOYOTA
<input type="radio" name="maker" value="BMW" id="BMW">BMW
.......
await page.click('#Tesla');
追記
あれから社内でWebブラウザを使うさまざまなクソつまらない定型事務作業をnode.jsサーバ上のExpressフレームワーク内でGoogle Puppeteer君を動かすことでバッチ処理化してきました。結果、大満足です(^^♪←昭和のおじさん風顔文字・・・汗;;。
バッチ処理がなじまない事務作業は、オンプレミスのnode.jsサーバをサクっと立ててWebアプリケーション化しました。
たとえば、グループ会社内情報共有掲示板の数十ある部署別スレッドに月次定型的な「みんな大好きエクセル帳票君」を定型文とともに投稿するなんていう「クソつまらない」事務「作業」はWebアプリケーションの画面にあるボタンをポチっと押すだけで、サーバーサイドでWebブラウザを一切表示せずに、黙々とバッチ的に即時実行してしまいます。
Google Puppeteer君半端ねぇ~(^_-)-☆。単純労働、事務大嫌いで、エクセル君キライなITエンジニアの味方 Puppeteer君 サイコウ!