playwright-coreでFirefox Nightlyを自動操作...できない


playwright-firefox (ブラウザのダウンロード機能付きのやつ) を使った自動操作(ggrks)は、以前に↓の記事を書いた。
https://qiita.com/YusukeIwaki/items/127dba7bb7197ea8d91b

でも、あらかじめFirefoxを詰め込んだDockerコンテナでplaywright自動操作したいときはブラウザダウンロード機能いらないんだけど...? みたいなことは稀によくあるので、puppeteer-coreとMacにインストール済みのChromeを使って自動操作 のFirefox版の手順を調べてみた。

playwright-core + Firefox Nightlyで自動操作を試みる

playwright-core使ってできる? と思って調べた。

ggrksのソース

TypeScriptを書くのが面倒なので、今回はJavaScriptで。

playwright-coreなので、executablePathにFirefox Nightlyのパスを指定しているのがポイント。

ggrks.js
const playwright = require("playwright-core");

const launchOptions = {
  executablePath: "/Applications/Firefox Nightly.app/Contents/MacOS/firefox",

  // ブラウザ画面を表示しながら(ヘッドレスモードを無効にする)。
  headless: false,

  // 人間味のある速度で入力/操作する。
  slowMo: 50,
};
playwright.firefox.launch(launchOptions).then(async (browser) => {
  // 大抵のサンプルコードはここで単純に browser.newBrowserContext(), browserContext.newPage() しているだけのものが多いが、
  // ブラウザを開いたときにすでに1つタブが開いている場合には、2つ目のタブが開いてしまう。
  // それを防ぐため、すでにタブが開いている場合にはそれを使うようにする。
  let browserContext = await browser.defaultContext();
  let browserPages = await browserContext.pages();
  let page =
    browserPages.length > 0 ? browserPages[0] : await browserContext.newPage();

  await page.goto("https://google.com/");
  await page.click("input[name='q']");
  await page.keyboard.type("playwright");
  await page.keyboard.press("Enter");
});

動かない。エラーになった。

2020.09.09現在はできない。なんか変な起動パラメータを指定しちゃってるらしく、エラーになる。

playwrightのソースを読んでみると...

firefox.ts
    const firefoxArguments = ['-no-remote'];
    if (headless) {
      firefoxArguments.push('-headless');
    } else {
      firefoxArguments.push('-wait-for-browser');
      firefoxArguments.push('-foreground');
    }
    firefoxArguments.push(`-profile`, userDataDir);
    firefoxArguments.push('-juggler', '0');
    firefoxArguments.push(...args);
    if (isPersistent)
      firefoxArguments.push('about:blank');
    else
      firefoxArguments.push('-silent');
    return firefoxArguments;

Jugglerっていう、改造版Firefoxみたいなやつ前提のコードっぽくみえる。
Firefox NightlyはChromeと同様に remote-debugging-port などを指定しないといけないはず...

おそらくそれが原因で動かないんだろう。

puppeteer-core + Firefox Nightlyで自動操作を試みる

同様のソースコードをPuppeteerで試してみよう。puppeteerは product: firefox 指定をすることでChromeではなくFirefox起動モードになる。

ggrks.js
const puppeteer = require("puppeteer-core");

const launchOptions = {
  product: "firefox",
  executablePath: "/Applications/Firefox Nightly.app/Contents/MacOS/firefox",

  // ブラウザ画面を表示しながら(ヘッドレスモードを無効にする)。
  headless: false,

  // 人間味のある速度で入力/操作する。
  slowMo: 50,
};
puppeteer.launch(launchOptions).then(async (browser) => {
  // 大抵のサンプルコードはここで単純に browser.newBrowserContext(), browserContext.newPage() しているだけのものが多いが、
  // ブラウザを開いたときにすでに1つタブが開いている場合には、2つ目のタブが開いてしまう。
  // それを防ぐため、すでにタブが開いている場合にはそれを使うようにする。
  let browserPages = await browser.pages();
  let page =
    browserPages.length > 0 ? browserPages[0] : await browser.newPage();

  await page.goto("https://google.com/");
  await page.click("input[name='q']");
  await page.keyboard.type("puppeteer");
  await page.keyboard.press("Enter");
});

puppeteer-coreなら Firefox Nightlyが動いた!

puppeteerのソースコードを読んでみると...

Launcher.ts
    const firefoxArguments = [];
    if (!ignoreDefaultArgs) firefoxArguments.push(...this.defaultArgs(options));
    else if (Array.isArray(ignoreDefaultArgs))
      firefoxArguments.push(
        ...this.defaultArgs(options).filter(
          (arg) => !ignoreDefaultArgs.includes(arg)
        )
      );
    else firefoxArguments.push(...args);

    if (
      !firefoxArguments.some((argument) =>
        argument.startsWith('--remote-debugging-')
      )
    )
      firefoxArguments.push('--remote-debugging-port=0');

    let temporaryUserDataDir = null;

    if (
      !firefoxArguments.includes('-profile') &&
      !firefoxArguments.includes('--profile')
    ) {
      temporaryUserDataDir = await this._createProfile(extraPrefsFirefox);
      firefoxArguments.push('--profile');
      firefoxArguments.push(temporaryUserDataDir);
    }

しっかり remote-debugging-port を指定している。

まとめ

2020.09.09現在、インストール済みのFirefoxを使って自動操作するには、playwright-coreではなくpuppeteer-coreの方を使う必要がある。

 

蛇足

実は、puppeteer-rubyっていうライブラリを個人で開発していて、 puppeteer-coreにはFirefox Nightlyを自動操作する機能があることは知っていました。
ただ、「Qiitaには、どうせならplaywrightの手順を共有したほうがよいかなー」と思い、調べたところ「playwrightだと動かん!!」ってなったのが、記事執筆の動機でしたw