Ribbit's works

【Node.js】対象URLにリンクされている全ての動画ファイルをダウンロードする

#JavaScript
にメンテナンス済み
記事のトップ画像

puppeteerを利用して、URL内にリンクされている動画ファイルをダウンロードします。

puppetterを使うことで、JavaScriptが動いたあとにコンテンツが生成されるような画面でも正しく情報を取得することができます。

実行環境

HeaderHeader
Node.js14.15.0
npm6.14.8
npm i puppeteer

ソースコード

全てpuppeteerのブラウザオブジェクトで完結することもできますが、URLさえ取得してしまえばNodeから直接URLにアクセスしてデータを取得できるので、今回はそちらの方法を使用しています。

// 保存先(相対パス)
const DOWNLOAD_PATH = "downloads";

// 接続先
const ADDRESS = "https://google.com";

const puppeteer = require("puppeteer");
const fs = require("fs");
const https = require("https");

(async () => {
  const browser = await puppeteer.launch({
    // headless: false,
  });

  const page = await browser.newPage();

  await page.goto(ADDRESS);

  // 動画のURLを取得
  const srcs = await page.evaluate((selector) => {
    const videos = Array.from(document.querySelectorAll(selector));

    return videos.map((video) => video.src);
  }, "video");

  await browser.close();

  console.log(`${srcs.length}件の動画を検出しました。ダウンロードを開始します`);

  // 動画をダウンロード
  let i = 0;
  for (const src of srcs) {
    const file = fs.createWriteStream(`${DOWNLOAD_PATH}/${i++}.mp4`);

    await https.get(data.src, (response) => {
      response
        .pipe(file)
        .on("error", (error) => console.error(error))
        .on("finish", () => console.log(`${i - 1}.mp4 saved`));
    });
  }
})();

今回はJavaScriptなどで動的にコンテンツが生成されることを想定しているため、スクロールしてコンテンツを表示させる場合は以下のコードを追加します。

await page.goto(ADDRESS)

// 表示後に下へ下へスクロール
await page._client.send(
  'Input.synthesizeScrollGesture',
  {
    x: 0,
    y: 0,
    xDistance: 0,
    yDistance: -1000,
    repeatCount: 10,
    repeatDelayMs: 200
  }
);

ページによっては、動画を遅延させて読み込ませるために、videoの要素に直接srcを設定していないケースがあります。

そういった場合は、page.evaluateの部分を調整してください。

今回は動画のファイル名を連番にしていますが、動画タイトルをウェブページ上から取得したい場合も、page.evaluateの返値を

return {
  'src': src,
  'title': '画面上から取得したタイトル'
}

上記のようにしておけば可変的に対応可能だと思います。