スポンサーリンク

WSH/JScriptで,Excelのデータを自動でグラフ描画 (題材:はてなカウンターのアクセス数を週ごとにグラフ化)


WSH/JScriptを使って,エクセルのデータをグラフ化しよう。

表計算データから,バッチで自動的にグラフを描画できる。



題材として,上記のような「ブログのアクセス数の推移」を

折れ線グラフにしてみる。


ここで掲載するバッチの処理は

  • はてなカウンターから,自動的にアクセス数を収集する。
  • 収集したデータを,Excelに記録する。
  • Excelに記録したデータをグラフ化して,ファイル内に保存する。

という処理を行なう。


グラフ生成部分のコードは,とても簡単。下記に抜粋:

// グラフを作成

var chs = sheet.ChartObjects();
var g = chs.Add( 30, 50, 500 + 20 * (x - 2), 300 );
g.Name = "アクセス数の遷移";

// データ範囲をセット(左上と右下)
var xlRows = 1; // データが横方向に並んでいる場合
var xlColumns = 2;
g.Chart.SetSourceData( sheet.Range( sheet.Cells(2, 2), sheet.Cells(3, x) ), xlRows );

// x軸の項目軸範囲をセット
g.Chart.SeriesCollection(1).XValues = sheet.Range(
	sheet.Cells(1, 2),
	sheet.Cells(1, x)
);

// オプションをセット
var xlLine = 4;
g.Chart.ChartType = xlLine // 折れ線

// 系列名をセット
g.Chart.SeriesCollection(1).Name = "PV";
g.Chart.SeriesCollection(2).Name = "ユニーク";

これだけで,WSHでグラフ描画できる。VBScriptの場合も同じ。

はてなカウンターから,週ごとのアクセス数をグラフ化するバッチ


週ごとのアクセス数をグラフ化.bat

@echo off

rem はてなカウンターから,週ごとのアクセス数を抽出してグラフ化するバッチ


rem ---- 設定事項 ----

rem カウンターを特定する情報
SET HATENA_ID=(はてなID)
SET COUNTER_ID=(はてなカウンター内の番号)

rem 集計の開始日
SET START_YYYY=2014
SET START_MM=01
SET START_DD=01


rem ---- 処理 ----

echo 処理開始時刻: %date% %time% > log.txt
SET CURRENT_DIR="%~dp0"

cscript.exe //nologo //E:JScript counter-weekly-access.js ^
  "%CURRENT_DIR%" "%HATENA_ID%" "%COUNTER_ID%" ^
  %START_YYYY% %START_MM% %START_DD%


echo 終了しました。
echo 処理終了時刻: %date% %time% >> log.txt

pause

counter-weekly-access.js

/*

	はてなカウンターから,対象月のレポートを作成してExcelに記録するバッチ
	
	・該当はてなIDにログイン済みであること
	・呼び出し側のバッチ内の設定事項を調整すること

*/


// 引数を取得
var curr_dir     = WScript.Arguments.Unnamed(0);
var hatena_id    = WScript.Arguments.Unnamed(1);
var counter_id   = WScript.Arguments.Unnamed(2);
var start_year   = WScript.Arguments.Unnamed(3);
var start_month  = WScript.Arguments.Unnamed(4);
var start_date   = WScript.Arguments.Unnamed(5);



// ---- 設定事項


// 情報を記録するExcelファイル名
var today = new Date();
var xls_filename = "weekly_graph_"
	+ hatena_id
	+ "_"
	+ counter_id
	+ "_"
	+ ( today.getFullYear() )
	+ "-"
	+ ( today.getMonth() + 1 )
		// 人間の見る世界なのでMonthに+1する事に注意
	+ "-"
	+ ( today.getDate() )
	+ "_since_"
	+ start_year
	+ "_"
	+ start_month
	+ "_"
	+ start_date
	+ ".xls"
;


// ---- 前処理


// グローバル変数を定義
var excel = null;

// 新規ブック生成
var file_path = curr_dir + xls_filename;
var book = createNewBookHere( file_path );
var sheet = getNewSheet( book, "calc" );

// IE起動
var ie = getIE();



// ---- 記録



// グラフ終了週の開始日曜
var previousSunday = getWeekStartSunday( today );
var calcEndDate = addDate( previousSunday, -7 );

