🚀
Playwrightで起動したブラウザ内のjsでfetchしたファイルをローカルに持ってきたい話
はじめに
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
から RedableStream
や blob
を返すことが出来ない。以下のような実装だと 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