Pytubeを使ってYoutubeの動画をDLしてみる

NCS（NoCopyrightSounds）やAudioLibraryの楽曲（というか動画）をダウンロードする方法が（Pythonじゃなければ）めんどくさかったので備忘録的に記載しておく。

「Pytube」で検索してわかるような人向けではなく、
「youtube 動画 ダウンロード Python」で検索した人向けの記事ってことで・・・。

記事書いてる本人も、Pythonはおろかプログラミング初心者です:weary:

# 従来の方法

Youtubeの動画をDLする方法は昔からいろいろありますが、大まかに

* WEB上にあるあやしいサービスを利用する
* （サイトを問わず）ダウンロードしてくれるソフトウェアを利用する
* GoogleChromeのキャッシュから復元する（もう出来ない気がする）

ほとんどの人が「Youtubeの動画を保存したいな」と思ったら、WEB上のあやしいサービスを利用することが多いと思うが、ここ最近、そのサービス自体も「youtubeに弾かれる」ような状態:thinking:

youtubeから見れば、広告収入が得られないアクセスなのだから当然といえば当然だが・・・。
もちろん長生きなサービスもあるが、UIUXも低く、そして広告だらけだ。

# Pytubeを選ぶ理由

## すごく簡単にできたから

Python（というかプログラミング自体）初学者の私にとっては、覚えるライブラリも少なく、メソッドも少ないので最高だと思った。直観。

## クライアント側の動作が軽い

重たいライブラリではないため、すごく軽快に動作する。
内部的にどう処理してるのかは全くわからないが、すごい。

## 何故かダウンロードが爆速

WEBのサービスを利用したことがある人は多いと思うが、一番の難点はその「遅さ」
20分程度の動画でも5分ないし10分待たされることが多い。
サービスから見たら一時保存してるのだから当たり前といえば当たり前だが・・・。

しかし、Pytubeは爆速。２時間の動画をFullHDでDL（1.5GBくらい）しても数分で終わってしまう。
（もちろん回線環境に依存する）

---

# 注意点

## YouTubeの利用規約

Pytubeの内部動作が謎なので、言及は控えるが
**YouTubeはスクレイピング行為を禁止している**ことを知っておいてほしい。
もちろん、プログラミングによってYouTubeを操作すること自体が禁止されているわけではない。

* YotouTube公式が提供するAPIやサービスを使った操作はOK
* ブラウザやサーバーに対する直接的なデータ取得は禁止

YouTubeは公式から「YouTube Data API」というものを提供されていて、
「YouTubeにある情報はここを経由して取得してね」を用意してくれている。
API-keyももちろんあるので、誰がアクセスしたかを記録している。

> #### YouTube Data APIについて
> 
> YouTube Data APIについて少し触れておく。
> このAPIでは、動画情報やチャンネル情報の取得、動画に紐づく様々な操作を行うことができる。
> 
> * チャンネル登録者数を調べる
> * 動画投稿数を調べる
> * 視聴回数を調べる
> * 動画情報（タイトル/概要欄/URL）を調べる
> * 予定されている動画投稿を調べる
> * 動画に対する評価を調べる
> 
> 様々なことができる。
> 割愛するがそのほかにアップロード系の操作も可能で、YouTubeブラウザ的なものを作るのであれば必須の情報が手に入る。

## YouTubeがOKしてる訳ではない

Pytube自体は、公式のライブラリではないので「YouTube側がOKしている」というわけではない。
（詳細は割愛するが）動画データの著作権はYouTubeと製作者が所有している。
その為、ダウンロードできる動画の二次利用をどうするかも「動画ごとに異なる」のがYouTubeだ。

***技術的には（簡単に）実現可能だが、多用されるべきではない***

というのが、正しい解釈だと思う。

## ダウンロードした動画の二次利用と保存

著作権は非常にややこしいが、この記事を参考にしている人には以下の３つを参考にしてほしい。

1. ダウンロードした動画の保存先はローカルのみにする（プライベートなストレージ）
2. ダウンロードした動画の二次利用は著作権者がOKしている場合のみ行う
3. 二次利用した動画で収益化するならYouTubeで（収益化審査）

## 要するに

YouTubeの利用規約的にも、
著作権法的にも、**非常にグレーゾーンな行為**であることを認識しておいてほしい。



---

# コード全体

JupiterNotebookの人もインストールが必要

```python
pip install pytube
```

## 必要なライブラリ

```python
from pytube import YouTube
import os
import pandas as pd
import datetime
```

* Pytube
  * YouTubeメソッドのみ利用する
  * 記事のメイン
* os
  * ローカルフォルダの操作に利用する
* Pandas
  * 複数の動画URLをCSV形式で保存して読み込むため
  * 初学者はpandasをどんどん使うべきと（勝手に）考えているため
* datetime
  * フォルダを実行日単位で作成するため
  * 同じ名前の動画を回収し続ける人は特に必要

```python
today = datetime.datetime.now()
todayis = str(today.year) + "年" + format(today.month, '0>2') + "月" + format(today.day, '0>2') + "日"
videopath = 'hogehoge' ### 動画の保存先を指定
savepath = videopath +'\\'+ todayis

if not os.path.exists(savepath):
    os.mkdir(savepath)

def EasyVideoDL():
    try:
        videolist = '\\douga-list.csv'
        video_df = pd.read_csv(videolist, header=None)

        for url in video_df[0]:
            yt = YouTube(url)
            f = savepath + '\\' + yt.title
            yt.streams.get_highest_resolution().download(f)
            print(yt.title)
    except:
        pass

EasyVideoDL()

```

