Youtubeの字幕をダウンロード(ブックマークレット)

youtubeの字幕ダウンローダーをブックマークレットで作ってみた コンピュータ関連
スポンサーリンク

Youtubeの字幕がダウンロードできたらいいなぁ・・・と考えた。

調べると方法は幾つかあるようです。
ブラウザのアドオン、ダウンロード機能があるアプリケーション、字幕がダウンロードできるwebサービス、そもそもyoutube上で「文字起こし」メニューから表示させてコピペで保存とか。

でももっとお手軽かつ簡単にできないの?

ということで
Youtubeの字幕がダウンロードできるブックマークレットを作ってみました。

【注意】仕様変更により、このままでは動作しません。(2016/03/20)
↓記事後方の修正版へ(Firefox,Chrome)

スポンサーリンク

Youtubeの字幕ダウンロードスクリプト

まずは本題から。

ブックマークレットの登録

新規にブックマークを作成し、下記コードをコピー(下記修正版へ)、URLの欄に保存してお使いください。
名前は適当に「YouTube – 【字幕DL】」などお好きにどうぞ。

javascript:(function(){var a=new XMLHttpRequest(),b=location.href.match(/\?v=([-\w]{11})/)[1];a.onreadystatechange=function(){if(a.readyState==4&&a.status==200){var f=a.responseXML.documentElement.getElementsByTagName("track");if(f.length){var g=document.createElement("div");g.style.cssText="padding:10px;background:white;border:1px solid #aaa;";g.onclick=function(){g.parentNode.removeChild(g);};for(var e=0;e<f.length;e++){var d=document.createElement("button");d.type="button";d.innerHTML=f[e].getAttribute("lang_original");d.style.cssText="margin:3px;";d.value=f[e].getAttribute("lang_code");d.onclick=c;g.appendChild(d);}document.getElementById("watch-header").appendChild(g);}else{alert("no subtitle!");}}function c(h){var i=document.createElement("a");i.download="RenameMe.srt";i.href="https://www.youtube.com/api/timedtext?fmt=srt&lang="+h.target.value+"&v="+b;g.appendChild(i).click();h.stopPropagation();}};a.open("GET","https://www.youtube.com/api/timedtext?type=list&v="+b);a.send(null);})();

使い方

字幕をダウンロードしたいページを開き、ブックマークメニューから当該ブックマークレットを選ぶと、評価ボタンの下あたりに字幕の一覧が出ます。

字幕の一覧ボタン

ダウンロードしたい言語を選んでクリックすれば、ダウンロードが始まります。
(ウインドウに直接表示される場合は「別名で保存」などしてください。)
字幕はsrt形式です。
保存されたファイルは必要に応じてリネームしてください。

尚、あくまでも自分が使う為に書いたので、youtubeの仕様変更や何やらで将来的には動かなくなる可能性もあります。
検証もmacのFirefox 43、chrome 47、safari 9でしかしていません。(2015/12/23現在)
IEなどでは動かない可能性もありますので、あしからず。
(※お約束ですがご利用は自己責任で。改変OK)

コードの解説

つたない文章と心もとない技術力ですが、簡単に説明してみます。
あ、怪しいことなんてしてないんだからね!・・・という言い訳みたいなもんです。

スポンサーリンク

圧縮前のソースコード:

