【Python】Python初心者がYoutubeから動画をダウンロードするスクリプトを解説

#1. はじめに

とある動画素材を探してネットの海を彷徨っていたところ、FireFoxのアドオンであるVideo Download Helperで動画をダウンロードすると、動画の左上にQRコードが烙印されてしまうという噂を発見。ついでにmacではコンパニオンアプリと呼ばれる補助アプリを複数ダウンロードさせられるようです。

そこで方針転換し動画DLできるスクリプトをQiitaで探したところ、@ryuta69さんの素晴らしいスクリプトを発見しました。
そのまま使用させていただくことも考えたのですが、Pythonの入門がてらgithubのコードを一行一行読み解いて解説記事を作ることにしました。

完成したスクリプトでは以下のようにコマンドを打つことで、YouTubeから動画をDLできます。

```
python [スクリプトの名前].py [YouTube動画のURL]
```

#2. 対象者
 - 超Python初心者
 - Pythonを触ったことがないプログラミング経験者

#3. 対象じゃない人
 - YouTubeの動画をDLしたい人　→ ryuta69さんの記事を読んでください。
 
#4. 前提条件
[ryuta69さんの記事](https://qiita.com/ryuta69/items/126fee12deb83cf3296d)にある通り、必要なツールをインストールしてください。

#5. 今回解説するryuta69さんのコード（一部省略）
今回はYouTubeのダウンロードのみ解説するので、コードの後半は省略させていただきます。

```python:main.py
import re, sys, os, time, subprocess
from bs4 import BeautifulSoup
from urllib.request import urlopen, urlretrieve

main_links = [
"https://www.youtube.com/",
"https://soundcloud.com/",
"https://www.nicovideo.jp/"
]

ero_links = [
"https://www.xvideos.com/"
]

# youtube soundcloud nicovideo
if any(s in sys.argv[1] for s in (main_links)):
    ans = input("type mp3 or mp4: ")
    if "https://www.youtube.com/" in sys.argv[1] and "list=" in sys.argv[1]:
        th_list = sys.argv[1].split("list=")[1]
    else:
        th_list = sys.argv[1]
    if ans=="mp3":
        cmd = 'youtube-dl -o ./%(playlist)s/%(title)s.%(ext)s -ci --extract-audio --audio-format mp3 --add-metadata ' + th_list
    if ans=="mp4":
        cmd = 'youtube-dl -o ./%(playlist)s/%(title)s.%(ext)s -i -f mp4 --add-metadata ' + th_list
    subprocess.check_call(cmd.split())
    sys.exit()
```
（後略）
引用元
[xvideo-youtube-niconico-soundcloud-download
](https://github.com/ryuta69/xvideo-youtube-niconico-soundcloud-download/blob/master/main.py)

#6. 一行ずつ解説
コード中の各機能ごとに解説したいと思います。
##6-1. ライブラリの宣言
Pythonのコードでは、冒頭で使用する標準ライブラリやパッケージを宣言します。

```python
import re, sys, os, time, subprocess
```

この一行で宣言されている5つは全てPythonの標準ライブラリです。
**・re ： 正規表現の処理。文字列の抽出や置換、分割など。**
**・sys ：　インタプリタや実行環境の情報を扱う。コマンドラインの引数も扱える。**
**・os ：　OSに依存する機能を使えるようにする。**
**・time ：　時間に関する広い用途で使われる。時刻取得や時刻フォーマット作成など。**
**・subprocess ：　Python以外のアプリを起動したり、実行結果を取得できる。**


続いては外部パッケージです。

```python
from bs4 import BeautifulSoup
from urllib.request import urlopen, urlretrieve
```

**・form ~ import ~**
from以下のライブラリから、import以下のモジュールのみ取ってくるという記述です。

**・BeautifulSoup**
webサイトのHTML上から要素を取ってくること（スクレイピング）ができるライブラリです。
今回は使用しませんが、他の動画サイトからDLするときは必要になります。

**・urllib.request**
URLを読みこめるモジュールで、その中からurlopenとurlretreiveという関数を用います。
それぞれurlopenはGETリクエストを投げる、urlretreiveはオブジェクトをダウンロードするという働きがあります。
こちらも今回は使用しません。

##6-2. DL対象サイトを指定する


```python
main_links =[
"https://www.youtube.com/",
"https://www.nicovideo.jp/"
]
```

動画をDL対象のサイトのURLを、このリストに格納して指定します。
コマンドライン上で入力したURLがこのリスト内の要素の文字列とマッチする場合、DLすることができます。

##6-3. URLの動画サイトの判定
ここから、入力されたURLが指定した動画サイトのものかを判定します。

```python
if any(s in sys.argv[1] for s in (main_links)):
```
冒頭の判定文ですが、これは入力されたURLが先ほどのmain_linkで指定したサイトのものかどうかを判定しています。
様々なメソッドが使用されているので、一つ一つ解説します。
解説の都合上順番が前後しますが、ご了承ください。

**・"for s in (main_links)"**
main_linksリストの各要素を"s"と置いて、それぞれに処理を施します。

**・"s in sys.argv[1]"**
sys.argvはコマンドライン上で入力された引数のリストで、[1]はそれの２番目を指しています。（１番目は[0]で、ファイル名を表す。）
ここでの"in"はfor文中のinとは異なり、検索の演算子の役割を担っています。
直訳すると、s（main_linksで指定したサイトURLの文字列）がsys.argv[1]（コマンドライン上で入力されたURL）の中に存在するか。
つまり、入力したURLがYouTubeかニコニコ動画の動画であれば、trueを返します。

**・any(条件式)**
条件式が一つでもtrueであれば、trueを返します。
これと対照なのがall(条件式)メソッドで、こちらは条件式が全てtrueのときのみtrueを返します。

結局このif文では、コマンドライン上で入力したURLがYouTubeかニコニコ動画のものであればtrue、そうでなければfalseという判定をしています。

##6-4. DLしたいフォーマットの指定

```python
ans = input("type mp3 or mp4: ")
```

ryuta69さんのスクリプトはmp3でもmp4でもDLできるようになっているので、ここでコマンド実行者にどちらかを指定させています。
入力した文字列がans変数に格納されます。

##6-5. YouTube動画がプレイリストにある場合の対応
YouTubeでプレイリスト内の動画のURLをコピペすると、"https://www.youtube.com/watch?v=hogehoge&list=fugafuga" のようにURLに「list=fugafuga」というプレイリストのIDがくっついてきます。
プレイリストごとダウンロードするため、このURLからプレイリスト部分以外を消し去る処理を行います。

```python
    if "https://www.youtube.com/" in sys.argv[1] and "list=" in sys.argv[1]:
        th_list = sys.argv[1].split("list=")[1]
    else:
        th_list = sys.argv[1]
```

一行目のif文では、入力したURLがYouTubeのもので、さらに"list="の文字列が含まれているかを判定し、プレイリスト経由の動画であるかを判定しています。falseの場合はそのままでいいのですが、trueの場合は特別な処理が必要になります。
そこで二行目では、"https://www.youtube.com/watch?v=hogehoge&list=fugafuga"というURLを、"https://www.youtube.com/watch?v=hogehoge&"と"fugafuga"に分割しています。

split(文字列)関数は、長い文字列を指定した文字で分割することができます。
この場合、"list="の前と後の二つを要素に含んだ["https://www.youtube.com/watch?v=hogehoge&", "fugafuga"]というリストを生成します。
そこでsys.argv[1].split("list=")[1]とリストの２番目を指定することで、上手くプレイリスト部分のIDを取り出すことができました。

##6-6. 動画をDLする
いよいよ大詰めです。動画をDLするコマンドを指定していきます。

```python
    if ans=="mp3":
        cmd = 'youtube-dl -o ./%(playlist)s/%(title)s.%(ext)s -ci --extract-audio --audio-format mp3 --add-metadata ' + th_list
    if ans=="mp4":
        cmd = 'youtube-dl -o ./%(playlist)s/%(title)s.%(ext)s -i -f mp4 --add-metadata ' + th_list
```

mp3とmp4のどちらの場合でも、cmdという変数にコマンドの文字列を代入します。
"youyube-dl"というのはコマンドライン上でYouTubeから動画をDLできるツールです。
これに様々なオプションを付け加えていきます。

**・-o [ファイル名]**
保存するファイル名を指定できます。"%()s"ものはテンプレートといい、動画ファイルの情報を引っ張ってくることができます。例えば、"%(ext)s"は拡張子を表しています。

**・-c**
ダウンロードが中断されても再開しないようにします。

**・-i**
ダウンロードエラーを無視します。

**・--extract-audio**
動画ファイルから音声のみを抽出します。

**・--audio-format [フォーマット]**
音声ファイルの形式を指定します。ここではmp3に指定しています。

**・--add-metadata**
ファイルにメタデータを書き込みます。

**・-f [フォーマット]**
フォーマットを指定します。

そのほかにもこちらのサイトに全てのオプションが紹介されています。
[youtube-dl オプション一覧及びそのメモ](https://masayoshi-9a7ee.hatenablog.com/entry/20150905/1441414821)

##6-7. コマンドを実行しプログラムを終える
最後に前項で書いたコマンドを実行します。

```python
    subprocess.check_call(cmd.split())
    sys.exit()
```

冒頭に紹介した通り、外部アプリを実行するときはsubprocessライブラリを使います。
check_call(リスト)関数はプログラムの出力とエラーを逐次出力してくれます。
check_call関数の引数はリストなので、cmd.split()をすることでコマンドをスペースで区切ったリストにしています。
つまり、["youtube-dl", "-o", …]という形に変換しています。

二行目のsys.exit()は、プログラムを終了させるコマンドです。
URLがYouTubeまたはニコニコ動画である場合のif文中の処理が終わったので、ここでプログラムを終了させる必要があります。

#7. 動作確認
コマンドライン上で以下のコマンドを打ってみましょう。
無事DLできたら成功です！

```
python [スクリプトの名前].py [YouTube動画のURL]
```

動かない場合は以下の原因が考えられます。
1. デフォルトのpythonコマンドのバージョンが古い

```
python -V
```

を実行してpythonのバージョンを確認しましょう。
python3がすでにインストールされている場合は、ホームディレクトリの.bash_profile（なければ作成）にエイリアスを追加するとpython3のコマンドを実行することができます。

```bash
alias python="python3"
```

2. pipがインストールされたpythonのバージョンが古い

```bash
pip -V
```

と打つと最後尾にpythonのバージョンが表示されます。
ここがpython3ではなかった場合は、コマンドを実行する際に"pip"の代わりに"pip3"を打つことで、python3にライブラリをインストールすることができます。

#8. 最後に
解説は以上になります。
コードを引用させていただいたryuta69さん、本当にありがとうございました。

#9. 参考記事、引用コード
スクリプトの紹介記事（Qiita）
[XvideosのMP3/MP4を一括でダウンロードするスクリプト作ってみた(おまけでyoutubeとかニコニコもOK)](https://qiita.com/ryuta69/items/126fee12deb83cf3296d)

スクリプトのコード（github）
[xvideo-youtube-niconico-soundcloud-download
](https://github.com/ryuta69/xvideo-youtube-niconico-soundcloud-download/blob/master/main.py)

#追記（2019年7月15日）
プレイリストをダウンロードする際、URLに"&"が含まれているとbashくんが誤作動してしまうみたいです。

```bash
python main.py "https://www.youtube.com/watch?v=hogehoge&list=fugafuga?"
```

このようにダブルクオートで囲うと上手くいきます。