Youtubeの動画playlistのリンク集を,Firebugワンライナーで生成しよう(consoleからXPATHでスクレイピング)
Firebugのコンソールを使って,Webページの情報を加工してみよう。
- HTMLの中から,特定のclassを持ったDOM要素だけを抽出
という処理をしてみる。
こういう単一ページ内でのWebスクレイピングは,簡易なブックマークレットでも可能。
だが,もしfirefoxを使っているなら,デバッグ用コンソールを利用すると一層便利だ。コンソール用のAPIメソッドを使えるから。
XPathの勉強だと思って,やり方を覚えてみよう。
題材となるプレイリストのページ
Webページの題材としては,「Youtubeのプレイリスト一覧画面」をネタにする。
このページ内から,全ての動画への個別のハイパーリンクを抜き出して,加工したい。
例えば,こんなプレイリストがある:
YakuSu ウクライナ語講座 さんのアップロード動画 - YouTube
http://www.youtube.com/playlist?list=...
これはウクライナ語の入門ビデオのリストだ。
個々のビデオのURLをリストアップして,番号付きの箇条書きにして,ブログに貼り付けたいとする。
つまり,「ムービーのリンク集を一発で生成したい」のだ。
手作業でいちいち右クリックしてURLをコピーするのは,数が増えるとめんどい。
あなたなら,どういう方法を使うか?
Firebugのコンソールを使おう
下記のコードを,Firebugのコンソールにペーストしてみよう。
1行だけで済む。
console.clear(); $x('//a[contains(concat(" ",normalize-space(@class)," "), " pl-video-title-link ")]').forEach(function(elem, i){ console.log((i+1)+". "+elem.innerHTML.replace(/^\s+/,"").replace(/\s+$/,"")+"\n"+elem.href+"\n\n\n");}); "";
Operaの開発者コンソールで実行(2020年版):
// 動画URLの配列 var yt_vid_links = []; // 動画URLをスキャン $x('//a[contains(concat(" ",normalize-space(@class)," "), " ytd-playlist-video-renderer ")]') // XPathでタグを絞り込み .forEach(function(elem, i){ // 全要素をループ // 動画のURLを取得 var yt_video_link = elem.href; // URLをplaylistから外す yt_video_link = yt_video_link.replace( /&list=.+/, "" ); // URL保管 yt_vid_links.push( yt_video_link ); console.log(yt_video_link); }); // 動画タイトルの配列 var yt_vid_titles = []; // 動画タイトルをスキャン $x('//a[contains(concat(" ",normalize-space(@class)," "), " ytd-playlist-video-renderer ")]') // XPathでタグを絞り込み .forEach(function(elem, i){ // 全要素をループ // 動画のタイトルを取得 var yt_video_title = elem.innerHTML; // タイトル整形 yt_video_title = yt_video_title .replace( /^\r?\n? */, "" ) .replace( /\r?\n? *$/, "" ) ; // タイトル保管 yt_vid_titles.push( yt_video_title ); console.log( "title='" + yt_video_title + "'" ); }); // 動画時間の配列 var yt_vid_times = []; // 動画時間をスキャン $x('//span[contains(concat(" ",normalize-space(@class)," "), " ytd-thumbnail-overlay-time-status-renderer ")]') // XPathでタグを絞り込み .forEach(function(elem, i){ // 全要素をループ // 動画の時間を取得 var yt_video_time = elem.innerHTML; // 時間整形 yt_video_time = yt_video_time .replace( /^\r?\n? */, "" ) .replace( /\r?\n? *$/, "" ) ; // 時間保管 yt_vid_times.push( yt_video_time ); console.log( "time='" + yt_video_time + "'" ); }); // 統合して出力 var yt_res = "<table><tbody>"; for( var i = 0; i < yt_vid_links.length; i ++ ){ yt_res += "<tr><td>" + ( i + 1 ) + "</td><td>" + yt_vid_titles[i] + "</td><td>" + yt_vid_links[i] // 時間はセルの先頭に'を付けて,スプレッドシート上での不要な自動整形を抑止する + "</td><td>'" + yt_vid_times[i] + "</td></tr>" ; } yt_res += "</tbody></table>"; document.body.innerHTML = yt_res;
古いバージョン(2018年)
2018年になり,プレイリストのページのDOM構造が変化しているので,コードを更新しました。
複数行バージョンをご利用ください。
改行を含めて整形したコードは下記のようになる。
console.clear(); // 出力をクリア // 動画タイトル var yt_vid_titles = []; // 動画URL var yt_vid_links = []; // スキャン $x('//a[contains(concat(" ",normalize-space(@class)," "), " yt-uix-tile-link ")]') // XPathでタグを絞り込み .forEach(function(elem, i){ // 全要素をループ var yt_video_title = elem.innerHTML.replace(/^\s+/,"").replace(/\s+$/,""); yt_vid_titles.push( yt_video_title ); //console.log(yt_video_title); var yt_video_link = elem.href; // playlistから外す yt_video_link = yt_video_link.replace( /&list=.+/, "" ); yt_vid_links.push( yt_video_link ); //console.log(yt_video_link); }); // 統合して出力 for( var i = 0; i < yt_vid_titles.length; i ++ ){ var yt_video_str = ( i + 1 ) + " " + yt_vid_titles[i] + "\n" + yt_vid_links[i] + "\n\n" ; console.log( yt_video_str ); } ""; // 末尾に「undefined」と出力されるのが気持ち悪いので。
ブックマークレットのようにお気に入り操作をしなくても,すぐに実行できるというのが長所。
Youtubeのプレイリスト一覧画面を開いた状態で,これを実行すると,コンソールで下記のようなテキストが出力される。
1. 四季と月の名前 - YakuSuウクライナ語講座
http://www.youtube.com/watch?v=zKIU-W...
2. I love you - ウクライナ語で告白 - YakuSuウクライナ語講座
http://www.youtube.com/watch?v=usucSs...
3. すみません - ウクライナ語で謝罪 - YakuSuウクライナ語講座
http://www.youtube.com/watch?v=a8X9sD...
4. ありがとう - ウクライナ語で感謝 - YakuSuウクライナ語講座
http://www.youtube.com/watch?v=2SmgPr...
5. こんにちは - ウクライナ語で挨拶 - YakuSuウクライナ語講座
http://www.youtube.com/watch?v=6woiPj...
ブログに貼り付ければ,すぐリンク集になる。なんと簡単なスクレイピング。
他の形式
Youtube上でプレイリストになっていない場合は,
「アップロードされた動画」の一覧画面で下記のコードを実行すれば似たような結果を取得できる。
// Operaの開発者コンソールで実行 console.clear(); // タイトルとリンクを収集 var arr_movies = []; $x('//a[contains(concat(" ",normalize-space(@class)," "), "ytd-grid-video-renderer")]') .forEach(function(elem, i){ arr_movies.push(elem); }); arr_movies = arr_movies.reverse(); // 再生時間を収集 var arr_times = []; $x('//span[contains(concat(" ",normalize-space(@class)," "), "ytd-thumbnail-overlay-time-status-renderer")]') .forEach(function(elem, i){ arr_times.push( elem.innerHTML.replace(/^\s+/,"").replace(/\s+$/,"") ); }); arr_times = arr_times.reverse(); // テーブルに出力 var ht = "<table><tbody>"; arr_movies.forEach(function(elem,i){ // リンクから余計な情報をはぎ取る var url_str = elem.href .replace(/&.+$/, "") ; ht += "" + "<tr>" + "<td>" + (i+1) + "</td>" + "<td>" + elem.title + "</td>" + "<td>" + url_str + "</td>" // 時間表示は欠ける場合があるので収録しないでおく //+ "<td>" //+ "'" //+ arr_times[i] //+ "</td>" + "</tr>" + "\n" ; }); ht += "</tbody></table>"; document.body.innerHTML = ht; /* // コンソールに出力 arr_movies.forEach(function(elem,i){ var ret = "" + elem.title + "\n" + elem.href + "\n・" + arr_times[i] + "\n" + "\n" ; console.log( ret ); }); */ "";
下記は古いコード(2017/11/27):
console.clear(); var arr_movies = []; $x('//a[contains(concat(" ",normalize-space(@id)," "), "video-title")]') .forEach(function(elem, i){ arr_movies.push(elem); }); arr_movies.reverse().forEach(function(elem,i){ console.log(elem.title+"\n"+elem.href+"\n\n\n"); }); "";
「あとで見る」リストから抽出することもできる。
console.clear(); $x('//a[contains(concat(" ",normalize-space(@class)," "), " pl-video-title-link ")]').forEach(function(elem, i){ console.log(elem.innerHTML.replace(/^\s+/,"").replace(/\s+$/,"")+"\n"+elem.href+"\n\n\n");}); "";
「履歴」とか「高く評価した動画」からリストを抽出することもできる。
console.clear(); var arr_movies = []; $x('//a[contains(concat(" ",normalize-space(@class)," "), " yt-uix-tile-link ")]') .forEach(function(elem, i){ arr_movies.push(elem); }); arr_movies.reverse().forEach(function(elem,i){ var tit = elem.textContent .replace(/^[ | |\t|\n]+/g,"") .replace(/[ | |\t|\n]+$/g,"") ; console.log(tit+"\n"+elem.href+"\n\n\n"); }); "";
「ひまわり動画」なら下記のコードでOK。
console.clear(); $x('//h2[@class="force_title"]//a').forEach(function(elem, i){ console.log((i+1)+". "+elem.innerHTML.replace(/^\s+/,"").replace(/\s+$/,"")+"\n"+elem.href+"\n\n\n");}); "";
参考リンク
特定のclass属性を持った任意の要素にマッチするXPath | 3.14
http://white.s151.xrea.com/blog/2008-...
- div[contains(concat(" ",normalize-space(@class)," "), " hoge ")]
Firebugで,XPathを使って特定のclass名を指定し,DOM要素を絞り込む方法
http://computer-technology.hateblo.jp/entry/20140312/p2
- $x("//div[@class='hoge']")
JavaScript初級者から中級者になろう:十章第二回 DOMでのXPathの利用
http://uhyohyohyo.sakura.ne.jp/javasc...
- documentのevaluateというメソッドは、5個の引数を渡してXPathを処理してもらい、結果をXPathResultというオブジェクトで返す
Firebug Command Line APIを弄る($xのコンテキスト) - os0x.blog
http://os0x.hatenablog.com/entry/2007...
- $x.toSource() と打ちます。 すると、 (function (xpath) {return FBL.getElementsByXPath(baseWindow.document, xpath);})
効率的なデバッグをサポートするFirebugのコンソールAPI - builder by ZDNet Japan
http://builder.japan.zdnet.com/html-c...
Command Line API - FirebugWiki
https://getfirebug.com/wiki/index.php...
Firebugの便利な組み込み関数 - 技術メモ帳
http://d.hatena.ne.jp/lurker/20060801...JavaScriptで配列をループで処理するベストな書き方は? - QA@IT
http://qa.atmarkit.co.jp/q/2803
- JavaScriptで配列をループを使って処理する場合、 for...inまたはネイティブのArray.forEach()
$x()で取得した要素配列は,[0]のように配列インデックスを使って個別の要素を取り出し,DOM要素として操作できる。
want to get an innerHTML through xpath - Google グループ
https://groups.google.com/forum/#!top...
- $x("//div[@id='searchtextPanel_wrap']/div[4]/div[4]")[0].innerHTML;
末尾のundefined問題などについて:
JavaScript - forEachとforとforinの速度テスト - Qiita
http://qiita.com/toshirot/items/94d13...
- forEach(function (x, i))
正規表現 - JavaScript | MDN
https://developer.mozilla.org/ja/docs...
- \s : スペース、タブ、改ページ、改行を含む 1 つのホワイトスペース文字にマッチします。
Issue 3820 - fbug - console.log always appends a line saying undefined - Web development evolved - Google Project Hosting
https://code.google.com/p/fbug/issues...
- I expect that only "Hello, World!" to be printed, instead I see this: "Hello, World!" undefined
Issue 3784 - fbug - "undefined" shouldn't be shown as return value of internal function calls in Console Panel - Web development evolved - Google Project Hosting
https://code.google.com/p/fbug/issues...
- function change() { test = "hello"; } change(); >hello :Better if not perfect
関連する記事:
Selenium 中級者になろう (変数+XPath+JavaScriptを,テストケース中で利用する方法)
http://language-and-engineering.hatenablog.jp/entry/20090818/p1
タブブラウザで,「開いている大量のタブ」をテキストで保存し,復元する方法
http://language-and-engineering.hatenablog.jp/entry/20140123/p1
はてなダイアリーに執筆した記事一覧を,表形式に整理するブックマークレット (アーカイブページを,Excelに貼り付けやすく整形加工)
http://language-and-engineering.hatenablog.jp/entry/20140102/p1
はてブのマイページから,情報を一括して整形・抽出するブックマークレット
http://language-and-engineering.hatenablog.jp/entry/20131229/p1