javascript:
(function(){
	var xhr = new XMLHttpRequest(), id = location.href.match(/\?v=([-\w]{11})/)[1];
	xhr.onreadystatechange = function(){
		if(xhr.readyState==4 && xhr.status==200){
			var tr = xhr.responseXML.documentElement.getElementsByTagName('track');
			if(tr.length){
				var sublst = document.createElement('div');
				sublst.style.cssText = 'padding:10px;background:white;border:1px solid #aaa;';
				sublst.onclick = function(){sublst.parentNode.removeChild(sublst);};
				for(var i = 0; i < tr.length; i++) {
					var bt = document.createElement('button');
					bt.type = 'button';
					bt.innerHTML = tr[i].getAttribute('lang_original');
					bt.style.cssText = 'margin:3px;';
					bt.value = tr[i].getAttribute('lang_code');
					bt.onclick = mySelect;
					sublst.appendChild(bt);
				}
				document.getElementById("watch-header").appendChild(sublst);
			}else{
				alert("no subtitle!");
			}
		}
		function mySelect(tmp){
			var aa = document.createElement('a');
			aa.download = 'RenameMe.srt';
			aa.href = 'https://www.youtube.com/api/timedtext?fmt=srt&lang='+tmp.target.value+'&v='+id;
			sublst.appendChild(aa).click();
			tmp.stopPropagation();
		}
	};
	xhr.open('GET','https://www.youtube.com/api/timedtext?type=list&v='+id);
	xhr.send(null);
})();

多分もっと短く書くことも可能だと思いますが、自分の技術力ではこの辺がせいぜい。
とりあえず動けば無問題。

【追記:12/26】
字幕を選ばずに閉じる事を実装していなかったのでちょろっと修正。

最初にサーバーから字幕リストを取得

まずURLから11桁のIDを取得。

var xhr = new XMLHttpRequest(), id = location.href.match(/\?v=([-\w]{11})/)[1];

そのIDをパラメータに入れて、

https://www.youtube.com/api/timedtext?type=list&v=取得したID

にアクセスすると、その動画に対する字幕のリストが取得できます。

字幕リストxml

こんな感じ。

取得した字幕リストからボタンを生成

