Deno で Puppeteer を動かしてみたかった
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
いきなり 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
ブラウザが見つからないと言われたので、@puppeteer/browsers
で chrome-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 と比べて容量や依存関係が少ないそうなので、今回は多分こちらの方が適任。
ダウンロードした 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
chrome-headless-shell
を動かすのに必要な動的ライブラリが不足している模様。
ldd
コマンドで必要な動的ライブラリとリンク先が一覧表示されるので、その内の not found と表示されるものが不足している動的ライブラリ。
ldd chrome-headless-shell/linux-123.0.6286.0/chrome-headless-shell-linux64/chrome-headless-shell
不足している動的ライブラリがどのパッケージに含まれているかは 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
Node.js では上記までで動作したが、Deno で動かした場合は Warning: Not implemented: ClientRequest.options.createConnection
という警告が表示された後、しばらくすると落ちる。
どうやら ClientRequest.options.createConnection
が未実装なために異常終了している模様。
同様の理由で Playwright なども動かないらしい。
ws
を Deno の WebSocket に差し替えると正常動作するらしい。
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,
});
previs は deno.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