コマンドライン上で動作するニコニコ動画ダウンローダー on Python
ニコニコ動画の.flvファイルをダウンロードするPythonスクリプトです.
引数としてsm??????のようなIDとhttp://www.nicovideo.jp/watch/sm?????? のようなURLの両方を受け付けます.また,複数の引数を受け取ると順次ダウンロードします.
GreaseMonkeyスクリプトなどで同様の目的を達成するものは既にいくつかありますが,サイトの仕様変更のためか,うまく動作しないことが多いので自作してみました.
標準のモジュール(ライブラリ)のみを使っているので,多くの環境で動作するはずです.
その他の特長として,プログレスバーで進捗を表示したり,ページのタイトルをチェックして適切なファイル名をつけたりするといったものもあります.
個人的にはPyhtonでのBasic認証やCookie制御の要領が分かったことと,自分で処理に時間がかかるプログラムを書いたときに,ProgressBarクラスを使って完了までの時間が予測出来るようになったことが一番の収穫だったりします.
#!/usr/bin/env python # -*- encoding: utf-8 -*- import cgi import cookielib import urllib import urllib2 import re import sys import time username = "" password = "" bufsize = 524288 class ProgressBar: format = "%s[%s%s] %d/%d ETA %s %dKB/sec\r" def __init__(self, _max, name="", size=24): self.max = _max self.cur = 0 self.name = name and name+" " or name self.size = size self.num_progress = 0 self.out = sys.stdout def start(self): self.progress(0) def progress(self, delta): if self.num_progress == 0: self.start = time.time() self.num_progress += 1 self.cur += delta cursize = int(float(self.cur) / self.max * self.size) leftsize = self.size - cursize time_consume = time.time()-self.start _eta = self.num_progress >= 2 and \ ( time_consume / (float(self.cur)/self.max) - time_consume) or -1 eta = _eta >= 0 and "%02d:%02d" % divmod(_eta, 60) or "--:--" kbs = self.cur / 1024 / time_consume self.out.write(self.format % \ (self.name, '*'*cursize, '-'*leftsize, self.cur, self.max, eta, kbs)) if self.cur >= self.max: self.out.write("\r\n") self.out.flush() def login(username, password): cj = cookielib.CookieJar() opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj)) req = urllib2.Request("https://secure.nicovideo.jp/secure/login?site=niconico"); account = {"mail": username, "password": password} req.add_data(urllib.urlencode(account.items())) opener.open(req) return opener def get_video(url_or_video_id, opener): video_id = re.match("^.*(sm.*)$", url_or_video_id).group(1) for line in opener.open("http://www.nicovideo.jp/watch/" + video_id): if line.find("<title>") > -1: title = re.match("^s*<title>.+?‐(.+?)</title>\s*$", line).group(1) break res = opener.open("http://www.nicovideo.jp/api/getflv.php?v=" + video_id).read() url = cgi.parse_qs(res)["url"][0] source = opener.open(url) length = int(source.headers["content-length"]) filename = "%s-%s.flv" % (video_id, title) dest = file(filename, "wb", bufsize) bar = ProgressBar(length) bar.start() while dest.tell() < length: dest.write(source.read(bufsize)) bar.progress(bufsize) def main(): if username == "" or password == "": print "error: please specify your account info in", sys.argv[0] sys.exit(1) if len(sys.argv) < 2: print "usage: %s url_or_video_id [url_or_video_ids]" % sys.argv[0] sys.exit(1) args = sys.argv[1:] for i, url_or_video_id in enumerate(args): sys.stdout.write("%s/%s " % (i+1, len(args))) session = login(username, password) get_video(url_or_video_id, session) if __name__ == '__main__': main()
PythonでBasic認証やCookie制御をサポートするアプローチは,PerlともRubyとも違っていて面白いなぁと思いました.どうやらデザインパターンでいうChain of Responcibilityのようですね.
cj = cookielib.CookieJar() opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj)) f = opener.open("http://example.com/") # Cookieを受信した場合適切に処理する