golangの日記

Go言語を中心にプログラミングについてのブログ

Youtube-DLがどのように動画URLを抽出しているのかソースコードを読んでみた

default-gray.png


Youtube-DL



YouTube動画ダウンローダーのリポジトリをGitHubが削除、音楽業界団体からの要請に基づき」とのことで、GitHubのyoutube-dlリポジトリには利用できませんとのメッセージが表示してある。どうやらバックアップとしてGitLabにリポジトリを作成して開発は継続しているみたい。

サポートサイト一覧を見るとすごい数のサイトに対応していてYoutubeやニコニコ動画はもちろん新日本プロレスワールド(謎)まである。

ソースコードを眺めてみるとextractor以下に、それぞれのサイトに対応するスクリプトがあるっぽい。





ということで、一番気になったPornHubの動画URLはどのように抽出しているのか見てみた。


結論から書くと PornHub はインラインJavaScriptの変数に、動画URLの断片が入っていて、その変数を連結することでクオリティに応じたURLを生成している。PornHub用のエクストラクターは、その変数を見つけて動画URLを取得している。



pornhub.py を見ると、最初に出てくる PornHubBaseIE クラスは同階層の common.py にある InfoExtractor というクラスを継承している。InfoExtractor はエクストラクターを作成するためのミドルウェアっぽい。

class PornHubBaseIE(InfoExtractor):


次に上述した PornHubBaseIE を継承した PornHubIE クラスの _real_extract 関数を見ると var flashvars = という文字列をウェブページ内から探し出している。

flashvars = self._parse_json(
            self._search_regex(
                r'var\s+flashvars_\d+\s*=\s*({.+?});', webpage, 'flashvars', default='{}'),
            video_id)


この flashvars はインラインのJavaScript内にある変数名で、その値はタイトルやサムネなど、メタデータのオブジェクトになっている。以下でそのインラインスクリプトをコンソールログから参照できる。

document.querySelector('#player > script');


インラインスクリプトにはメタデータの他に断片化されたURLを値として持つ変数があり extract_js_vars 関数で quality_ などから始まる変数を探し出し、その値を使って parse_js_value で繋合わせてるっぽい。

js_vars = extract_js_vars(
    webpage, r'(var\s+(?:%s)_.+)' % '|'.join(FORMAT_PREFIXES),
    default=None)


以上がYoutube-DLでPornHubの動画URLを抽出している方法で、他のサイトでは全く異なるんだと思う。サポートサイト一覧を見てわかるようにこれだけ多くのサイトに対応するにはオープンソースで有志の協力が不可欠なんだろうなと思った。 有志の側もHTMLの解析なんかは8946 | ハッキングチャレンジサイト(現在は可動していないけど、簡単なハッキング体験ができるサイトだった)のように宝探しみたいな楽しさがあるのかもしれない。



途中で気がついたんだけど quality_1080p とか完成したURLが入ってる変数をブラウザのコンソールログから見ると動画のURLが出てくる。

console.log(quality_1080p); 

ということはPhantomJSでJavaScript注入できるのであればメタデータや動画URLを正規表現使わずに簡単に取れそう