表示するエリア(div)を作って、その子要素としてリストの数だけボタンを生成。
出来たら適当な場所にappendChildします。(この場合は”#watch-header”という場所あたりに。)

var sublst = document.createElement('div');
sublst.style.cssText = 'padding:10px;background:white;border:1px solid #aaa;';
sublst.onclick = function(){sublst.parentNode.removeChild(sublst);};
for(var i = 0; i < tr.length; i++) {
 var bt = document.createElement('button');
 bt.type = 'button';
 bt.innerHTML = tr[i].getAttribute('lang_original');
 bt.style.cssText = 'margin:3px;';
 bt.value = tr[i].getAttribute('lang_code');
 bt.onclick = mySelect;
 sublst.appendChild(bt);
}
document.getElementById("watch-header").appendChild(sublst);

字幕データをダウンロードして保存

ボタンがクリックされたら<a>タグを生成。

リンク先パラメータに id や、ダウンロードする言語を lang で渡します。(字幕リストのlang_codeの値)
fmtはフォーマットで、この場合はsrt。

aa.href = 'https://www.youtube.com/api/timedtext?fmt=srt&lang='+tmp.target.value+'&v='+id;

appendChildして自動的にクリックさせたら、ダウンロードが始まるはず。

ちなみに27行目を document.title に変更すれば ページのタイトル.srt で保存できます。

aa.download = 'RenameMe.srt';

 ↓

aa.download = document.title+'.srt';

修正版

【追記(2016/02/05)】
記事を書いてから1ヶ月程度で、早速404エラーになってしまうのを発見。
フォーマットでsrtを指定しても「そんなのないよ!(プンスカ)」・・・と言われちゃう。
仕様が変わったようです。

一応自分用に修正したものを掲載しておきますが、MacのFirefox 43,Chrome 47でのみ動作確認。safariは動かないけど使ってないからもはや無視。きっとIEでも動かないと思いますのであしからず。
ちなみにやっている事は途中まで同じ。srt形式で落とせないなら、srv2と言うので取得できる字幕xmlをそのままjs内でsrt形式に変換&保存できるようにしただけ。
コードはより醜くなりましたが、一応動作します。しばらく様子見。

スポンサーリンク

【追記(2016/03/20)】
新しい動画の一部でまたDL出来なくなったので、再度修正。
字幕のname属性をパラメータとして渡さなければダメになったようだ。
ちょっと前の動画なんかは無くても保存できるのだけれど。
更に取得する字幕のフォーマットを srv2 から srv3 へ変更。
今回変更したのはその2点。後は基本的に同じ。

【追記(2017/01/12)】
どうも試験的に新しいデザインも使っているようで、アクセスするごとに画面のデザインが変わったりする。
新しい方だとタグの名前や構造自体が変わっているのでうまく表示できない。
ただ字幕自体の取得方法は変わっていないので、生成したタグを挿入する場所が特定できないだけ。
ということでとりあえずの修正版を掲載。
勿論今後もタグの名前が変わるかもしれないので様子見。
(Firefox 50.1.0とChrome 55のみで動作確認)
あ、あとミリセカンドがカンマなのにピリオドになっちゃってたのを直しました。

javascript:(function(){var a=new XMLHttpRequest(),b=location.href.match(/\?v=([-\w]{11})/)[1];a.onreadystatechange=function(){if(a.readyState==4&&a.status==200){var g=a.responseXML.documentElement.getElementsByTagName("track");if(g.length){var h=document.createElement("div"),f,e;h.style.cssText="padding:10px;background:white;border:1px solid #aaa;";h.onclick=function(){h.parentNode.removeChild(h);};for(f=0;f<g.length;f++){e=document.createElement("button");e.type="button";e.innerHTML=g[f].getAttribute("lang_original");e.style.cssText="margin:3px;";e.value=g[f].getAttribute("lang_code");e.dataset.nm=g[f].getAttribute("name");e.onclick=d;h.appendChild(e);}var c=document.getElementsByTagName("ytd-video-primary-info-renderer");if(c[0]==null){document.getElementById("watch-header").appendChild(h);}else{c[0].appendChild(h);}}else{alert("no subtitle!");}}function d(j){j.stopPropagation();a.onreadystatechange=function(){if(a.readyState==4&&a.status==200){var k="",n,m=a.responseXML.documentElement.getElementsByTagName("p");for(n=0;n<m.length;n++){k+=n+1+"\n";k+=i(Number(m[n].getAttribute("t")))+" --> "+i(Number(m[n].getAttribute("t"))+Number(m[n].getAttribute("d")))+"\n";k+=m[n].innerHTML+"\n\n";}var l=new Blob([k],{type:"text/plain"}),o;o=document.createElement("a");o.href=URL.createObjectURL(l);o.target="_blank";o.download=document.title+".srt";h.appendChild(o).click();URL.revokeObjectURL(l);}};function i(n){var p,k,o,l;p=String(Math.floor(n/3600000)+100).substring(1);k=String(Math.floor((n-p*3600000)/60000)+100).substring(1);o=String(Math.floor((n-p*3600000-k*60000)/1000)+100).substring(1);l=String(n+1000).slice(-3);return p+":"+k+":"+o+","+l;}a.open("GET","https://www.youtube.com/api/timedtext?fmt=srv3&lang="+j.target.value+"&name="+encodeURIComponent(j.target.dataset.nm)+"&v="+b);a.send(null);}};a.open("GET","https://www.youtube.com/api/timedtext?type=list&v="+b);a.send(null);})();

まとめ

実はURLをコピペしてダウンロードできるWEBサービスを使っていたのですが、思いのほか面倒くさいのです。
アドレス欄からコピーして新規ウインドウでサイトを開いて貼付けて・・・とか。
いちいちアドオンをインストールするのも・・・なんて思ったので、今回作ってみました。

「○○の環境じゃ動かないよ!」とか「○○のブラウザじゃうまくいかない!」なんて場合は是非ご自分で修正してみてください。
(※ヒントは「blob」というオブジェクト。IEとかsafariとかは微妙に記述の仕方が違うので。)
あくまでもこれは個人的に利用する為だけに作ったものなのであしからず。

それでは今回は以上!

コメント

タイトルとURLをコピーしました