// グラフ開始週の開始日曜
var userStartDate = new Date( start_year, start_month, start_date );
var calcStartDate = getWeekStartSunday( userStartDate );

// 指定期間でグラフを作成
recordWeeklyAccessGraph( sheet, calcStartDate, calcEndDate );



// ---- 終了


quitIE( ie );
saveBook( book );
log("全処理が終了");






// --------------- 以下はユーティリティ関数 ---------------



// ログ出力
function log(s){ WScript.Echo(s); }


// 2ケタで0埋めした文字列を返す
function dig2( num )
{
	if( num < 10 )
	{
		return "0" + num;
	}
	else
	{
		return "" + num;
	}
}


// ----- 日付演算 -----



// ある日付が属する週の,週が開始する日曜日を求める
function getWeekStartSunday( target_date )
{
	var diff_day_count = target_date.getDay();
	
	// 日曜日まで曜日をさかのぼる
	return addDate( target_date, - diff_day_count );
		// http://javascript123.seesaa.net/article/297521353.html
}


// 日付に対し,日にちを加算する
function addDate( orig_date, day_count )
{
	var result_date = new Date(
		orig_date.getFullYear(),
			// http://kawara-tan.blogspot.jp/2009/10/javascript-getyeargetfullyear.html
			
		orig_date.getMonth(),
			// 人間の見ない世界なのでMonthに+1しない事に注意
			
		orig_date.getDate()
	);
	
	// 指定日数分のミリ秒を加える
	var diff_ms = day_count * ( 1000 * 60 * 60 * 24 );
	result_date.setTime(
		result_date.getTime() + diff_ms 
	);
		// http://www.hoge256.net/2007/08/64.html
	
	return result_date;
}



// ----- Excel操作 -----



// カレントフォルダにExcelを新規生成し,ブックを返す。
function createNewBookHere( file_path )
{
	var fso = WScript.CreateObject("Scripting.FileSystemObject");

	// ファイルが存在するか
	if( fso.FileExists( file_path ) )
	{
		log( "既にファイルが存在します。実行停止");
		WScript.Quit();
	}
	else
	{
		log( "記録対象:" + file_path );
	}

	// Excel起動
	try
	{
		excel = WScript.CreateObject("ET.Application");
	}
	catch(e)
	{
		excel = WScript.CreateObject("Excel.Application");
	}
	excel.Visible = true;

	// 新規ブック
	excel.Workbooks.Add();
	var book = excel.Workbooks( excel.Workbooks.Count );

	// 新規ブックを保存
	excel.DisplayAlerts = false;
	book.SaveAs( file_path );
	log("とりあえずブックを保存しました");

	return book;
}


// 新しいシートを追加して返す
function getNewSheet( book, new_name )
{
	var sheet = book.Worksheets.Add();
		// http://officetanaka.net/excel/vba/sheet/sheet03.htm
	sheet.Name = new_name;
	
	return sheet;
}


// ブックを保存
function saveBook( book )
{
	excel.DisplayAlerts = false;
	book.SaveAs( file_path );

	log( "ブックを保存しました。" );
}



// ----- IE操作 -----



// IE起動
function getIE()
{
	var ie = WScript.CreateObject("InternetExplorer.Application")
	ie.Visible = true;
	ie_goto_url( ie, "http://www.google.co.jp/" );
	log("ブラウザでのアクセスを開始します。");
	
	return ie;
}


// IEがビジー状態の間待ちます
function ie_wait_while_busy( ie, _url )
{   
	var timeout_ms      = 45 * 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 )
}


// ページを移動
function ie_goto_url( ie, url ){
	ie.Navigate( url );
	ie_wait_while_busy( ie, url );
}


// IEの制御を破棄
function quitIE( ie )
{
	ie.Quit();
	ie = null;
}


function $( ie, elem_id )
{
	return ie.document.getElementById( elem_id );
}



// ----- はてなカウンター操作 -----



// はてなカウンターの週アクセスのURLを返す。
function getCounterWeeklyAccessURL( target_year, target_month, target_date )
{
	var counter_url = "http://counter.hatena.ne.jp/" 
		+ hatena_id
		+ "/report?cid="
		+ counter_id
		+ "&date="
		+ target_year
		+ "-"
		+ dig2( target_month )
		+ "-"
		+ dig2( target_date )
		+ "&mode=access&type=weekly"
	;
	
	return counter_url;
		// http://counter.hatena.ne.jp/XXXXX/report?cid=1&date=2014-02-09&mode=access&type=weekly
}