「try必要か？」とか
「関数にする意味」とかは許してほしい:weary:

それぞれについて説明？していく


---

## 保存先フォルダの用意とCSVファイルの読み込み

ここでは、保存先の準備と、動画URLが入ったCSVファイルを読み込んで利用可能な状態にする。
気になる人は、JupiterNotebookなんかで１行（ブロック）ずつ実行してみてほしい。

```python
today = datetime.datetime.now()
```
今日の日付をdatetime型で取得

```python
todayis = str(today.year) + "年" + format(today.month, '0>2') + "月" + format(today.day, '0>2') + "日"
```
取ってきた日付を文字列として「YYYY年MM月DD日」の形式へ
datetime型で取得した時点でformatすればいいけど、まぁわかりやすさ重視ってことで:weary:

```python
videopath = 'hogehoge'
savepath = videopath +'\\'+ todayis
```
動画の保存先の（親）ディレクトリを指定して（Windowsの人は「r」使ったほうが幸せ）
実際に保存される日付ごとのディレクトリ名を定義

```python
if not os.path.exists(savepath):
    os.mkdir(savepath)
```
日付のディレクトリを作成。すでにある場合は何もしないってことで。

```python
videolist = '\\douga-list.csv'
video_df = pd.read_csv(videolist, header=None)
```
CSVをpandasDataFrameへ格納。
格納するときにヘッダは削除する（今回はいらない）

> CSVの中身はこんな感じ
> 
> ```csv
> https://www.youtube.com/watch?v=Kp-eibuQpWg
> https://www.youtube.com/watch?v=JgT5vhDB7W8
> https://www.youtube.com/watch?v=kY-6R5fa0gw
> ```
> URL先にはなんの意図もありません。
> 長い動画を参考にしてるだけです（後述）



## Pytubeを回す

```python
for url in video_df[0]:
    yt = YouTube(url)
    f = savepath + '\\' + yt.title
    yt.streams.get_highest_resolution().download(f)
    print(yt.title)
```
1. `video_df`から値を取り出します。１列目だけなので`video_df[0]`を指定。取り出した値を`url`へ格納。
2. Pytubeの`YouTube`メソッドに`url`を渡して格納。
3. 保存するフォルダを`f`に定義。
4. （後述）
5. 進捗をコンソール上に表示（`.title`は動画のタイトルを取得）

> WindowsもMacも？「/」や「.」がフォルダに使えないのでその辺はご愛敬

```python
yt.streams.get_highest_resolution().download(f)
```
`yt`は`YouTube(url)`な訳ですが、
そこから`.streams`と続けることで動画のデータを取得できます。

今回は`.get_highest_resolution()`を使用しました。
これは「最も画質のいい音声付動画」を指します。

ただ、これが曲者で、Youtube動画の構成は以下のようになっています。
（実際はUPされる動画によって異なります）

| file | 映像 | 音声 | 形式 |
|:-:|:-:|:-:|:-:|
| 1 | 720p | AAC | MP4 |
| 2 | 1080p | なし | MP4 |
| 3 | 360p | AAC | MP4 |
| 4 | 360p | AAC | webm |
| 5 | なし | AAC | MP4 |
| 6 | なし | AAC | webm |

Youtubeの現在の仕様では、1080p以上の解像度の動画は「音声と分ける」運用になっているようで、1080pの動画を音声付きのMP4として直接ダウンロードすることはできません。
高画質1080p以上な動画をダウンロードする場合は、
一度、音声なし動画と音声のみ動画をDLして、合成して再生成する処理が必要になります。
（このあたりもWEBのあやしいサービスが苦戦する理由かもしれませんね）

なので、`.get_highest_resolution()`で取得できるのは`720p+AAC`の動画ってことになります。
`itag`というものを使って、この組み合わせを指定することもできるんですが、
itagの規則性が掴めなかったので、安定して同じ結果が得られるものを使用しました。

（楽曲だけ集めたい人は`.get_audio_only()`にすると`AAC-MP4`が対象になります）

んで

`.download()`で動画を保存します。引数には保存先のフォルダを指定します。
今回の場合は`f`ですね。

これをfor文で`video_df`の最後まで回せば、順番に動画を保存してくれます。
最後の`print`はあってもなくてもかまいません。
どの動画までDLが終わったかがわかるので表示させているだけです。

---

# 最後に

実行するとわかるんですが、youtubeで動画を見る時より速いんですよね。
VideoBrowserが全力を出すのは再生ヘッドの数秒前だけでいいと思うので、
**アプリやブラウザで見る行為よりyoutubeに負荷をかけていることがわかります。**

Google様がこんな程度で怒るとは思いませんが、
Google様に睨まれたくないので、ほどほどに利用します。

GoogleChromeの開発者ツールと睨めっこしたことがある人ならわかると思うんですが、
Youtubeの動画ってデータ的には複数に分かれていることがあるんですよね。
動画の長さなのか容量なのかは判らないんですが・・・:thinking:

それを考慮して１つのファイルとして書き出してくれるpytubeは優秀です。

私はYoutubePremiumに加入してるので「モバイルアプリで見る為に」とかそういう必要はないんですよね。
PCでもダウンロード視聴できたらいいな　と思う今日この頃でした。

年末年始の休みに考えるにはちょうどいい題材でした:relieved:

## Pytube公式
https://python-pytube.readthedocs.io/en/latest/