アクセス解析CGI #00001 [プログラミング]
なんとなくアクセス解析CGIを自分で作ってみたくなったので、早速作ってみることにします。
細かく細かく何回にもわけて作っていくつもり。
とりあえず今回はJavaScriptをページに設置して、JavaScriptでページのタイトル・URLとリンク元URLを取得。そのデータをCGIに渡して、CGI側がページのURL・タイトルと呼び出された回数をデータファイルに吐き出す、というプログラムを書きました。テスト用としてデータファイルの行数を表示するJavaScriptを返します。バージョン0.001。
以下がCGIのソース。perlで書いてます。
ana.cgi - アクセス解析 ver.0.001
#!/usr/local/bin/perl $query = $ENV{'QUERY_STRING'}; @query = split(/&/, $query); foreach (@query) { ($property, $value) = split(/=/, $_); $value =~ tr/+/ /; $value =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack("C", hex($1))/eg; $value =~ s/\r\n//g; $value =~ s/\r//g; $value =~ s/\n//g; $q{$property} = $value; } $ttl = $q{'TITLE'}; $url = $q{'URL'}; $ref = $q{'REFR'}; @dat = (); # データファイル読み込み open(FILE, "./ana.dat") or die; @dat = <FILE>; close(FILE); if (@dat != 0) { $i = 0; $g = 0; while ($i <= $#dat) { ($durl, @dres) = split(/<>/, $dat[$i]); if ($durl eq $url) { $g = 1; last; } $i++; } if ($g == 1) { ($durl, $dcnt, @dres) = split(/<>/, $dat[$i]); $dcnt++; $dat[$i] = join('<>', ($durl, $dcnt, @dres)); } else { push(@dat, "$url<>1<>$ttl<>\n"); } } else { @dat = ("$url<>1<>$ttl<>\n"); } #データファイル書き込み $lfh = my_flock() or die 'Busy!'; open(FILE, ">./ana.dat") or die; foreach (@dat) { print FILE "$_"; } close(FILE); my_funlock($lfh); $prt = $#dat + 1; print "Content-type: text/javascript\n"; print "document.write(\'test"; print "$prt"; print "\')\;\n"; exit(0); # ファイルロック・アンロック # 参考:http://tech.bayashi.net/pdmemo/filelock.html sub my_flock { my %lfh = (dir => './lockdir/', basename => 'lockfile', timeout => 60, trytime => 10, @_); $lfh{path} = $lfh{dir} . $lfh{basename}; for (my $i = 0; $i < $lfh{trytime}; $i++, sleep 1) { return \%lfh if (rename($lfh{path}, $lfh{current} = $lfh{path} . time)); } opendir(LOCKDIR, $lfh{dir}); my @filelist = readdir(LOCKDIR); closedir(LOCKDIR); foreach (@filelist) { if (/^$lfh{basename}(\d+)/) { return \%lfh if (time - $1 > $lfh{timeout} and rename($lfh{dir} . $_, $lfh{current} = $lfh{path} . time)); last; } } undef; } sub my_funlock { rename($_[0]->{current}, $_[0]->{path}); }
文字コードの変換とかもしてない未熟児。時刻さえもとっていなかったりする。
データの書き方が悩みどころ。1時間で区切って訪問者数を積み上げていくのがいいのだろうか。
1日ごとにデータファイルを作っていくのがいいのかもしれない。もうちょっと悩んでみよう。
で、下がページに設置するJavaScriptのソース。
ana.js
var cgi = "./ana.cgi"; // CGIファイルの場所 // URLエンコード関数 // 参考:http://www.cresc.co.jp/tech/java/URLencoding/JavaScript_URLEncoding.htm function EncodeURL(str){ var s0, i, s, u; s0 = ""; for (i = 0; i < str.length; i++){ s = str.charAt(i); u = str.charCodeAt(i); if (s == " "){ s0 += "+"; // 半角スペースは+に変換 }else { // 2a:* 2d:- 2e:. 5f:_ 30-39:0-9 41-5a:A-Z 61-7a:a-z if ( u == 0x2a || u == 0x2d || u == 0x2e || u == 0x5f || ((u >= 0x30) && (u <= 0x39)) || ((u >= 0x41) && (u <= 0x5a)) || ((u >= 0x61) && (u <= 0x7a))){ s0 = s0 + s; }else { // 上以外の1バイト文字 if ((u >= 0x0) && (u <= 0x7f)){ s = "0"+u.toString(16); // 文字コードを16進数にして頭に0を挿入 s0 += "%"+ s.substr(s.length-2); // 変数sの文字列の後ろ2文字を取って、頭に%を挿入 // 4バイト文字 }else if (u > 0x1fffff){ s0 += "%" + (0xf0 + ((u & 0x1c0000) >> 18)).toString(16); s0 += "%" + (0x80 + ((u & 0x3f000) >> 12)).toString(16); s0 += "%" + (0x80 + ((u & 0xfc0) >> 6)).toString(16); s0 += "%" + (0x80 + (u & 0x3f)).toString(16); // 3バイト }else if (u > 0x7ff){ s0 += "%" + (0xe0 + ((u & 0xf000) >> 12)).toString(16); s0 += "%" + (0x80 + ((u & 0xfc0) >> 6)).toString(16); s0 += "%" + (0x80 + (u & 0x3f)).toString(16); // 2バイト }else { s0 += "%" + (0xc0 + ((u & 0x7c0) >> 6)).toString(16);d s0 += "%" + (0x80 + (u & 0x3f)).toString(16); } } } } return s0; } var ttl = EncodeURL(document.title); var url = EncodeURL(document.URL); var ref = EncodeURL(document.referrer); var vsrc = cgi; vsrc = vsrc + '\?TITLE='; vsrc = vsrc + ttl; vsrc = vsrc + '&URL='; vsrc = vsrc + url; vsrc = vsrc + '&REFR='; vsrc = vsrc + ref; document.write('<script ' , 'type="text/javascript" src="' , vsrc , '"><\/script>');
なんともいえないやっつけ仕事感。
てか受験前なのに何やってんだろ・・・・。
<1月15日追記>
修正すべき場所。
ロックするのはデータ読み込む前にしなくちゃ。
データの区切りに"<>"を使ってるのにデータ中にそれが含まれているかどうかのチェックしてないのはまずいし。
とりあえずはこれだけかな。
ver.0.002では日付ごとにデータファイルを作るようにしようと思う。
コメント 0