🚀

Playwrightで起動したブラウザ内のjsでfetchしたファイルをローカルに持ってきたい話

2023/04/24に公開

はじめに

PlaywrightでChromiumを起動し、その中でfetchしてきたファイルをローカルに保存する実装に少し詰まったのでまとめておく

問題点

通常のWebAPIで取得したjsonの文字列などであれば、以下のような実装で問題なく取得できる。

const json = await page.evaluate(async() => {
  const res = await fetch("https://some-json-api");
  return await res.json()
})

ただ、取得したのが文字列ではなく application/octet-stream などのバイナリデータの場合、 evaluate から RedableStreamblob を返すことが出来ない。以下のような実装だと binary がカラのobjectになってしまう。

const stream = await page.evaluate(async() => {
  const res = await fetch("https://some-binary-api");
  return res.body;
})
const blob = await page.evaluate(async() => {
  const res = await fetch("https://some-binary-api");
  const response = new Response(res.body);  
  return response.blob();
})

解決策

仕方ないのでbase64でエンコードした文字列を返し、受け取った側でデコードすることで対応した。

const base64 = await page.evaluate(async() => {
  const res = await fetch("https://some-binary-api");
  const response = new Response(res.body);  
  const blob = response.blob();
  const fileReader = new FileReader();
  fileReader.readAsDataURL(blob);
  
  // fileReader は Promiseを返すメソッドを提供していないらしい。
  return await new Promise((resolve) => {
    reader.onload = () => {
      resolve(reader.result); // reader.result がbase64の文字列。
    };
  });
})

const fileData = base64.split(",")[1]; // base64からヘッダ部分を取り除く
const buffer = Buffer.from(fileData, "base64");

これで、ダウンロードしたバイナリのデータを含む Buffer のデータが作成できる。

終わりに

もう少しシンプルなやり方があってもよさそうなものだが、とりあえず解決できたのでよしとする。
是非Playwrightによる優雅なスクレイピングライフを。

Discussion