Open8

Deno で Puppeteer を動かしてみたかった

ProudustProudust

Deno で Puppeteer を動かし、任意の URL の HTML を PDF に変換する CLI を作れないか試してみたかった。

以下はバージョン情報。出力されていないが WSL 環境下。

$ \cat /etc/os-release 
PRETTY_NAME="Ubuntu 22.04.3 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.3 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy

$ deno --version
deno 1.40.3 (release, x86_64-unknown-linux-gnu)
v8 12.1.285.6
typescript 5.3.3

$ node --version
v18.16.0
ProudustProudust

いきなり Puppeteer を動かしてみる。

import * as puppeteer from "npm:puppeteer@22.0.0";

const browser = await puppeteer.launch({ headless: "shell" });
const page = await browser.newPage();

await page.goto("https://example.com");

const pdf = await page.pdf();

await Deno.stdout.write(pdf);

browser.close();

deno run \
    --allow-env \
    --allow-net=127.0.0.1 \
    --allow-read \
    --allow-run \
    --allow-write=/tmp \
    main.ts >test.pdf
ProudustProudust

ブラウザが見つからないと言われたので、@puppeteer/browserschrome-headless-shell をダウンロード。

deno run \
    --allow-env \
    --allow-read \
    --allow-net=edgedl.me.gvt1.com,googlechromelabs.github.io \
    npm:@puppeteer/browsers@2.0.0 install chrome-headless-shell

通常の Chrome と比べて容量や依存関係が少ないそうなので、今回は多分こちらの方が適任。
https://developer.chrome.com/blog/chrome-headless-shell

ProudustProudust

ダウンロードした chrome-headless-shell のパスを渡して起動を試みるも謎エラーで落ちる。

const browser = await puppeteer.launch({
  executablePath: "./chrome-headless-shell/linux-123.0.6286.0/chrome-headless-shell-linux64/chrome-headless-shell",
  headless: "shell",
});

仕方ないので少し書き換えて Node.js で動かしてみる

import * as puppeteer from "puppeteer-core";

const browser = await puppeteer.launch({
  executablePath: "./chrome-headless-shell/linux-123.0.6286.0/chrome-headless-shell-linux64/chrome-headless-shell",
  headless: "shell",
});
const page = await browser.newPage();

await page.goto("https://example.com");

const pdf = await page.pdf();

process.stdout.write(pdf);

browser.close();

面倒なので node_modules は Deno に作らせる

deno run \
    --allow-env \
    --allow-net=127.0.0.1 \
    --allow-read \
    --allow-run \
    --allow-write=/tmp \
    --node-modules-dir \
    main.ts
node main.mjs
ProudustProudust

chrome-headless-shell を動かすのに必要な動的ライブラリが不足している模様。
ldd コマンドで必要な動的ライブラリとリンク先が一覧表示されるので、その内の not found と表示されるものが不足している動的ライブラリ。

ldd chrome-headless-shell/linux-123.0.6286.0/chrome-headless-shell-linux64/chrome-headless-shell

https://qiita.com/one-kelvin/items/07bf9b99288e8ecfa4a2


不足している動的ライブラリがどのパッケージに含まれているかは apt-file で検索できる。

sudo apt install apt-file
sudo apt-file update
apt-file search libXdamage.so.1

私の環境では以下が不足していた。

sudo apt install \
  libasound2 \
  libatk-bridge2.0-0 \
  libatk1.0-0 \
  libgbm1 \
  libnss3 \
  libxcomposite1 \
  libxdamage1 \
  libxfixes3 \
  libxkbcommon0 \
  libxrandr2
ProudustProudust

Node.js では上記までで動作したが、Deno で動かした場合は Warning: Not implemented: ClientRequest.options.createConnection という警告が表示された後、しばらくすると落ちる。

どうやら ClientRequest.options.createConnection が未実装なために異常終了している模様。
同様の理由で Playwright なども動かないらしい。
ws を Deno の WebSocket に差し替えると正常動作するらしい。

https://github.com/denoland/deno/issues/20179
https://github.com/denoland/deno/issues/19507
https://github.com/denoland/deno/issues/16899

ProudustProudust

Puppeteer の Deno 用 Fork はどれもメンテ止まってそうだったので検討もしていなかったが、puppeteer_plus はまだ生きているようなので試しに差し替えた。
警告は出るが動いてそう。出力後もしばらく落ちないのは何かのタイムアウトを待ってそう。

import * as puppeteer from "https://deno.land/x/puppeteer_plus@0.19.0/core.ts";

const browser = await puppeteer.launch({
  executablePath:
    "./chrome-headless-shell/linux-123.0.6286.0/chrome-headless-shell-linux64/chrome-headless-shell",
  headless: true,
});
ProudustProudust

previsdeno.land/x/puppeteer を使用しているようなので試してみたが、案の定エラーになった。

error: Uncaught (in promise) TypeError: reader is not async iterable
  for await (const chunk of reader) {
                            ^
    at getReadableStreamAsUint8Array (https://deno.land/x/puppeteer@16.2.0/vendor/puppeteer-core/puppeteer/common/util.js:329:29)
    at Page.pdf (https://deno.land/x/puppeteer@16.2.0/vendor/puppeteer-core/puppeteer/common/Page.js:2606:24)
    at eventLoopTick (ext:core/01_core.js:64:7)
    at async file:///home/proudust/repos/github.com/proudust/deno-html-to-pdf/main.ts:12:13