ニコニコから動画を落とすスクリプト改
ニコニコ動画ダウンローダ - odz bufferを参考にして、以前作ったニコニコから動画を落とすスクリプトを少しばかり修正しました。
- デフォルトでCookieを"nico-cookie.txt"から読み込む
- -cオプションでブラウザ(Mozilla系)のCookieファイルを指定
- ログイン処理を行わないので、ブラウザの方でログインしてから使用
- スクリプト使用後もブラウザの方のログイン状態を維持
- プログレスバーにhttp://pypi.python.org/pypi/progressbarを使用
関数を使ってまとめたので、前よりは見やすくなったと思います。複数の動画を落とせるようにしようと思ったのですが、ブラウザからアクセスした時に「短時間での連続アクセスはご遠慮ください」と言われたので止めておきました。後、22時から2時ぐらいまでダウンロードの制限がかかっているようです。FLVファイルを落とす際にhttplib.BadStatusLineという例外が発生するので調べてみたら、それが原因でした。
#!python # encoding=utf-8 import os import sys import re import urllib import urllib2 import cookielib import httplib import cgi from optparse import OptionParser from progressbar import * # Constants for niconico VIDEO_URL_FORMAT = "http://www.nicovideo.jp/watch/{video_id}" LOGIN_URL = "https://secure.nicovideo.jp/secure/login" API_URL = "http://www.nicovideo.jp/api/getflv?v=" VIDEO_URL_RE = re.compile(r"http://www.nicovideo.jp/watch/(sm\d+)$") VIDEO_TITLE_RE = re.compile(r'<h1><a class="video" [^<>]*>(.*)</a></h1>') # Constants for progress bar PBAR_WIDGETS = [Percentage(), " ", Bar(marker="=",left="[",right="]"), " ", ETA(), " ", FileTransferSpeed()] COOKIE_FILE = "nico-cookie.txt" class LoginError(Exception): pass def login(mail, password): for i in range(2): if mail is None: mail = raw_input("mail: ") if password is None: password = raw_input("pass: ") query = {"mail":mail, "password":password} query = urllib.urlencode(query) try: response = urllib2.urlopen(LOGIN_URL, query) if response.headers["x-niconico-authflag"] == "1": return True else: mail = password = None except urllib2.URLError: pass return False def get_flv_url(video_id): response = urllib2.urlopen(API_URL + video_id) try: content = response.read() flv_url = cgi.parse_qs(content)["url"][0] return flv_url except KeyError: if content == "closed=1&done=true": raise LoginError else: raise ValueError def get_video_title(video_url): response = urllib2.urlopen(video_url) match = VIDEO_TITLE_RE.search(response.read()) if match: video_title = match.group(1).decode("utf-8", "ignore") video_title = re.compile(u'[\/:*?"<>|]').sub(" ", video_title) return video_title def download(flv_url, filepath): try: file = None response = urllib2.urlopen(flv_url) try: file = open(filepath, "wb") except IOError: raise RuntimeError("unable to open the file for writing") total = response.headers["content-length"] downloaded = 0 pbar = ProgressBar(widgets=PBAR_WIDGETS, maxval=int(total)).start() while True: data = response.read(4096) if not data: break file.write(data) downloaded += len(data) pbar.update(downloaded) pbar.finish() except (urllib2.URLError, IOError): raise RuntimeError("unable to download video data") finally: if file: file.close() # Create the command line options parser and parse command line usage = "usage: %prog [options] http://www.nicovideo.jp/watch/{video_id}" parser = OptionParser(usage) parser.add_option("-m", dest="mail", help="specify the email address") parser.add_option("-p", dest="password", metavar="PASS", help="specify the password") parser.add_option("-c", dest="cookie_file", metavar="FILE", help="load cookies from FILE of the Mozilla/Netscape 'cookie.txt' format"), parser.add_option("-d", dest="save_dir", metavar="PREFIX", help="save a file to PREFIX/ (default current directory)") parser.add_option("-n", dest="no_title", action="store_true", help="save the file under the name {ID}.flv (default {title}.flv)") (options, args) = parser.parse_args() # Check arguments if not args: parser.print_help() sys.exit(1) video_url = args[0] match = VIDEO_URL_RE.match(video_url) if match is None: sys.exit("Error: video url not in this format: %s" % VIDEO_URL_FORMAT) video_id = match.group(1) # Check whether the directory specified by -d option exists if options.save_dir: if not os.path.isdir(options.save_dir): sys.exit("Error: %s is not a directory" % options.save_dir) # Configure urllib2 to use cookies cj = cookielib.MozillaCookieJar() try: cj.load(options.cookie_file or COOKIE_FILE) except: pass opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj)) urllib2.install_opener(opener) # Prepare downloading video try: try: flv_url = get_flv_url(video_id) except ValueError, e: sys.exit("Error: invalid video ID") except LoginError: # Exit if use the browser's cookies file if options.cookie_file: sys.exit("Error: not logged in") # Login and retry if login(options.mail, options.password): cj.save(COOKIE_FILE) flv_url = get_flv_url(video_id) else: sys.exit("Error: unable to login") video_title = get_video_title(video_url) except urllib2.URLError, e: sys.exit("Error: unable to prepare downloading a video") # Download video filename = video_title or video_id if options.no_title: filename = video_id filepath = os.path.join(options.save_dir or ".", filename + ".flv") print video_title try: download(flv_url, filepath) except (IOError, RuntimeError), e: sys.exit("Error: %s" % e) except httplib.BadStatusLine: sys.exit("Error: download seems to be limited")