Puppeteerでテキストボックスに文字列をコピー&ペーストする方法

2020年7月25日土曜日

Node.js Puppeteer

t f B! P L


Puppeteer は Headless Chrome(Chromium)を操作するための Node.js 用ライブラリです。

2chまとめブログの自動投稿システムを作ったときにもお世話になった便利なライブラリなのですが、使っているとたまに不満点もあったりします。


page.type() は文字の入力速度が遅い

通常、Puppeteerでテキストボックスに文字列を入力するときは page.type() メソッドを使うのが一般的です。

const puppeter = require('puppeteer');

(async () => {

    const text = 'サンプルテキスト';
    const selector = 'textarea[id="xxxx"]'; // セレクターは仮の値

    // Webブラウザを起動
    const browser = await puppeter.launch({
        headless: false,
        slowMo: 0
    });

    // 対象のページへ遷移
    const page = await browser.newPage();
    await page.goto('http://xxxx.xxxx.jp'); // URLは仮の値

	// テキストボックスに文字列を入力
    await page.type(selector, text);

})();


簡単な用途であればこれで問題ないのですが、このメソッドはぶっちゃけ処理速度が超遅いです。(キーボード入力を模倣しているので仕方ないのですが)
なので入力文字数が数百、数千となってくると処理時間に無視できないレベルの影響が出てきます。
一瞬でコピー&ペーストができるようなメソッドがあれば良かったのですが、どうもPuppeteerではそういったものは提供されていないようです。。

コピー&ペースト処理を自作

というわけで、コピー&ペーストの処理を自作してみました。
以下が作成したコードになります。

const puppeter = require('puppeteer');
const path = require('path');
const fs = require("fs").promises;

(async () => {

    const text = 'サンプルテキスト';        // ここに長い文字列が設定される想定
    const selector = 'textarea[id="xxxx"]'; // セレクターは仮の値

    // Webブラウザを起動
    const browser = await puppeter.launch({
        headless: false,
        slowMo: 0
    });

    // 対象のページへ遷移
    const page = await browser.newPage();
    await page.goto('http://xxxx.xxxx.jp'); // URLは仮の値

    // コピー&ペースト処理

    // 貼り付ける予定のテキストを一旦ローカルのテキストファイルに保存する
    const tempTextPath = __dirname + '/temp_text.txt'
    await fs.writeFile(tempTextPath, text);

    // 相対パスを絶対パスに変換
    let pathName = path.resolve(tempTextPath).replace(/\\/g, '/');
    // Windowsの場合はスキームとパスの間にスラッシュが必要
    if (pathName[0] !== '/') {
        pathName = '/' + pathName;
    }
    // ローカルテキストのURLを取得
    const tempTextURL = encodeURI('file://' + pathName);

    // 保存したテキストファイルをWebブラウザの別ページで開く
    const tempPage = await browser.newPage();
    await tempPage.goto(tempTextURL);
    await tempPage.waitFor(100);

    // 別ページで開いたテキストの内容を全選択(Ctrl + A)
    await tempPage.keyboard.down('Control');
    await tempPage.keyboard.press('KeyA');
    await tempPage.keyboard.up('Control');
    await tempPage.waitFor(100);

    // 選択したテキストをクリップボードへコピー(Ctrl + C)
    await tempPage.keyboard.down('Control');
    await tempPage.keyboard.press('KeyC');
    await tempPage.keyboard.up('Control');
    await tempPage.waitFor(100);

    // 開いた別ページを閉じる
    await tempPage.close();

    // 貼り付け先のテキストボックスにフォーカスを当てる
    await page.focus(selector);
    await page.waitFor(100);

    // クリップボードの内容を貼り付ける(Ctrl + V)
    await page.keyboard.down('Control');
    await page.keyboard.press('KeyV');
    await page.keyboard.up('Control');
    await page.waitFor(100);

    // 一時ファイルを削除する
    await fs.unlink(tempTextPath);

})();

keyboard.down()、keyboard.press() メソッドを使ってコピー&ペースト(Ctrl+A → Ctrl+C → Ctrl+V)を実現しています。
注意点としては一時的にクリップボードを使用するため、他のプロセスが書いたクリップボードの内容が上書きされてしまう可能性があります。
バックグラウンドで動かすときはなるべく単体で使用するようにしましょう。

自己紹介

SESから工場へ転職 ⇒ その後SESへ出戻った底辺客先常駐エンジニアです。 主に組み込み、オープン系の現場で詳細設計~実装を担当しています。 使用言語:C/C++/C#/Java/JavaScript (Node.js)/Python etc..

QooQ