Twitter に投稿された画像・動画をダウンロードする CLI ツール「twsv」を作った

Twitter に投稿された画像や動画をダウンロードする際、

としていた。

コレをもっと手軽に、複数のツイートからのダウンロードをバッチ処理できないかと思い、自分で同様のツールを作ってみることにした。その名も twsv。「TWitter SaVer」の略だ。

目次

先に作ったツールの紹介

twsv は Node.js 製の CLI ツール。npm でインストールできる。パッケージ名としては @neos21/twsv

$ npm install -g @neos21/twsv

裏では Twitter API を使っているので、各自 Twitter の Developer 登録を行い、API キーを発行しておくこと。環境変数で以下のように設定しておく。

# Twitter クレデンシャル情報を設定する
export TWITTER_CONSUMER_KEY='Your Consumer Key'
export TWITTER_CONSUMER_SECRET='Your Consumer Secret'
export TWITTER_ACCESS_TOKEN_KEY='Your Access Token Key'
export TWITTER_ACCESS_TOKEN_SECRET='Your Access Token Secret'

グローバルインストールしたら twsv コマンドが使えるようになっているので、以下のように Twitter の URL を引数で指定してやる。

# 指定ユーザのタイムラインより直近200件のツイートを取得し、それらに紐付く画像・動画を取得する
$ twsv https://twitter.com/USERNAME

# 指定のユーザのいいね一覧より直近200件のツイートを取得し、それらに紐付く画像・動画を取得する
$ twsv https://twitter.com/USERNAME/likes

# 指定のツイートから画像・動画を取得する
$ twsv https://twitter.com/USERNAME/status/0000000000000000000

デフォルトの保存先は、コマンドを実行した時のカレントディレクトリに twsv-downloads/ ディレクトリを作り、その下にファイルを保存する。

保存先ディレクトリを変更する場合は、環境変数か第2引数で指定できる。両方指定されている場合は第2引数が優先される。

# 環境変数で指定して実行
$ export TWSV_SAVE_DIRECTORY='/home/downloads'
$ twsv https://twitter.com/USERNAME/status/0000000000000000000

# 第2引数で指定して実行
$ twsv https://twitter.com/USERNAME/status/0000000000000000000 '/home/downloads'

動画については、画質 (ビットレート) が一番良いモノを選んで取得している。

以降ツールを作るまでの苦労話

Syncer のツールはブラウザで操作し、「URL 貼り付け」→「動画 URL を選んで DL」というステップを踏んでいた。複数のツイートから動画を拾いたい場合は毎回この操作でダルかった。

そこで、ツールは CLI ツールにし、ツイートの URL をかき集めたらバッチ処理で一気に DL できるようにすることにした。

当初は Twitter API を使わないで済む方法がないかと思い、Web ページをスクレイピングして取得できないか調べてみた。

画像については、ページ内の img 要素を取得し、https://pbs.twimg.com/media/ を含む URL を引っ張ってくればダウンロードできた。

動画については、video 要素を調べてみたが、blob: から始まる URL になっていて、うまくダウンロードできなかった。Syncer では https://video.twimg.com/ext_tw_video/【色々…】.mp4 といった URL が拾えていたのだが、ウェブページ上からはこの URL は拾えなさそうだったので断念した。

仕方なく Twitter API を使ってツイートオブジェクトを取得し、画像や動画の URL を拾い上げることにした。

Twitter API を叩くのは、公式の twitter npm パッケージを使った。いいね一覧とユーザタイムラインを拾えそうだったので、ツイート単体の DL だけでなく、そういうツイート一覧から拾えるだけ画像や動画を拾えるようにも対応させてみた。

ツイートオブジェクトの中は愚直に見ていった。画像も動画も、ツイートオブジェクトの .extended_entries.media という配列プロパティの中に入っている。画像の場合は、配列の中の各要素の .media_url が目的の URL。

動画の場合は .video_info.variants プロパティが配列になっていて、ビットレートごとに目的の URL が入っている。そこで、ビットレートを比較して、ビットレートが一番大きい動画の URL を拾うことにした。

動画のダウンロードには request-promise を使ったが、同時接続数が多いと上手くダウンロードできなくなってしまったので、同時接続数を制限する仕組みを入れた。以下の記事のコードほぼそのまま。

保存先ディレクトリの存在確認、作成、保存処理などは fspath あたりの Node.js 組み込みのパッケージを使っている。

最終的に、外部依存パッケージは twitterrequestrequest-promise の3つで、コードは単一の JS ファイルに454行でまとまった。

以上

割と愚直にコーディングしまくっただけだが、自分が欲しいモノが作れた。Twitter API を使っているので、API コールのレート制限が確認できるとなお良しか。コンソール出力はもう少し改善の余地ありそうだが、特に気にせず。w