puppeteer でファイルをダウンロードするときに、任意のパスと名前を指定して保存する

Pocket

Puppeteer を使ってファイルをダウンロードする際に、任意のパスと名前で保存したい。

残念ながら、 現時点ではシンプルな方法は提供されていないようだ。
以下の Issue で何年にもわたって議論されているものの、 「コレ!」 という解決方法は無さそう。
Question: How do I get puppeteer to download a file? · Issue #299 · puppeteer/puppeteer

しかし、 この Issue の #issuecomment-668087154 のコメントで、 なかなか泥臭い方法で実現するヒントが書かれていた。
これを参考にして、任意のパスと名前でダウンロードファイルを保存してみよう。

実行方法

あらかじめ、 puppeteer の npm パッケージをローカルにインストールしておく。

npm install puppeteer --save

その状態で、後述の .js ファイルを nodejs で実行すれば OK だ。

node puppeteer-download-with-specify-name.js

コードと解説


何をしているのかというと、 GitHub 上の puppeteer のソースコード ZIP ファイルをダウンロードする際に、 Chrome DevTools Protocol を直叩きして、 任意のパスとファイル名で保存している。

具体的なポイントは、主に 以下の 2点。

  • Page.setDownloadBehavior メソッドで、 ファイルのダウンロードの許可とダウンロード先のディレクトリを指定
  • Fetch.enable メソッドと Fetch.requestPaused イベントで、 ファイルダウンロードのレスポンスに Content-Disposition HTTP ヘッダーを無理やりねじ込む

Content-Disposition HTTP ヘッダー のドキュメントに書かれている通り、 attachmentfilename ディレクティブを指定することで、 ファイルが (ブラウザ内で表示されるのではなく)ダウンロードが必要であることと、 ダウンロード時のファイル名を指定することができる。

但し、 Page.setDownloadBehavior メソッドは 実験的で且つ非推奨 なので、 将来にわたってサポートが続くかどうかはわからない点は、注意だ。
少なくとも、 Chromium 92.0.4512.0 (r884014) では問題なく動いている。

ちなみに、実行する Chromium はヘッドレスモードでもヘッドフルモードでもどちらでも意図通り動くはず。


この方法は Chrome DevTools Protocol に思いっきり依存しているので、 Selenium など他のブラウザ自動化ツールでは同一の方法が難しく (※)、 Puppeteer ならではの方法と言える。
※: Selenium 4.x のプレリリース版を使えば、 Chrome DevTools Protocol にアクセスできるようだが、 イベントハンドラを書くのが難しそう? ドキュメントがそろってないのでまだなんとも…

スクレイピング中にファイルをダウンロードする場合などでは、保存先のパスと名前を指定できたほうが良い気がするのだが……
今後の puppeteer や Chrome DevTools Protocol の更新でもっと簡単に実現できるようになることを期待しよう。

コメントを残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください