【Node.js】Puppeteerでページ内の動画ファイルを一括ダウンロードする
#JavaScript
にメンテナンス済み
Puppeteerを使って、ページ内にある <video> の動画URLを取得し、Node.jsから一括でダウンロードする方法を紹介します。
Puppeteerはヘッドレスブラウザを操作するため、JavaScriptの実行後にコンテンツが生成されるページでも情報を取得できます。
チェック
動画の多くは著作権で保護されています。ダウンロードは、自分が権利を持つ動画、またはサイトの利用規約で許可されている範囲に限ってください。また、短時間に大量のアクセスをしないよう配慮してください。
動作環境
| ツール | バージョン |
|---|---|
| Node.js | 18 以上(fetch を使用) |
| Puppeteer | 20 以上 |
npm i puppeteer
ソースコード
動画のURLだけをPuppeteerで取得し、ダウンロード自体はNode.jsの fetch で行います。ブラウザを閉じたあとに処理できるため、シンプルに書けます。
import fs from 'node:fs/promises';
import puppeteer from 'puppeteer';
// 保存先(相対パス)
const DOWNLOAD_PATH = 'downloads';
// 接続先
const ADDRESS = 'https://example.com';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto(ADDRESS, { waitUntil: 'networkidle2' });
// 動画のURLを取得
const srcs = await page.evaluate(() =>
Array.from(document.querySelectorAll('video'))
.map((video) => video.currentSrc || video.src)
.filter(Boolean)
);
console.log(`${srcs.length}件の動画を検出しました。ダウンロードを開始します`);
await fs.mkdir(DOWNLOAD_PATH, { recursive: true });
// 動画をダウンロード
for (const [i, src] of srcs.entries()) {
const response = await fetch(new URL(src, ADDRESS));
if (!response.ok) {
console.error(`${src} の取得に失敗しました (${response.status})`);
continue;
}
await fs.writeFile(`${DOWNLOAD_PATH}/${i}.mp4`, Buffer.from(await response.arrayBuffer()));
console.log(`${i}.mp4 saved`);
}
} finally {
await browser.close();
}
.mjs で保存するか、package.json に "type": "module" を指定して実行してください。
ポイントは次のとおりです。
video.currentSrcを使うと、<video src>だけでなく<source>要素で指定された動画のURLも取得できますnew URL(src, ADDRESS)で、相対パスのURLも絶対URLに解決できます- 大きな動画をメモリに載せたくない場合は、
response.bodyをstream.pipelineでファイルに書き込む方法もあります
スクロールしないと表示されないページの場合
スクロールして初めて動画が読み込まれるページでは、page.goto のあとにスクロール処理を入れます。
await page.goto(ADDRESS);
// 下へ向かって10回スクロール
for (let i = 0; i < 10; i++) {
await page.evaluate(() => window.scrollBy(0, 1000));
await new Promise((resolve) => setTimeout(resolve, 200));
}
動画がvideo要素のsrcに設定されていない場合
遅延読み込みなどで、video 要素に直接 src が設定されていないページもあります。その場合は、開発者ツールでURLがどこに保持されているか(data-src 属性など)を確認し、page.evaluate の中身を調整してください。
動画のタイトルをページ上から取得して、ファイル名に使いたい場合は、page.evaluate の返り値をオブジェクトにします。
return Array.from(document.querySelectorAll('video')).map((video) => ({
src: video.currentSrc || video.src,
title: video.closest('article')?.querySelector('h1')?.textContent ?? '',
}));
ストリーミング配信(HLS / DASH)の動画には使えない
video.src が blob: から始まる場合、その動画はHLSやMPEG-DASHなどで分割配信されており、この方法ではダウンロードできません。