Perl/CGI研究室 'PERL-LABO'

Perl/CGI研究室 'PERL-LABO' TOPへ
戻る(History.Back)

IPアドレスで重複カウント回避

研究内容

IPアドレスをチェックして、同じ人がページを繰り返し見てもカウントされない アクセスカウンターを研究します。

詳細

今回作るアクセスカウンター

前回作ったアクセスカウンターは、CGIプログラムが呼び出された回数を カウントする単純なものでした。同じ人が連続してページを開いた場合も、 ページを開くたびにカウントが増えてしまいます。 アクセスカウンターというよりページビューカウンターという 感じですね。アクセスカウンターは、 ページが何回見られたかっていうことより、 何人の人が訪問してくれたかっていうことを 知りたいのが普通です。 そこで、同じ人を連続してカウントしないようなアクセスカウンターを作りましょう。

同じ人かどうかを判断するために、IPアドレスが利用できます。 前回カウントを増やした時と同じIPアドレスだった場合は、カウントしないようにしてみましょう。

結果

処理の流れ

IPアドレスは、環境変数 REMOTE_ADDR に入っているんでした。 カウントを増やした時にこのIPアドレスを保存しておいて、 次回カウントを増やす時、IPアドレスが同じだったらカウントを増やさないようにする、 という感じです。

IPアドレスはファイルに保存しておく必要があります。 カウントを保存しているファイルとは別にしておいてもいいのですが、 ファイルのアクセスって時間がかかるものなんですよね。 ファイルを開いたり閉じたりというのは少ない方がいいです。 ですから、カウントを保存しておくファイルにIPアドレスも一緒に記録しておくことにしました。

ファイルにはカウントとIPアドレスの2つの情報を保存します。 例によって <> で区切って記録しておいてもいいんですが、 データが2つだけなので、1行目にカウント、2行目にIPアドレスっていうように しましょう。こうしておけば、<FILE> を2回呼べば必要なデータを読み込めます。 split で文字列を分割するよりも、ちょっとだけ簡単だし処理もちょっとだけ速い…ような気がします。

作成したCGIプログラム

counter.cgi
#!/usr/bin/perl

require 'lock.pl';

print "Content-type: text/html\n\n";
print "<body style=\"margin:0;font-size:15px;\"><tt>";

$ip        = $ENV{'REMOTE_ADDR'};
$lockdir   = "lockdir";
$countfile = "count.cgi";

if (! plab::lock($lockdir)) {
	error();
}

open FILE, "< $countfile";

$count  = <FILE>;	# 1行目はカウント
$previp = <FILE>;	# 2行目はIPアドレス

close FILE;

# 前回と今回でIPアドレスが異なっていたらカウントする
if ($previp ne $ip)
{
	++$count;

	if (! open(FILE, "> $countfile")) {
		plab::unlock($lockdir);
		error();
	}

	print FILE "$count\n";	# 1行目はカウント
	print FILE  $ip;		# 2行目はIPアドレス
	close FILE;
}

plab::unlock($lockdir);

$txt = sprintf("%05d", $count);
print $txt;
exit;

sub error
{
	print "?????";
	exit;
}

実行結果

カウンターです。ほぼ、このページに訪問してくれた方の数になると思います。

次のようなIFRAMEタグで表示しています。

<iframe src=counter.cgi width=45 height=20 frameborder=0></iframe>

解説

動きました

なんとか動きました。 これで、ページが表示された回数じゃなくて、 ページを訪問してくれた人の数にだいぶ近い数字になると思います。

プログラムの解説

新しいことが1つ出てきました。ne というものです。 これは、文字列同士が異なっているかどうかを調べるものです。 A ne B で、A と B が異なっていれば true、A と B が等しければ false になります。 eq の逆です。! (A eq B) と同じですね。 そういえば、eq は equal (イコール)、ne は not equal (ノット・イコール)ですね。 イコールじゃなくてイークォーゥかな?英語は苦手です。

さらに改良を

このサイトのように訪問者様が少ないサイトならこれでもほぼ問題ありません (^^; でもアクセスの多いサイトの場合、これでは足りません。 前回のIPアドレスしか記憶していないので、例えば Aさん → Bさん → Aさん というように 間に人が入ると、2回目のAさんは最初のAさんと区別が付かなくて計3人と数えられて しまいます。ですので、実際の人数よりも多めにカウントされてしまいます。 これを避けるためには、例えば、IPアドレスを過去1時間分記憶しておくとか、 そんな方法がありますね。 1時間さかのぼって同じIPアドレスならカウントしないようにすれば、 かなり重複カウントを避けられると思います。 でも、アクセスの多いサイトでそんなことをしたら、処理に時間がかかってしまいます。 CGIプログラムというのは単にHTMLファイルを開くよりも ずっとサーバーに負荷をかけますから、できるだけ処理は軽くしたいですよね。 そこで、クッキーの登場です!クッキーを使って訪問時間を記憶しておいてもらって、 1時間以内の再アクセスはカウントしないっていうようにする方法があります。 これならサーバーに負荷をかけることもないです。 ということで、これをやったら、重複カウントを避ける仕組みとしては合格点でしょうか? 次回のテーマは(たぶん)これですね。

分かったこと

  1. IPアドレスをチェックして重複カウントを避ける方法があります。
  2. 直前のIPアドレスを保存しておくだけでも、だいぶ違うと思います。
  3. IPアドレスを1時間分とか100個分とか保存しておく方法もありますが、
    サーバーへの負荷が気になるところです。
Perl/CGI研究室 'PERL-LABO' TOPへ
戻る(History.Back)

Copyright (c) 'PERL-LABO' All Rights Reserved.  リンクフリーです。