0
2

More than 3 years have passed since last update.

youtube-dlのextractor書き方講座 4/4

Posted at

遅くなってごめんなさい

卒論やら何やらで忙しく、書こう書こうと思ってたらもう2月も半分を過ぎました。ウソだろお前

前回の続き

また見てない方は前回はここからどうぞ。
前回は最低限以上の情報の取得を書きました。
今回は、youtube-dlのコーディング規則を簡単にやりたいと思います。

コーディング規則って何があるの?

具体的には、README.mdのyoutube-dl coding conventionsのセクションに載っています。

Mandatory and optional metafields

_real_extractで返す情報には次のキーと、対応する情報が必要だよ。という意味です。

  • id (動画ID)
  • title (動画タイトル)
  • url または formats (今回のシリーズは後者を使用)

逆に、それ以外は全て任意です。ダウンロード自体はできます。

Provide fallbacks

ウェブサイトには仕様変更が付きものです。これに備えるためにも、いくつかの方法で同時に情報を取得し、仕様変更にある程度耐えられるようにしなければなりません。
例えば、OpenGraphやTwitter Card向けの情報から取り出すのを最終手段として用意しておくのが良いと思います。

# JSONをダウンロード
meta = self._download_json(url, video_id)

# これはNG ('meta'キーがJSONから消えたらKeyErrorになる)
title = meta['title']

# これはOK ('meta'キーがJSONから消えてもOpenGraphのog:titleから取得できる)
title = meta.get('title') or self._og_search_title(webpage)

# Twitter Card向けのmetaタグを先に検索し、出来なければOpenGraphのog:titleを探す
title = self._html_search_meta(
        ('twitter:title', 'og:title'),
        webpage)

Regular expressions

これは第1巻で簡単に扱いました。

まず、大前提として、不要なグループはキャプチャしないようにします。
ここでは、XMLのattributeという属性を取り出す正規表現を考えます。

# NG ("attribute"はどう見ても定数なのでキャプチャする意味はない)
r'(attribute)="(.+?)"'

# 場合によってはOK
# 不特定多数のXMLやHTMLに対して使うのであれば、問題無い
r'attribute=([\'"]?)(.+?)\1'

# OK
# ウェブサイトが引用符や二重引用符でしか囲まないと分かっているのであれば、そのグループは削除するべき
r'attribute="(.+?)"'
r"attribute='(.+?)'"

また、正規表現は出来る限り柔軟にするべきです。
ここで、FC2動画のテストケースにあったこのURLを考えます。

FC2動画では、言語や設定などを考慮すると、次のようなURLが理論上存在し得ます。

# 最短
http://video.fc2.com/content/20121103kUan1KHs

# アダルト
http://video.fc2.com/a/content/20121103kUan1KHs

# 言語付き
http://video.fc2.com/en/content/20121103kUan1KHs
http://video.fc2.com/ja/content/20121103kUan1KHs

# 言語付き+アダルト
http://video.fc2.com/en/a/content/20121103kUan1KHs
http://video.fc2.com/ja/a/content/20121103kUan1KHs

さて、このURLに全てマッチし、同時にIDを正しく取り出せる正規表現はどうなるでしょうか?

愚直に考えれば、次のような正規表現になるでしょう。

r'^https?://video\.fc2\.com/(?:a/)?(?:(?:en|ja)/)?content/(?P<id>[^/]+)'

でも、例えば上記言語付き+アダルトのURLの一つ http://video.fc2.com/ja/a/content/20121103kUan1KHsjaaを入れ替えても問題なく動画を見ることが出来ます。

では、こうでしょうか?

r'^https?://video\.fc2\.com/(?:a/)?(?:(?:en|ja)/)?(?:a/)?content/(?P<id>[^/]+)'

すごく複雑になってしまいました。

ここで、何が本当に必要とされているかを考えてみましょう。

最初のhttps://video.fc2.com/は必須として、/content/以降のIDパートも必要です。それだけを取り出そうとすると、次のような正規表現になります。

r'^https?://video\.fc2\.com/(?:[^/]+/)*content/(?P<id>[^/]+)'

これであれば、先程の様々なパターンのURLに対応することができます。

このように、正規表現は可能な限り柔軟に作ることが重要です。

Long lines policy

1行当たりの長さはできるだけ80文字以内に収める、というものです。
ただし、強制ではないので、長い文字列を分割してまでやる必要はありません。

Inline values

一回しか使わない定数は、特に理由がない限り1はコード内に入れるようにします。
頻繁に使うものだけ定数として取り出しておくのが最も良いです。

Collapse fallbacks

Provide fallbacksで扱った次のコードを考えます。

# OK: 綺麗なコード
title = self._html_search_meta(
        ('twitter:title', 'og:title'), # このtupleを変えるだけ
        webpage)

# NG: 同じようなコードを複数行書く
title = (
    self._html_search_meta('twitter:title', webpage) or 
    self._og_search_description('title', webpage))
    # ...行を増やす

上記のコードは実質的に同じですが、取り出すmetaタグの数が増減するに従って、OKのコードはどのmetaタグを取り出すか一目で分かりますが、NGの方ではあまりに多くなると見づらくなることが予想されます。
よって、行数が比較的少なくなるOKのコードにするべきです。

Trailing parentheses

閉じ括弧だけの行が(文字列リテラルの一部である場合を除いて)無いようにし、前の行の末尾にまとめます。

# OK: 閉じ括弧だけの行が無い
title = (
    self._html_search_meta('twitter:title', webpage) or 
    self._og_search_description('title', webpage))

# NG: 閉じ括弧だけの行がある
title = (
    self._html_search_meta('twitter:title', webpage) or 
    self._og_search_description('title', webpage)
)

Use convenience conversion and parsing functions

youtube_dl/utils.pyには便利なメソッドがたくさんあります。extractorからならfrom ..utils import hogehogeでimportできます。

おわりに

今回は、youtube-dlのコーディング規則を簡単にやりました。
これで、多分最低限はやったと思うので、extractorを書いてみたい方は参考にしてくれれば幸いです。
もし何かあったら追加があるかもしれないですが。

最後までご覧いただきありがとうございました。


  1. 例えば、'''aa'''を用いて複数行かつ長い文字列を作成するなど、やむを得ない場合は除く 

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2