// 指定期間のアクセス数のグラフを作成
function recordWeeklyAccessGraph( sheet, calcStartDate, calcEndDate )
{
	log( "開始日:" + calcStartDate );
	log( "終了日:" + calcEndDate );
	var calcDate = new Date( calcStartDate );
	
	sheet.Cells( 1, 1 ).Value = "期間";
	sheet.Cells( 2, 1 ).Value = "PV";
	sheet.Cells( 3, 1 ).Value = "ユニーク";
	
	
	// シート上に期間内のアクセス数を記録
	var continue_flag = true;
	var x = 2;
	while( continue_flag )
	{
		// 該当週の情報へジャンプ
		var target_url = getCounterWeeklyAccessURL( 
			calcDate.getFullYear(), 
			calcDate.getMonth() + 1,
			calcDate.getDate()
		);
		ie_goto_url( ie, target_url );
		
		
		// 該当週のアクセス数を抽出
		var tds = $( ie, "hourlyreport" ).getElementsByTagName("td");
		var count_pv     = tds[0].innerText;
		var count_unique = tds[1].innerText;
		
		
		// 書き込み
		sheet.Cells( 1, x ).Value = calcDate.getFullYear() 
			+ "/"
			+ ( calcDate.getMonth() + 1 )
			+ "/"
			+ calcDate.getDate()
		;
		sheet.Cells( 2, x ).Value = count_pv;
		sheet.Cells( 3, x ).Value = count_unique;
		sheet.Columns( x ).AutoFit();
			// http://home.att.ne.jp/zeta/gen/excel/c04p54.htm
			
		
		// 次の週へ
		calcDate = addDate( calcDate, 7 );
		x ++;
		
		// 継続するか
		if( calcDate >= calcEndDate )
		{
			continue_flag = false;
		}
	}
	
	
	// グラフを作成
	// http://language-and-engineering.hatenablog.jp/entry/20090516/p1

	var chs = sheet.ChartObjects();
		// http://vbsguide.seesaa.net/article/144812178.html
	var g = chs.Add( 30, 50, 500 + 20 * (x - 2), 300 );
    g.Name = "アクセス数の遷移";

	// データ範囲をセット(左上と右下)
	var xlRows = 1;
	var xlColumns = 2; // http://9013.teacup.com/tokyoinfbox/bbs/20
	g.Chart.SetSourceData( sheet.Range( sheet.Cells(2, 2), sheet.Cells(3, x) ), xlRows );

	// x軸の項目軸範囲をセット
	g.Chart.SeriesCollection(1).XValues = sheet.Range(
		sheet.Cells(1, 2),
		sheet.Cells(1, x)
	);

	// オプションをセット
	var xlLine = 4; // http://officetanaka.net/excel/vba/graph/06.htm
	g.Chart.ChartType = xlLine // 折れ線
	/*
	g.Chart.HasTitle = true
	g.Chart.ChartTitle.Characters.Text = chart_title
	g.Chart.Axes(xlCategory, xlPrimary).HasTitle = true
	g.Chart.Axes(xlCategory, xlPrimary).AxisTitle.Characters.Text = x_title
	g.Chart.Axes(xlValue, xlPrimary).HasTitle = True
	g.Chart.Axes(xlValue, xlPrimary).AxisTitle.Characters.Text = y_title
	*/

	// 系列名をセット
	g.Chart.SeriesCollection(1).Name = "PV";
	g.Chart.SeriesCollection(2).Name = "ユニーク";

}

関連する記事:

Excel VBAで,グラフを自動で描画しよう(データ範囲を動的に変える) + ソフトウェアの品質保証について
http://language-and-engineering.hatenablog.jp/entry/20090516/p1


JavaScript で,クリックした座標に点を追加できるグラフチャートを描画する方法 (jQuery のプラグイン jquery.sparklines / jquery.flotの使い方)
http://language-and-engineering.hatenablog.jp/entry/20081120/1227114053


日経平均株価の下落ぶりをMIDIサウンドで味わう (コマンドラインでMIDI生成)
http://language-and-engineering.hatenablog.jp/entry/20081027/1225038111