はてなカウンターから単一の検索語のランキングを取得し,エクセルに書きだす。
lib_excel.jsに,ブックを上書き保存を追加した。
キーワード集計.bat
@echo off cscript //nologo sum_kwds.wsf pause
sum_kwds.wsf
<job> <script language="jscript" src="lib_excel.js" /> <script language="jscript" src="common.js" /> <script language="jscript" src="main.js" /> </job>
common.js
function log(s){ WScript.Echo(s); } // WSHの実行引数から有効なファイルを取得 function getFilePathFromArgument( wargs ){ // 引数があるか if( wargs.length == 0 ) { // http://d.hatena.ne.jp/language_and_engineering/20110921/p1 log("同一フォルダ上のファイルをドロップしてください。"); WScript.Quit(); } // ファイルパスを構築 var filepath = wargs.Unnamed(0); var fso = WScript.CreateObject("Scripting.FileSystemObject"); // ファイルが存在するか if( ! fso.FileExists( filepath ) ) { // http://wsh.style-mods.net/ref_filesystemobject/fileexists.htm log( filepath + " は無効なファイルパスです。"); log("同一フォルダ上のファイルをドロップしてください。"); WScript.Quit(); } else { log( filepath + " は有効なファイルです。"); } return filepath; } // IE // ページを移動 function ie_goto_url( ie, url ){ ie.Navigate( url ); ie_wait_while_busy( ie, url ); } // IEがビジー状態の間待ちます function ie_wait_while_busy( ie, _url ) { var timeout_ms = 30 * 1000; var step_ms = 100; var total_waited_ms = 0; while( ( ie.Busy ) || ( ie.readystate != 4 ) ) { WScript.Sleep( step_ms ); // タイムアウトか? total_waited_ms += step_ms; if( total_waited_ms >= timeout_ms ) { log( "警告:タイムアウトのため,リロードします。(" + ie.LocationURL + ")" ); // どこかに移動中なら,そこへの移動を再試行 if( _url ) { log( _url + "への遷移を再試行"); ie_goto_url( ie, _url ); } else { log( "リロード中"); // 移動先が明示されていなければリロード ie.document.location.reload( true ); ie_wait_while_busy( ie ); } break; } } WScript.Sleep( 1000 ) }
lib_excel.js
// // MS ExcelとOOo CalcとKingsoft Spreadsheetsを // 共通して取り扱うためのライブラリ // // ver0.2 // // 要件: // Excelで組んだロジックをOOcで使いまわし,その逆も可としたい。 // 設計方針: // Decorator・factoryパターンあたりを参考に, // オフィス製品の差異を内部にコンポジットで隠蔽して切り替え,外側のAPIは統一する。 /* クラス設計: IExcel --- IBook --- ISheet --- ICell 名前空間はIExcelに集約 */ // ---------- 表計算ソフトのラッパーオブジェクト ---------- var IExcel = function(){ // 初期化 this.defineExcelType(); }; IExcel.prototype = { // 内部で使うオフィス製品のタイプ isMS : false, isKS : false, isOO : false, type_code : null, // どれを使うか調査して決める defineExcelType : function(){ try{ // MS製のオフィスがインストールされていれば最優先する this._excel = WScript.CreateObject("Excel.Application"); // http://d.hatena.ne.jp/language_and_engineering/20140214/p1 this.isMS = true; this.type_code = "ms"; }catch(e){ // MSが無かったら try{ // KingSoftがあれば,Excelと同一のAPIなのでこれを使う this._excel = WScript.CreateObject("ET.Application"); // http://d.hatena.ne.jp/language_and_engineering/20121218/p1 this.isKS = true; this.type_code = "ks"; }catch(e2){ // Kingsoftも無かったら try{ // 最後の手段として,OpenOffice.org Calcを使う var service_manager = WScript.CreateObject("com.sun.star.ServiceManager"); this._ooo_desktop = service_manager.createInstance("com.sun.star.frame.Desktop") // http://d.hatena.ne.jp/language_and_engineering/20141227/OOoCalcByWSHJScript this.isOO = true; this.type_code = "oo"; }catch(e3){ WScript.Echo("オフィス製品を何か一つインストールしてください。"); } } } this._books = []; // 初期化完了 return; } , _excel : null, _ooo_desktop : null, // Visible setVisible : function( b ){ if( this.isMS || this.isKS ){ this._excel.Visible = b; }else{ // TODO: } } , // 新規ブックを開く getNewBook : function(){ var ibook; if( this.isMS || this.isKS ){ this._excel.Workbooks.Add(); var book = this._excel.Workbooks( this._excel.Workbooks.Count ); // インタフェースに変換 ibook = new IExcel.IBook( this, book ); return ibook; }else{ var doc = this._ooo_desktop.loadComponentFromURL( "private:factory/scalc", "_blank", 0, [] ); // インタフェースに変換 ibook = new IExcel.IBook( this, doc ); return ibook; } } , // 既存のブックをファイルパスで開く openBookByFilePath : function( file_path ){ var ibook; if( this.isMS || this.isKS ){ this._excel.Workbooks.Open( file_path ); var book = this._excel.Workbooks( this._excel.Workbooks.Count ); ibook = new IExcel.IBook( this, book ); return ibook; }else{ var doc = this._ooo_desktop.loadComponentFromURL( "file:///" + file_path.replace(/\\/g, "/"), "_blank", 0, [] ); ibook = new IExcel.IBook( this, doc ); return ibook; } } }; // ---------- ブックを表すラッパオブジェクト ---------- IExcel.IBook = function( parent, real_book ){ this._parent = parent; this._book = real_book; this.type_code = parent.type_code; this._sheets = []; }; IExcel.IBook.prototype = { _parent : null, _book : null, type_code : null, // ファイルパスを指定して保存 saveAs : function( file_path ){ if( this.type_code == "ms" || this.type_code == "ks" ){ this._parent.DisplayAlerts = false; this._book.SaveAs( file_path ); }else{ var file_url = "file:///" + file_path.replace(/\\/g, "/") ; this._book.storeAsURL( file_url, [] ); } }, // 上書き保存 save : function(){ if( this.type_code == "ms" || this.type_code == "ks" ){ this._parent.DisplayAlerts = false; this._book.Save(); // http://www.happy2-island.com/excelsmile/smile03/capter00303.shtml }else{ this._book.store(); } }, // 番号でシートを取得(1始まり) getSheetByIndex : function( index ){ log("現在のシートの個数:" + this.getSheetsCount() ); log("index: " + index); var sheet, isheet; if( this.type_code == "ms" || this.type_code == "ks" ){ sheet = this._book.Worksheets( index ); isheet = new IExcel.ISheet( this._parent, sheet ); return isheet; }else{ sheet = this._book.Sheets.getByIndex( index - 1 ); // 0始まり // http://blog.livedoor.jp/addinbox/archives/51243622.html // http://itref.fc2web.com/openoffice/basic/calc.html //sheet = this._book.Sheets( index - 1 ); // 0始まり //REM: これだとシートがうまく取れなかった log( "取得したシートの名称は" + sheet.Name ); isheet = new IExcel.ISheet( this._parent, sheet ); return isheet; } } , // シートの個数 getSheetsCount : function(){ if( this.type_code == "ms" || this.type_code == "ks" ){ return this._book.Sheets.Count; }else{ return this._book.Sheets.getCount(); } } // TODO:他のブック操作メソッド }; // ---------- シートを表すラッパオブジェクト ---------- IExcel.ISheet = function( parent, real_sheet ){ this._parent = parent; this._sheet = real_sheet; this.type_code = parent.type_code; }; IExcel.ISheet.prototype = { _parent : null, _sheet : null, type_code : null, // セル参照(番号は一始まり) getCell : function( y, x ){ log( y + "行" + x + "列目に書き込み" ); if( this.type_code == "ms" || this.type_code == "ks" ){ var cell = this._sheet.Cells( y, x ); // 1始まり var icell = new IExcel.ICell( this._parent, cell ); return icell; }else{ var cell = this._sheet.getCellByPosition( x - 1, y - 1 ); // 0始まりでMSと逆 var icell = new IExcel.ICell( this._parent, cell ); return icell; } } // TODO:他のシート操作メソッド }; // ---------- セルを表すラッパオブジェクト ---------- IExcel.ICell = function( parent, real_cell ){ this._parent = parent; this._cell = real_cell; this.type_code = parent.type_code; }; IExcel.ICell.prototype = { _parent : null, _cell : null, type_code : null, // 値を書き込み setValue : function( v ){ if( this.type_code == "ms" || this.type_code == "ks" ){ this._cell.Value = v; }else{ this._cell.String = v; } } , // 値を取得 getValue : function(){ if( this.type_code == "ms" || this.type_code == "ks" ){ return this._cell.Value; }else{ return this._cell.String; } } // TODO:他のセル操作メソッド }; /* サンプルコード // Excel起動 var excel = new IExcel(); excel.setVisible( true ); // 対象ブックを開く var book = excel.openBookByFilePath( filepath ); // 最初のシート var sheet = book.getSheetByIndex(1); ・・・ // ブックを保存 book.saveAs( filepath ); */
main.js
// ---- 設定事項 // 自分のはてな情報 var hatena_id = "〜〜"; var counter_id = "1"; // 対象年 var target_year = "2014"; // はてなカウンターの基本的なURL。 // 表示情報のページングに関する情報は除外してある var counter_url_base = "http://counter.hatena.ne.jp/" + hatena_id + "/report?cid=" + counter_id + "&date=" + target_year + "-12-01&mode=summary&target=searchwordsingle&type=yearly&" ; // 実際にはこの後ろに page=4 などが付与される // 情報を記録するExcelファイル名 var xls_filename = "searchwordsingle_report_" + hatena_id + "_" + counter_id + "_" + target_year + ".xls" ; // このバッチでページングを行なう際の最高ページ,リミット var max_page = 4; // 検索された回数の下限として認める範囲 var min_cnt = 1; // 1ページあたりに表示されるリンクの上限 var links_num_in_page = 50; // ページングの開始ページ var first_page_num = 1; function log(s){ WScript.Echo(s); } // ---- カレントフォルダにExcelを新規生成 var curr_dir = WScript.ScriptFullName.replace(WScript.ScriptName,""); var file_path = curr_dir + xls_filename; var fso = WScript.CreateObject("Scripting.FileSystemObject"); // ファイルが存在するか if( fso.FileExists( file_path ) ) { log( "既にファイルが存在します。実行停止"); WScript.Quit(); } else { log( "記録対象:" + file_path ); } // Excel起動 var excel = new IExcel(); excel.setVisible( true ); // 新規ブック var book = excel.getNewBook(); book.saveAs( file_path ); log("とりあえずブックを保存しました"); // 先頭のシートを情報の記録場所とする var sheet = book.getSheetByIndex(1); // ---- IEではてなカウンターから情報を抽出 // IE起動 var ie = WScript.CreateObject("InternetExplorer.Application") ie.Visible = true; ie_goto_url( ie, "http://www.google.co.jp/" ); log("ブラウザでのアクセスを開始します。"); // ページが存在する限り抽出を続行 var page_num = first_page_num; var continue_flag = true; while( continue_flag ) { var target_url = counter_url_base + "page=" + page_num ; // IEで開く log("[" + page_num + " ページ目] " + target_url + " を開きます"); ie_goto_url( ie, target_url ); // tableを取得 var table = ie.document .getElementById("hourlyreport") .getElementsByTagName("table")[0] ; var trs = table.getElementsByTagName("tr"); // trが51行あるので情報抽出。先頭のタイトル行はスキップ for( var i = 1; i < links_num_in_page + 1; i ++ ) { // 行があるか? var tr = trs[ i ]; if( tr ) { var y = ( page_num - 1 ) * links_num_in_page + i; log( y + "番目の情報を抽出"); var tds = tr.getElementsByTagName("td"); // 検索語を認識 var marks_sp = tds[0].getElementsByTagName("span")[0]; marks_sp.parentNode.removeChild( marks_sp ); var sw_txt = tds[0].innerText; // アクセス回数 var cnt = parseInt( tds[1].innerText.replace( /,/g, "" ), 10); if( cnt < min_cnt ) { continue_flag = false; log( "アクセス回数が下限に達したので抽出を終了" ); } else { log( i + " 行目から情報を抽出:「" + sw_txt + "」, " + cnt ); // 書き込み sheet.getCell( y, 1 ).setValue( "'" + sw_txt ); // 検索語が = で始まる場合があった sheet.getCell( y, 2 ).setValue( cnt ); } } else { // 行が途切れたらそこで終わり continue_flag = false; // ページが終わる場合もtable自体と先頭行は表示され, // 下部に「アクセスが記録されておりませんでした。」と出る。 } } // 次のページへ page_num ++; if( page_num > max_page ) { continue_flag = false; } } log("全ページの単独検索ワード抽出が完了"); // IEの制御を破棄 ie.Quit(); ie = null; // ---- 終了 // ブックを保存 book.save(); log( "ブックを保存しました。" ); // Excelを閉じて終了 //excel.Quit(); //excel = null; log("全処理が終了");
改良案:
- 月ごとの推移をグラフ化できるとよい。