Tech Nuggets (15) In English

vimcasts.org のすべての動画をダウンロードする

Posted:

Vimcasts という良いサイトを見つけた。vim の基本的な操作や有名なプラグインの使い方を動画と記事で紹介していて、非常にわかりやすく、よくまとまっていておすすめだ。 現在、76 本のエピソードが掲載されいる。

動画は1本あたり5分ほどだがまとめると結構な量なので、オフラインで視聴するべくダウンロードするスクリプトを書いてみた。久しぶりに BeautifulSoup を使ったが、やっぱり便利だ。

下記の手順ですべての動画をダウンロードした。

  1. BeautifulSoup を使って動画のタイトルとURLを取得
  2. 動画ファイルを1つだけダウンロードする fish スクリプトを作成
  3. xargs-P オプションを指定して (2) を並列で実行

並列ダウンロードの箇所(2),(3)は少し冗長に思える。今回のような簡単な並列処理を行うときは xargs -P をよく使うが、 xargs に複数のコマンドを渡すと一気に読みにくくなるので「数行の shell スクリプトをわざわざ書く」ことがままある。このひと手間が面倒だ。Python で並列処理が書ければ...

今回は「12の同時接続程度でサーバーに過度な負荷はかからないだろう」という根拠のない予想のもと並列でダウンロードしましたが、適切なリクエストの頻度はどの程度なのだろうか。 Web スクレイピングというと Librahack事件 が思い出されるが、「リクエスト毎に1秒空ける」のが2021年現在でもゴールドスタンダードなのだろうか。

以下、作成したスクリプトと実行結果です。

1. BeautifulSoup を使って動画のタイトルと URL を取得

import requests
from bs4 import BeautifulSoup


def get_title_and_url(n, extension):
    url = f"http://media.vimcasts.org/videos/{n}/"
    page = requests.get(url)
    soup = BeautifulSoup(page.content, 'html.parser')

    for a in soup.select('a'):
        href = a.get('href')
        if f".{extension}" in href:
            return f"{n}_{a.text},{url}{href}"


for i in range(1, 68 + 1):
    print(get_title_and_url(i, 'ogv'))
for i in range(69, 76 + 1):
    print(get_title_and_url(i, 'mp4'))

エピソード #1~#68 は .ogv 形式、#69以降は .mp4 形式の動画をダウンロードしている。ここはイケてない。 初めはすべてのエピソードに .ogv 形式の動画が存在する前提で書いていたが、実行したところ #69 以降には存在せず、エラーが出たため場当たり的に修正した箇所だ。 始めから「存在するなら .ogv、存在しないなら最初にヒットした動画ファイルの URL を取得する」というように書けばよかった。

2. 動画ファイルを 1 つだけダウンロードする fish スクリプトを作成

#!/usr/bin/fish
set title (echo $argv | cut -d',' -f1)
set url (echo $argv | cut -d',' -f2)
curl -q --output "$title" "$url"

(1)で出力した csv を1行ずつ、1つの文字列として渡す想定で書いている。

$ download.fish "title,url"

3. xargs-P オプションを指定して (2) を並列で実行

$ chmod +x download.fish
$ python get_title_and_url.py > title_and_url.csv
$ cat title_and_url.csv | xargs -I'{}' -P 12 ./download.fish {}
.
.
.

$ ls -1
vimcasts-01_show_invisibles.ogv
vimcasts-02_tabs_and_spaces.ogv
vimcasts-03_whitespace_preferences_and_filetypes.ogv
.
.
.

🎉

あとがき

CLI で並列処理を行うときは xargs -P をよく使っている。GNU Parallel も頭をよぎるが構文が覚えられず、またドキュメントを読むのも億劫で食指が動かない。パターン総当りのような処理であれば parallel に軍配が上がると思うのだが。

未だに知らないこと (しかも基礎的な!) がたくさんあることを思い出させてくれた Drew Neil さん、素晴らしいコンテンツをありがとう。