芋の独り言

当ブログへのアクセスは当ブログのプライバシーポリシーに同意したものとみなします.

YouTubeダウンローダ― with Python(現在はダウンロードできません...)

kusoimox.hatenablog.jp
以前うpした記事で,pytubeYouTubeの動画がダウンロードできる~とい言ったんですが,最近久しぶりに使うと, itagは取れるんだけど,動画のダウンロードでエラーが出て,使えなくなちゃった...
そこで,調べると,

っとあったんですが,原因が少し違うような...よう分りまへんわ~
っということで,それならスクリプト自作してしまえってことになり,以下のようになりました. スクリプト構成の土台はpytubeですけどね.

# -*- coding: utf-8 -*-
# 動画
movie = [5,6,17,18,22,34,35,37,38,43,44,45,46,59,78,82,83,84,85,91,92,93,94,
    95,96,100,101,102,132,151]
# 動画像のみ
video = [13,36,133,134,135,136,137,138,160,167,168,169,170,212,218,219,242,
    243,244,245,246,247,248,264,266,271,272,278,298,299,302,303,308,313,
    315,330,331,332,333,334,335,336,337]
# 音声のみ
audio =[139,140,141,171,172,249,250,251]


from urllib.parse import unquote
from time import sleep
import re,sys,requests,ast,wx,os

class youtube:
    
    def save(self):
        app=wx.App() # アプリケーションのオプジェクトを生成
        wx.MessageBox('保存先フォルダを選択してください','フォルダ選択',wx.STAY_ON_TOP)
        dialog = wx.DirDialog(None, message='ファイルを選択してください',style=wx.DD_CHANGE_DIR | wx.OK | wx.STAY_ON_TOP)
        # ファイルが選択されたとき
        if dialog.ShowModal() == wx.ID_OK:
        # 選択したファイルパスを取得する
            self.path = dialog.GetPath()
            dialog.Destroy()
    
    def download(self,itag_no):
        itag_no = int(itag_no)
        for i in self.itag:
            if i['itag'] == itag_no:
                dl_url = i['url']
                break
            
        title = re.sub(r'[\\/:*?"<>|]+','',self.videoDetails['title'].replace(' ',''))
        if (itag_no in movie) or (itag_no in video):
            kakuchoshi = '.mp4'
        elif itag_no in audio:
            kakuchoshi = '.wmv'
        title = title + kakuchoshi

        path = os.path.join(self.path,title)

        sys.stderr.write('downloading...\n')
        data = requests.get(dl_url).content
        with open(title,mode='wb') as f:
            f.write(data)
            
        sys.stderr.write('download finish!\n')

    def video_info_url(self,url):
        video_id  = re.search(r'(?:v=|\/)([0-9A-Za-z_-]{11}).*', url).groups(1)[0]
        if video_id == None:
            return False
        watch_url = 'https://youtube.com/watch?v=' + video_id
        watch_html = requests.get(watch_url).content.decode()
        eurl = 'https://youtube.googleapis.com/v/{}'.format(video_id)
        embed_html = 'https://www.youtube.com/embed/{}'.format(video_id)

        params = {
            'video_id':video_id,
            'el':'detailpage',
            'ps':'default',
            'hl':'en_US'
        }
        
        rurl = requests.get('https://youtube.com/get_video_info',params=params).url
        res = unquote(requests.get(rurl+'?eurl={}'.format(eurl)).text)

        self.itag=[]
        info = re.search(r'player_response=({.+})&',res)
        if info != None:
            info = info.groups(1)[0]
            info = info.strip()
            info = info.replace('"',"'")
            info = info.replace('true','True')
            info = info.replace('false','False')
            info = info.replace('null','None')
            try:
                video_info = ast.literal_eval(info)
            except SyntaxError:
                return False

            del info
        else:
            return False
            
        streamingData = video_info['streamingData']
        self.itag += streamingData['formats']
        self.itag += streamingData['adaptiveFormats']
        self.playbackTracking = video_info['playbackTracking']
        self.videoDetails = video_info['videoDetails']

        return True
    
    def __init__(self,url):
        self.save()

        sys.stderr.write('info getting...\n')
        while True:
            try:
                res = self.video_info_url(url)
            except requests.exceptions.SSLError:
                continue
            except requests.exceptions.ConnectionError:
                continue
            if res:
                break
            sleep(60)

        for i in self.itag:
            if i['itag'] in movie:
                sys.stderr.write('動画    ')
            elif i['itag'] in video:
                sys.stderr.write('動画像のみ ')
            elif i['itag'] in audio:
                sys.stderr.write('音声のみ  ')
            else:
                sys.stderr.write('None ')
            sys.stderr.write('itag {0}:[type:{1} bitrate:{2} quality:{3}]\n'.format(
                i['itag'],i['mimeType'].split('=')[-1],i['bitrate'],i['quality']
                ))

if __name__ == '__main__':
    url = input('url:')
    yt = youtube(url)
    yt.download(input('choice itag number:'))

https://www.youtube.com/watch?v=動画ID&feature=youtu.be”みたいなURLが対象です.基本はこの形のURLで. え~と,”https://youtu.be/動画ID”でもイケましたね.共有からURLをコピーしたときはこの形ですからね. 通常はitagを22,なければ18を選択すれば,動画がダウンロードできます.

参考

pytubePython参考書

2020/8/30追記:ダウンロードできません

'https://youtube.com/get_video_info'へのアクセスができません... ブラウザで人手で行えばアクセスしてデータを得られるのですが, スクリプトから自動的にアクセスしようとすると”HTTP Status Code:429”が出ます. 以上のリンクへのアクセスだけでなく,ログインもスクリプトからはできない模様.

アクセスのし過ぎというより,YouTube自体がスクリプトによる自動アクセスを拒絶しているような気がします. どうしてもやりたい場合はサービスに登録してGoogleAPIを使うしかないと思われます.

色々スクリプト書き直したんですが,”HTTP Status Code:429”でアクセスできなくなりお手上げです...