「はてなカウンター」から,今月の情報を一括して抽出しExcelに保存するバッチ
「はてなカウンター」から,特定の一カ月分の情報を抜き出して保存するバッチ。
ある月の
- アクセスされたページの順位とページタイトル
- リンク元URLの中から,検索エンジンなどを除去したリストのURLとページタイトル
- 単一の検索語
- 組み合わせの検索語
を,1つのExcelファイルにまとめて保存してくれる。
はてなIDや対象月などの設定項目は,書き変えやすいように工夫されている。
要は,下記の3つのエントリーを統合し,1つのバッチに整理して,使いやすくしたもの。
「はてなカウンター」から1年分の「検索キーワード」情報を抽出し,Excelに記録するバッチ
http://language-and-engineering.hatenablog.jp/entry/20140122/p1
「はてなカウンター」から1年分の「リンク元」情報を抽出し,URLをExcelに記録するバッチ (URLのフィルタリング機能付き)
http://language-and-engineering.hatenablog.jp/entry/20140120/p1
はてなカウンターのアクセス解析を整理して,「人気記事のランキング」を自動生成するバッチ
http://language-and-engineering.hatenablog.jp/entry/20140112/GenerateAccessRa...
下記はソースコード。ファイルが2つある。
月間レポートを作成.bat
@echo off rem はてなカウンターから,対象月の情報を収集する rem ---- 設定事項 ---- rem カウンターを特定する情報 SET HATENA_ID=(自分のはてなID) SET COUNTER_ID=(はてなカウンターのID) rem 対象月 SET YYYY=2014 SET MM=01 rem 集計の設定値 rem アクセスURLを集計する際の,アクセス回数の最低値 SET MIN_CNT_ACCESS_PAGE_URL=80 rem リンク元URLを集計する際の,アクセス回数の最低値 SET MIN_CNT_LINK_PAGE_URL=2 rem 単独語を集計する際の,出現回数の最低値 SET MIN_CNT_SINGLE_SEARCH_WORD=5 rem 複合語を集計する際の,出現回数の最低値 SET MIN_CNT_MIXED_SEARCH_WORD=2 rem ---- 処理 ---- echo 処理開始時刻: %date% %time% > log.txt SET CURRENT_DIR="%~dp0" cscript.exe //nologo //E:JScript counter-monthly-report.js ^ "%CURRENT_DIR%" "%HATENA_ID%" "%COUNTER_ID%" ^ %YYYY% %MM% ^ %MIN_CNT_ACCESS_PAGE_URL% ^ %MIN_CNT_LINK_PAGE_URL% ^ %MIN_CNT_SINGLE_SEARCH_WORD% ^ %MIN_CNT_MIXED_SEARCH_WORD% echo 終了しました。 echo 処理終了時刻: %date% %time% >> log.txt pause
counter-monthly-report.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 target_year = WScript.Arguments.Unnamed(3); var target_month = WScript.Arguments.Unnamed(4); // アクセスURLを集計する際の,アクセス回数の最低値 var min_cnt_access_page_url = WScript.Arguments.Unnamed(5); // リンク元URLを集計する際の,アクセス回数の最低値 var min_cnt_link_page_url = WScript.Arguments.Unnamed(6); // 単独語を集計する際の,出現回数の最低値 var min_cnt_single_search_word = WScript.Arguments.Unnamed(7); // 複合語を集計する際の,出現回数の最低値 var min_cnt_mixed_search_word = WScript.Arguments.Unnamed(8); // ---- 設定事項 // 情報を記録するExcelファイル名 var xls_filename = "monthly_report_" + hatena_id + "_" + counter_id + "_" + target_year + "_" + target_month + ".xls" ; // URLフィルタリングのホワイトリスト var reg_patterns_white = [ /^http/ ]; // URLフィルタリングのブラックリスト //(検索エンジンやマッシュアップ,URL加工系のWebサービスなど) var reg_patterns_black = [ // 自分のブログ内のリンク new RegExp( "^http:\/\/d\.hatena\.ne\.jp\/" + hatena_id + ".*\/" ), // 検索エンジン /^http(s)?:\/\/www\.google\./, /^http:\/\/(nl\.)?search\.yahoo\./, /^http:\/\/www\.bing\.com\/search/, /^http:\/\/jp\.ask\.com\//, /^http:\/\/jp\.hao123\.com\//, /^http:\/\/websearch\./, /^http:\/\/cgi\.search\./, /^http:\/\/nortonsafe\.search\.ask\.com\//, /^http:\/\/notify\.bluecoat\.com\/notify\-/, /^http:\/\/(s)?(ksearch)?\.luna\.tv/, /^http:\/\/wsearch\.ocn\.ne\.jp\//, /^http:\/\/www\.search\./, /^http:\/\/geo\-cafe\.starthome\.jp\//, /^http:\/\/green\.search\./, /^http:\/\/image\.search\./, /^http:\/\/realtime\.search\./, /^http:\/\/kids\.goo\.ne\.jp\/search/, /^http:\/\/office\.microsoft\.com\/ja\-jp\/results/, /^http:\/\/sp\-search\.auone\.jp/, /^http:\/\/webcache\.googleusercontent\.com/, /^http:\/\/www\.aolsearch\./, /^http:\/\/www\.hatena\.ne\.jp\/o\/search/, /^http:\/\/www\.metasearch\./, /^http:\/\/www\.pointtown\.com\/ptu\/search/, /^http:\/\/www\.so\-net\.ne\.jp\/search/, /^http:\/\/so\-net\.ne\.jp\/search/, /^http:\/\/ysearch\./, /^http:\/\/isearch/, /^http:\/\/hatenatunnel\.appspot\.com\/language_and_engineering\/searchdiary/, /^http:\/\/www\.amazon\.(co\.jp)?(com)?\/gp\/bit\/apps\/web\/SERP\/search/, /^http(s)?:\/\/encrypted\.google\.com/, /^http:\/\/www(.+)\.delta\-search\.com/, /^http:\/\/www\.searchgol\.com/, /^http:\/\/www\.mysearchresults\.com/, /^http:\/\/pex\.jp\/search/, /^http:\/\/livedoor\-search/, /^http:\/\/kaikatsu\.jword\.jp/, /^http:\/\/eonet\.excite\.co\.jp\/search/, /^http:\/\/www\.benri\.com\/kensaku/, // URL加工系 /^http:\/\/d\.hatena\.ne\.jp\/notify\-/, /^http:\/\/t\.co\//, /^http:\/\/search\./, /^http:\/\/r\.duckduckgo\.com/, // Webクリップ系,その他サイト /^http:\/\/b\.hatena\.ne\.jp\//, /^http:\/\/hatebu\.net\/entry\/d\.hatena\.ne\.jp\/language_and_engineering/, /^http:\/\/ceron\.jp\//, /^http:\/\/getpocket\.com\//, /^http:\/\/reader\.livedoor\.com/, /^http:\/\/tophatenar\.com/, /^http:\/\/k\.hatena\.ne\.jp\/keywordblog/, /^http:\/\/hatebu\-graph\.com/, /^http:\/\/tweetbuzz\.jp/, /^http:\/\/translate\.google\./, /^http(s)?:\/\/m\.facebook/, /^http(s)?:\/\/www\.facebook/, // ほか,はじきたいもの /^http(s)?:\/\/[^/]+\/$/ // ドメインのトップページからリンクされている ]; // ---- 前処理 // グローバル変数を定義 var excel = null; // 1ページあたりに表示されるリンクの上限 var links_num_in_page = 50; // 新規ブック生成 var file_path = curr_dir + xls_filename; var book = createNewBookHere( file_path ); // IE起動 var ie = getIE(); // ---- 記録 var sheet; sheet = getNewSheet( book, "アクセスURL" ); recordAccessPageURLCountersInSheet( sheet, min_cnt_access_page_url ); sheet = getNewSheet( book, "リンク元URL" ); recordLinkPageURLCountersInSheet( sheet, min_cnt_link_page_url ) sheet = getNewSheet( book, "単独の検索語" ); recordSingleSearchWordCountersInSheet( sheet, min_cnt_single_search_word ); sheet = getNewSheet( book, "組み合わせの検索語" ); recordMixedSearchWordCountersInSheet( sheet, min_cnt_mixed_search_word ); // ---- 終了 quitIE( ie ); saveBook( book ); log("全処理が終了"); // --------------- 以下はユーティリティ関数 --------------- // ログ出力 function log(s){ WScript.Echo(s); } // はてなカウンターの基本的なURLのトップを返す。 // ページング情報は含まない function getCounterBaseURL( target_type ) { // 表示情報のページングに関する情報は除外してある var counter_url_base = "http://counter.hatena.ne.jp/" + hatena_id + "/report?cid=" + counter_id + "&date=" + target_year + "-" + target_month + "-01&mode=summary&target=" + target_type + "&type=monthly&" ; return counter_url_base; } // はてなカウンターのページング情報付きのURLを返す。 function getCounterURLWithPage( target_type, page_num ) { var counter_url = getCounterBaseURL( target_type ) + "page=" + page_num ; return counter_url; } // カレントフォルダに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起動 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; } // ----- 検索キーワードを集計する関数 ----- // http://language-and-engineering.hatenablog.jp/entry/20140122/p1 // 単独語を集計してシートに記録 function recordSingleSearchWordCountersInSheet( sheet, min_cnt_single_search_word ) { var max_page = 1000; var min_cnt = min_cnt_single_search_word; recordSearchWordCountersInSheet_Common( sheet, "searchwordsingle", min_cnt, max_page ); } // 複合語を集計してシートに記録 function recordMixedSearchWordCountersInSheet( sheet, min_cnt_mixed_search_word ) { var max_page = 1000; var min_cnt = min_cnt_mixed_search_word; recordSearchWordCountersInSheet_Common( sheet, "searchword", min_cnt, max_page ); } // 単独語または複合語を集計してシートに記録する共通関数 function recordSearchWordCountersInSheet_Common( sheet, counter_type, min_cnt, max_page ) { // ページが存在する限り抽出を続行 var page_num = 1; var continue_flag = true; while( continue_flag ) { var target_url = getCounterURLWithPage( counter_type, 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"); // 値を認識 if( ( counter_type == "searchword" ) || ( counter_type == "searchwordsingle" ) ) { // アイコンなどを除去 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.Cells( y, 1 ).Value = "'" + sw_txt; // 検索語が = で始まる場合があった sheet.Cells( y, 2 ).Value = cnt; } } else { // 行が途切れたらそこで終わり continue_flag = false; // ページが終わる場合もtable自体と先頭行は表示され, // 下部に「アクセスが記録されておりませんでした。」と出る。 } } // 次のページへ page_num ++; if( page_num > max_page ) { continue_flag = false; } } // ワードの列の幅を自動調整 sheet.Rows(1).EntireColumn.AutoFit(); // http://www.happy2-island.com/excelsmile/smile03/capter00607.shtml log("全ページから情報の抽出が完了"); } // ---- URLを正規化して格納するためのオブジェクト // http://language-and-engineering.hatenablog.jp/entry/20140112/GenerateAccessRankingTableHTMLFromHatenaCounter // 新規インスタンスを返す function getNewUrlDic() { var url_dic = { // 辞書配列の本体 _arr : [] , blog_id : "" , // 総記事数 get_entry_num : function(){ return this._arr.length; } , // 位置に基づいて記事情報を返す get_entry_by_index : function( ind ) { return this._arr[ind]; } , // 位置に基づいて,正規化された記事URL情報を返す get_normal_url_info_by_index : function( ind ) { var info = this.get_entry_by_index( ind ); var _normal_url = "http://d.hatena.ne.jp/" + this.blog_id + "/" + info.entry_date + "/" + ( info.entry_id ? info.entry_id : "" ) ; return { normal_url : _normal_url, cnt : info.cnt }; } , // URLを受け付けて記録 record : function( url_raw, cnt ) { var url_info = this._normalize_url( url_raw ); var entry_date = url_info[1]; var entry_id = url_info[2]; // 無効なURLははじく if( ( ! url_info[0] ) || ( ! url_info[1] ) ){ log( "スキップ:" + url_raw ); return; } this.blog_id = url_info[0]; // 既に登録済みのURLの別形態URLであれば if( this._has_entry( entry_date, entry_id ) ) { this._update_entry_info( entry_date, entry_id, cnt ); } else { this._add_entry_info( entry_date, entry_id, cnt ); } } , // URLを正規化 _normalize_url : function( url_raw ) { // 注:「不明」などの文字列や,googleなど他ドメインのサイトのURLが渡ってくる場合もある if( url_raw.match( new RegExp("/mobile\\?", "i" ) ) ) { // ガラケーからアクセスされた場合 log("ガラケーからのアクセスを検出"); // URLからコアな情報だけを抽出 url_raw.match( new RegExp( "^http://d\\.hatena\\.ne\\.jp/([^/]+)/mobile\\?([^&]*&)?date=([0-9]+)(§ion=([^/]*))?(/)?$", "i" ) ); // http://d.hatena.ne.jp/---/mobile?guid=on&date=20130101§ion=p1 // guid=on& は無い場合もある // http://ratememo.blog17.fc2.com/blog-entry-907.html log( "1:" + RegExp.$1 + "," + "2:" + RegExp.$2 + "," + "3:" + RegExp.$3 + "," + "4:" + RegExp.$4 + "," + "5:" + RegExp.$5 ); var blog_id = RegExp.$1; var entry_date = RegExp.$3; var entry_id = RegExp.$5; // 空の場合もある。日付だけでアクセスされた場合 } else { // PCまたはタッチデバイスからアクセスされた場合 // URLからコアな情報だけを抽出 url_raw.match( new RegExp( "^http://d\\.hatena\\.ne\\.jp/([^/]+)/(touch/)?([0-9]+)(/)?([^/]*)(/)?$", "i" ) ); var blog_id = RegExp.$1; var entry_date = RegExp.$3; var entry_id = RegExp.$5; // 空の場合もある。日付だけでアクセスされた場合 // 201301 のように,月だけで指定された場合は,アクセス集計に数えないものとする。 if( entry_id ) { // URL末尾の?以降のパラメータを除去 // http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q13118651288 entry_id.match( /^([^\?]+)(\?.*)?$/ ); entry_id = RegExp.$1; } } // 重要な情報だけを返す return [ blog_id, entry_date, entry_id ]; } , // エントリが辞書に登録済みか _has_entry : function( _entry_date, _entry_id ) { for( var i = 0; i < this._arr.length; i ++ ) { var info = this.get_entry_by_index(i); if( ( info.entry_date == _entry_date ) && ( info.entry_id == _entry_id ) ) { // もうある return true; } } // まだない return false; } , // エントリ情報を追加 _add_entry_info : function( _entry_date, _entry_id, _cnt ) { if( ( ! _entry_date ) || ( ! _cnt ) ) { log( "無効なデータを追加できません。" + "entry_date=" + _entry_date + ", cnt=" + _cnt ); WScript.Quit(); } this._arr.push( { entry_date : _entry_date, entry_id : _entry_id, cnt : _cnt } ); } , // エントリ情報を更新 _update_entry_info : function( _entry_date, _entry_id, _cnt ) { // 同一エントリ情報を探して数字だけ更新 for( var i = 0; i < this._arr.length; i ++ ) { var info = this.get_entry_by_index(i); if( ( info.entry_date == _entry_date ) && ( info.entry_id == _entry_id ) ) { // 加算 info.cnt += _cnt; log( "重複:" + _entry_date + "/" + _entry_id + "のカウントを" + _cnt + "から" + info.cnt + "に調整" ) } } } , // 全記事情報がそろった時点で,登録情報を最終チェックする finalize_data : function() { // 記事IDを持っていない記事情報について, // 可能なら記事IDを付与する。 for( var i = 0; i < this._arr.length; i ++ ) { var info = this.get_entry_by_index(i); if( ! info.entry_id ) { var entry_date = info.entry_date; var entries = this._find_entries_by_entry_date( entry_date ); // 適切な記事IDを探す var ids = []; for( var j = 0; j < entries.length; j ++ ) { // 有効な記事IDを持っていれば候補として考慮 if( entries[ j ].entry_id ) { ids.push( entries[ j ].entry_id ); } } // 記事IDの候補があれば if( ids.length > 0 ) { // デフォルトでは,日付内で下から順にp1, p2, p3... と記事IDが付与される。 // 日付URLを開くと最初に見えるのは一番上の記事だから,最後の記事IDを採用する。 ids.sort(); var new_entry_id = ids[ ids.length - 1 ]; // 新しい記事IDを基に数値を加算 this._update_entry_info( entry_date, new_entry_id, info.cnt ); info.delete_flag = true; // 削除予約 log(entry_date + "の日付の記事に,記事IDとして" + new_entry_id + "を付与"); } else { // 記事IDの候補がなければしょうがないので,日付URLのまま放置する。 log(entry_date + "の日付の記事には記事IDを付与せず"); } } } // 上記で削除予約した情報を削除 for( var i = this._arr.length - 1; i >= 0; i -- ) { if( this.get_entry_by_index(i).delete_flag ) { // 配列から要素を削除 this._arr.splice( i, 1 ); // http://javascript-memo.seesaa.net/article/24832361.html } } } , // 日付をキーにして記事情報を検索する _find_entries_by_entry_date : function( entry_date ) { var ret = []; for( var i = 0; i < this._arr.length; i ++ ) { var info = this.get_entry_by_index(i); if( info.entry_date == entry_date ) { ret.push( info ) } } return ret; } }; return url_dic; } // ---- アクセスURLを読み取って整理する関数 // あるシート上のURLを正規化して,別のシートに記録 function normalizeURLCountersInSheet( sheet_from, sheet_to ) { var url_dic = getNewUrlDic(); var continue_flag = true; var y = 2; while( continue_flag ) { // 該当セルが空でなければ読み取りを継続 if( sheet_from.Cells( y, 1 ).Value ) { log( y + "行目を検査" ); // 正規化前のURLを取得 var url_raw = sheet_from.Cells( y, 1 ).Value; // 記録 url_dic.record( url_raw, sheet_from.Cells( y, 2 ) ); log( "URLを記録 [" + url_dic.get_entry_num() + "] " + url_raw ); // 次の行へ y ++; } else { // 空セルに到達したので読み取りを中断 continue_flag = false; } } log("全URLの記録が完了"); // 全記事情報を整理 url_dic.finalize_data(); log("記事情報の整理が完了"); // ---- 整理済みのURLをシートに書きだし // 登録順に書きだし for( var i = 0; i < url_dic.get_entry_num(); i ++ ) { var info = url_dic.get_normal_url_info_by_index( i ); var y = i + 1; sheet_to.Cells( y, 1 ).Value = info.normal_url; sheet_to.Cells( y, 2 ).Value = info.cnt; } log("全URLの書き出しが完了"); // シート上で,アクセス数の多い順にソート(URL統合のため順序が変動しているから) var xlDescending = 2; // http://msdn.microsoft.com/en-us/library/office/ff834316.aspx sheet_to.Range( "A1:B" + url_dic.get_entry_num() ).Sort( sheet_to.Cells(1, 2), // アクセス数をキーに降順で並び替え xlDescending ); // http://www.excel.studio-kazu.jp/kw/20091227224357.html log("ソートが完了"); } // あるシート上のURLをフィルタリングして,別のシートに記録 function filterLinkURLInSheet( sheet_from, sheet_to ) { var continue_flag = true; var y_raw = 1; var y_write = 1; while( continue_flag ) { // 該当セルが空でなければ読み取りを継続 if( sheet_from.Cells( y_raw, 1 ).Value ) { log( y_raw + "行目のURLを検査" ); var url = sheet_from.Cells( y_raw, 1 ).Value + ""; var cnt = sheet_from.Cells( y_raw, 2 ).Value + 0; var ok_flag = true; // ホワイトリストに通る? for( var i = 0; i < reg_patterns_white.length; i ++ ) { if( ! url.match( reg_patterns_white[i] ) ) { ok_flag = false; } } // ブラックリストにひっかからない? for( var i = 0; i < reg_patterns_black.length; i ++ ) { if( url.match( reg_patterns_black[i] ) ) { ok_flag = false; } } // フィルタを通過した? if( ok_flag ) { log("OK"); sheet_to.Cells( y_write, 1 ).Value = url; sheet_to.Cells( y_write, 2 ).Value = cnt; y_write ++; } else { log("NG"); } // 次の行へ y_raw ++; } else { // 空セルに到達したので読み取りを中断 continue_flag = false; } } log("全URLのフィルタリングが完了"); } // シートの特定の列の全URLにページタイトルを付与 function getTitlesForAllURLInSheet( sheet, url_row_num, write_row_num ) { // シート掲載順にアクセス var y = 1; var continue_flag = true; while( continue_flag ) { var target_url = sheet.Cells( y, url_row_num ).Value; if( ! target_url ) { continue_flag = false; } else if( target_url.match( /^http/ ) ) // 「不明」などの場合もある { // 開く log("[" + y + "] " + target_url + "を開きます"); ie_goto_url( ie, target_url ); // タイトルを抽出して記録 var tit = ie.Document.title; log( "タイトルは " + tit ); sheet.Cells( y, write_row_num ).Value = tit; } y ++; } log("全URLのタイトル抽出が完了"); } // アクセスURLを集計してシートに記録 function recordAccessPageURLCountersInSheet( sheet, min_cnt ) { var sheet_to = sheet; // 全URL var tmp_sheet_name = "アクセスURL(正規化前)"; var sheet_from = getNewSheet( book, tmp_sheet_name ); recordAccessOrLinkPageURLCountersInSheet_Common( sheet_from, min_cnt, "url" ); // URLの統合処理を実行 normalizeURLCountersInSheet( sheet_from, sheet_to ); // 作業用のシートを削除しないでとっておく // 全URLにページタイトルを付与 getTitlesForAllURLInSheet( sheet_to, 1, 3 ); } // リンク元URLを集計してシートに記録 function recordLinkPageURLCountersInSheet( sheet, min_cnt ) { var sheet_to = sheet; // 全URL var tmp_sheet_name = "リンク元URL(正規化前)"; var sheet_from = getNewSheet( book, tmp_sheet_name ); recordAccessOrLinkPageURLCountersInSheet_Common( sheet_from, min_cnt, "link" ); // URLのフィルタリングを実行 filterLinkURLInSheet( sheet_from, sheet_to ); // 作業用のシートを削除しないでとっておく // 全URLにページタイトルを付与 getTitlesForAllURLInSheet( sheet_to, 1, 3 ); } // アクセスURLとリンク元URLの共通関数 function recordAccessOrLinkPageURLCountersInSheet_Common( sheet, min_cnt, counter_type ) { var max_page = 1000; // ページが存在する限り抽出を続行 var page_num = 1; var continue_flag = true; while( continue_flag ) { var target_url = getCounterURLWithPage( counter_type, 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"); // URLを認識 var elem_as = tds[0].getElementsByTagName("a"); if( elem_as.length > 0 ) { var elem_a = elem_as[0]; //var link_url = elem_a.getAttribute("href"); // エラーになる場合がある //var link_url = elem_a.href; // エラーになる場合がある var link_url = elem_a.getAttribute("href", 2); // http://might1976.doorblog.jp/archives/51159843.html } else { // 「不明」などの文言の場合もある var link_url = tds[0].innerText; } // アクセス回数 var cnt = parseInt( tds[1].innerText.replace( /,/g, "" ), 10); if( cnt < min_cnt ) { continue_flag = false; log( "アクセス回数が下限に達したので抽出を終了" ); } else { log( i + " 行目からリンクを抽出:「" + link_url + "」, " + cnt ); // 書き込み sheet.Cells( y, 1 ).Value = link_url; sheet.Cells( y, 2 ).Value = cnt; } } else { // 行が途切れたらそこで終わり continue_flag = false; // ページが終わる場合もtable自体と先頭行は表示され, // 下部に「アクセスが記録されておりませんでした。」と出る。 } } // 次のページへ page_num ++; if( page_num > max_page ) { continue_flag = false; } } log("全ページのリンク抽出が完了"); }
「はてなカウンターを操作するためのユーティリティ関数」が,だいぶそろってきた。
関連する記事:
はてなダイアリーに執筆した記事一覧を,表形式に整理するブックマークレット (アーカイブページを,Excelに貼り付けやすく整形加工)
http://language-and-engineering.hatenablog.jp/entry/20140102/p1
はてブのマイページから,情報を一括して整形・抽出するブックマークレット
http://language-and-engineering.hatenablog.jp/entry/20131229/p1
2013年の人気記事ランキング (アクセス数やブクマ数の多かったエントリ)
http://language-and-engineering.hatenablog.jp/entry/20140201/p1