スポンサーリンク

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");}); ""; 

参考リンク

XPathFirebug APIについて:

特定の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...

  • FirebugのコンソールAPIを使えば、コンソールへのメッセージの出力やスクリプトの実行時間の計測などを行うことができる。


Command Line API - FirebugWiki
https://getfirebug.com/wiki/index.php...

  • $x(xpath) Returns an array of elements that match the given XPath expression.


Firebugの便利な組み込み関数 - 技術メモ帳
http://d.hatena.ne.jp/lurker/20060801...

  • Firebugには、便利な組み込み関数が定義されている。$x() で 任意のXPath要素が取得できる

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 中級者になろう (変数+XPathJavaScriptを,テストケース中で利用する方法)
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