Статистика LJ

Я видел очень много скриптов на разных языках, которые рассказали бы мне о том, как узнать о своих посетителях.

Но они все были очень странные и местами требовали непонятных мне вещей.

В силу того, что всю жизнь писал на Perl'е, подумал что неплохо бы разобраться как дела обстоят с решением подобной задачи. 

Сама по себе задача тривиальна и решение её просто. Особенно на PHP. Но я PHP не люблю.

Посему за вечер был накидан вот такой код:


ljstat.pl


#!/usr/bin/perl -w

##
## Author: Nadz Goldman
##
## http://arviol.ru/
##

use CGI::WebOut(1);
use Mail::Mailer;

require 'mysql.pm';

###############################################################################################


# объявляю переменные
my( $topic , $date , $ref , $flag , $tmp );
my( $query , $pair , $name , $value , $input );
my @pairs;
my %input;
my( $to_address , $subject , $mailer , $body , $from_address );

# так я беру время системы
$date = `/bin/date "+%Y-%m-%d %H:%M"`;
# кому слать
$to_address = 'my@email.com';
# от кого слать
$from_address = 'robot@email.ru';
$ref = $ENV{'HTTP_REFERER'};
$flag = 0;

if( $ENV{'REQUEST_METHOD'} eq 'GET' ){
    $query=$ENV{'QUERY_STRING'};
}
elsif( $ENV{'REQUEST_METHOD'} eq 'POST' ){
    sysread( STDIN , $query , $ENV{'CONTENT_LENGTH'});
}

# начинаю распарсивать query, взятое из pairs на предмет
# наличия topic и чему он равен
# и попутно пихаю в body, где
# body - тело письма
@pairs = split( /&/ , $query );
foreach $pair ( @pairs ){
    ( $name , $value ) = split( /=/ , $pair );
    if( defined( $name )){
        if( !( defined( $value ))){
            next;
        }
    }
    $value =~ tr/+/ /;
    $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex())/eg;
    $value =~ s/<!--(.| )*-->//g;
    $input{$name} = $value;
    if( $name ne 'topic' ){
        if( $flag == 0){
            $flag = 0;
        }
    }
    if( $name eq 'topic' ){
        $topic = $value;
        $body .= "Topic: $topic\n";
        $flag++;
        if( $flag > 1 ){ $flag = 1; }
        next if $flag == 1;
    }
}
if( !( defined( $flag ))){ ololo(); }
if( !( defined( $value ))){ ololo(); }
if( $flag == 0 ){ ololo(); }

if( defined( $ENV{'REMOTE_ADDR'} )){
    $body .= "Remote address: $ENV{'REMOTE_ADDR'}\n";
}
else{
    $body .= "Remote address is unknown\n";
    $ENV{'REMOTE_ADDR'} = "UNKNOWN";
}
if( defined( $ENV{'REMOTE_HOST'} )){
    $body .= "Remote host: $ENV{'REMOTE_HOST'}\n";
}
else{
        $body .= "Remote host is unknown\n";
        $ENV{'REMOTE_HOST'} = "UNKNOWN";
}
if( defined( $ref )){
    $body .= "Referer: $ref\n";
}
else{
    $body .= "Referer is unknown\n";
    $ref = "UNKNOWN";
}

$body .= "Date: $date\n";
$subject = "$topic";

# отправляю почту
$mailer = Mail::Mailer->new();
    $mailer->open({
        From    => $from_address,
        To      => $to_address,
        Subject => $subject,
    }) or ololo();
    print $mailer $body;
$mailer->close();

# кладу в базу
insert();

# отдаю пустышку
print "Content-type: image/gif\n\n";
open GIF , 'empty.gif';
while( <GIF> ){ print; }
close GIF;

# люблю освобождать =)
sub ololo{
    undef @pairs;
    undef %input;
    undef $topic; undef $date; undef $ref; undef $flag; undef $tmp; undef $query; undef $pair;
    undef $name; undef $value; undef $input;
    undef $to_address; undef $subject; undef $mailer; undef $body; undef $from_address;
    undef $mysql::dbh;
    undef $mysql::sth;
    exit 0;
}

# процедура записи в базу
sub insert{
    mysql::_connect();

    $mysql::sth=$mysql::dbh->prepare( "INSERT INTO `ljstat` ( `topic` , `referer` , `remote_addr` , `remote_host` , `mydate`  ) VALUES ( '$topic' , '$ref' , '$ENV{'REMOTE_ADDR'}' , '$ENV{'REMOTE_HOST'}' , NOW() );" );
    $mysql::sth->execute || ololo();

    mysql::_disconnect();
}


exit;




Содержимое mysql.pm


package mysql;

##
## Author: Nadz Goldman
##
## http://arviol.ru/
##

use DBI;
use strict;
use warnings;


my $sql_user            = $ENV{SQL_USER};
my $sql_password        = $ENV{SQL_PASSWD};
my $sql_base            = $ENV{SQL_BASE};
my $debug;
our $dbh;
our $sth;
our $DBD;

if( ! ( $sql_user ))            { $sql_user="MegaUser"; }
if( ! ( $sql_password ))        { $sql_password="MegaPassword"; }
if( ! ( $sql_base ))            { $sql_base="MegaDB"; }

if( $debug ){ print "User:$sql_user\nPassword:$sql_password\n"; }


sub _connect{
    my( $u , $p ) = @_;

    $sql_user           = $u if $u;
    $sql_password       = $p if $p;

    if( $debug ){ print "User:$sql_user\nPassword:$sql_password\n"; }

    $dbh =  DBI->connect( "DBI:mysql:$sql_base;host=localhost" , $sql_user , $sql_password );
    $dbh -> { RaiseError } = 1;
}

sub _disconnect{
    $dbh->disconnect;
}

1;





В .htaccess я написал вот что:

Options +Includes +ExecCGI

У себя в записи ЖЖ я пишу вот такую вещь:


<div style="display:none"><img src="http://my.server.com/ljstat.pl?topic=ThisIsMyTopic".</div>

 

Вроде как необходимо указывать gif-пустышку, которая будет отдаваться, но то ли у меня достаточно современный браузер (Firefox 3.6.17), то ли я чего-то не понял, раскуривая маны, но скрипт вполне рабочий. Хохмы ради из него можно слепить счётчик или еще чего полезное. Этот пример скрипта скорее полезен тем, кто только начинает работать с вебом при помощи Perl'а (Это как раз про меня). Показан простейший способ работы без монстроподобных модулей типа Mason или CGI, избежание 500-ой ошибки и всяких print "Text/plain\n\n";

Просмотр статистики

Весьма ущербно, просто и примитивно.

Но на коленке за вечер с перерывами на пиццу, звонки и прочую чушь - самое то =)



#!/usr/bin/perl -w


##
## Author: Nadz Goldman
##
## http://arviol.ru/
##

use strict;
use warnings;
use CGI::WebOut(1);
use Socket;
use Math::Round;

no warnings;

require 'mysql.pm';

#####################################################################################################


my @pairs;
my ( $query , $pair , $name , $value , $input );
my %input;
my ( $p , $page , $sort , $debug , $sr );
my $limit;
my( $counter , $flag );
my ( $whois , @who_r );

if( $ENV{'REQUEST_METHOD'} eq 'GET' ){
    $query=$ENV{'QUERY_STRING'};
}
elsif( $ENV{'REQUEST_METHOD'} eq 'POST' ){
    sysread( STDIN , $query , $ENV{'CONTENT_LENGTH'});
}

@pairs = split( /&/ , $query );
foreach $pair ( @pairs ){
    ( $name , $value ) = split( /=/ , $pair );
    if( defined( $name )){
        if( !( defined( $value ))){
            next;
        }
    }
    $value =~ tr/+/ /;
    $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex())/eg;
    $value =~ s/<!--(.| )*-->//g;
    $input{ $name } = $value;
    if( $name eq 'p' ){
        $p = $value;
    }
    if( $name eq 'page' ){
        $page = $value;
    }
    if( $name eq 's' ){
        $sort = $value;
    }
    if( $name eq 'd' ){
        $debug = $value;
    }
    if( $name eq 'sr' ){
        $sr = $value;
    }
}

NoCache();

print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">";
print "<html>";
print "<head>";
print "
    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">
    <meta http-equiv=\"Content-Language\" content=\"ru\">
    <meta name=\"generator\" content=\"NG CMS\">
    <meta name=\"authors\" content=\"Ilya Vasilyev, ANZ Systems LTD copyright 2011\" />
    <meta name=\"copyright\" content=\"&copy; ANZ Systems\" />
    <META HTTP-EQUIV=\"CACHE-CONTROL\" CONTENT=\"NO-CACHE\">
    <META HTTP-EQUIV=\"PRAGMA\" CONTENT=\"NO-CACHE\">
    <META HTTP-EQUIV=\"EXPIRES\" CONTENT=\"Mon, 22 Jul 2002 11:12:01 GMT\">
    ";
print "<title>Lj Stats view page</title>";
print "
<script type=\"text/javascript\">
<!--
function zebraTable () {
tables = document.getElementsByTagName(\"table\");
for (i = 0; i < tables.length; i++) {
    if (tables[i].className == \"zebra\") {
        tr = document.getElementsByTagName(\"tr\");
            for (j = 0; j < tr.length; j++) {
                if (j%2) tr[j].className = \"odd\";
            }
        }
    }
}
window.onload = zebraTable;
-->
</script>

<style type=\"text/css\">
body{
    background: #fafafa;
    text-align: left;
    align: center;
    font: 8pt sans-serif;
}
img.right{
    border:none;
    padding:2px;
    margin:2px;
    -webkit-transform:rotate(-90deg);
    -moz-transform:rotate(-90deg);
    -o-transform: rotate(-90deg);
    -khtml-transform: rotate(-90deg);
    filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
}
img.left{
    border:none;
    padding:2px;
    margin:2px;
    -webkit-transform:rotate(90deg);
    -moz-transform:rotate(90deg);
    -o-transform: rotate(90deg);
    -khtml-transform: rotate(90deg);
    filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
}
table.info{
    align:center;
    padding:0px;
    margin:5px;
    border:1px dotted green;
}
table.info td{
    border:1px dotted blue;
    text-align:left;
    vertical-align:top;
    font:8pt verdana;
    width:100px;
    vertical-align:middle;
    text-align:center;
}
table.info table tr td{
    border:none;
    text-align:center;
    vertical-align:middle;
    padding:0px;
    margin:0px;
}
/*
table.info table tr td:hover{
    background:#ccc;
}

table.info table tr td a{
    text-decoration:none;
}
table.info table tr td a:hover{
    text-decoration:underline overline;
}
*/
table.zebra{
    padding:5px;
    margin:5px;
    align:center;
}
table.zebra tbody tr.odd {
    background:#eee;
}
table.zebra td{
    border:1px dotted green;
    text-align:left;
    vertical-align:top;
    font:8pt verdana;
}
table.zebra thead td{
    font-weight:bold;
    text-align:center;
    vertical-align:middle;
    background:#bbb;
    color:#444;
}
div{
    float:right;
    position:relative;
}

/* tooltip */
.tooltip { position: relative; }
.tooltip span {
  position: absolute;
  right: 0;
  top: -30px;
  display: none;
  min-width: 50px;
  padding: 3px 8px;
  white-space: nowrap;
  font-size: 10px;
  text-align: left;
/*  background-color: rgba(0,0,0,.0);*/
  background-color:#000;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  color: #fff;
}
.tooltip:hover span { display: block; }
/* для IE */
.tooltip span { background-color: #000; }
.tooltip span small { border-top: 6px solid #000; }
/* фиксим проблему со стрелочкой в IE 6 */
*html .tooltip span small {
border-left: 6px solid #363636; /* Соответствует цвету фона */
}
/* фиксим проблему в IE 8 */
.tooltip span { background-color /*\**/: #000\9  }
.tooltip span small { border-top /*\**/: 6px solid #000; }
</style>
      ";

print "</head>";
print "<body>";

mysql::_connect();
count();

print "<hr width=50% align=\"center\">";

print "<table width=100% class=\"zebra\">";
print "
        <tr>
            <thead>
            <td>Num</td>
            <td><a href=\"?p=id&page=$page&s=$sort&d=$debug&sr=$sr\">id</a></td>
            <td><a href=\"?p=topic&page=$page&s=$sort&d=$debug&sr=$sr\">Topic</a></td>
            <td><a href=\"?p=ref&page=$page&s=$sort&d=$debug&sr=$sr\">Referer</a></td>
            <!--<th>Mail</th>-->
            <td><a href=\"?p=ra&page=$page&s=$sort&d=$debug&sr=$sr\">Remote address</a></td>
            <!--<th>Remote host</th>-->
            <td><a href=\"?p=date&page=$page&s=$sort&d=$debug&sr=$sr\">Date</a></td>
            </thead>
        </tr>
        <tbody>
      ";

show_data();

print "</tbody>";
print "</table>";

mysql::_disconnect();

print "</body>";
print "</html>";

#####################################################################################################

sub count{
    print "<table width=200px class=\"info\">";

    $mysql::sth =  $mysql::dbh -> prepare( "SELECT COUNT(*) FROM ljstat;" );
    $mysql::sth -> execute || die $mysql::DBD::errstr;
    if( !( defined( $mysql::DBD ))){ undef  $mysql::DBD; }
    $counter = $mysql::sth -> fetchrow;
    print "<tr><td>Total</td><td>$counter</td></tr>";

    if( $counter > 100 ){ $flag = 1; }
    if( $flag == 1 ){
        if( !( defined( $page ))){ $page = 0 ; }
            my ( $left , $right , $cur_page , $total_pages , $last_page );
            if( $page == 0 ){ $left = 0; $right = 100; }
            else{ $left = $page-100; $right=$page+100; }
            $cur_page = round( $page / 100 );
            $total_pages = round(( $counter / 100 ));
            $last_page = ( $total_pages * 100 );
            if( $last_page > $counter ){ $last_page = $last_page - 100; $total_pages = $total_pages - 1; }
            if( $cur_page == $total_pages ){
                print "<tr><td>Page</td><td><table><tr><td colspan=2>$cur_page from <a href=\"?p=$p&page=$last_page&s=$sort&d=$debug&sr=$sr\">$total_pages</a></td></tr><tr><td colspan=2><a href=\"?p=$p&page=$left&s=$sort&d=$debug&sr=$sr\"><img src=\"/i/0.png\" class=\"right\"></a></td></tr></table></td></tr>";
            }
            elsif( $cur_page > $total_pages ){
                print "<tr><td>Page</td><td><table><tr><td colspan=2>$cur_page from <a href=\"?p=$p&page=$last_page&s=$sort&d=$debug&sr=$sr\">$total_pages</a></td></tr><tr><td colspan=2><a href=\"?p=$p&page=$left&s=$sort&d=$debug&sr=$sr\"><img src=\"/i/0.png\" class=\"right\"></a></td></tr></table></td></tr>";
            }
            elsif( $cur_page == 0 ){
                print "<tr><td>Page</td><td><table><tr><td colspan=2>$cur_page from <a href=\"?p=$p&page=$last_page&s=$sort&d=$debug&sr=$sr\">$total_pages</a></td></tr><tr><td colspan=2><a href=\"?p=$p&page=$right&s=$sort&d=$debug&sr=$sr\"><img src=\"/i/0.png\" class=\"left\"></a></td></tr></table></td></tr>";
            }
            else{
                print "<tr><td>Page</td><td><table><tr><td colspan=2>$cur_page from <a href=\"?p=$p&page=$last_page&s=$sort&d=$debug&sr=$sr\">$total_pages</a></td></tr><tr><td><a href=\"?p=$p&page=$left&s=$sort&d=$debug&sr=$sr\"><img src=\"/i/0.png\" class=\"right\"></a></td><td><a href=\"?p=$p&page=$right&s=$sort&d=$debug&sr=$sr\"><img src=\"/i/0.png\" class=\"left\"></a></td></tr></table></td></tr>";
            }
    }
    print "
        <tr>
        <td>Sort</td>
        <td><a href=\"?p=$p&page=$page&s=0&d=$debug&sr=$sr\">Norm</a>&nbsp;<a href=\"?p=$p&page=$page&s=1&d=$debug&sr=$sr\">Reverse</a></td>
        </tr>
        <tr>
        <td>Debug</td>
        <td><a href=\"?p=$p&page=$page&s=$sort&d=0&sr=$sr\">OFF</a>&nbsp;<a href=\"?p=$p&page=$page&s=$sort&d=1&sr=$sr\">ON</a></td>
        </tr>
        <tr>
        <td>Show empty</td>
        <td><a href=\"?p=$p&page=$page&s=$sort&d=$debug&sr=YES\">YES</a>&nbsp;<a href=\"?p=$p&page=$page&s=$sort&d=$debug&sr=NO\">NO</a></td>
        </tr>
        <tr>
        <td colspan=2>
        <a href=\"?p=id&page=0&s=0&d=0&sr=YES\">Reset</a>
        </td>
        </tr>
        ";
        if( $debug eq '1' ){
            print "
                <tr>
                <td colspan=2>
                debug
                <hr>
                ";
                if( !(defined( $sort ))){ print "sort not defined<br>";}
                if( !(defined( $p ))){ print "p not defined<br>";}
                if( !(defined( $page ))){ print "page not defined<br>";}
                print "
                p: $p&nbsp;page: $page&nbsp;&nbsp;sort: $sort&nbsp;<br>
                </td>
                </tr>
                ";
        }
    print "</table>";
}

sub show_data{
    if( !( defined( $page ))){ $page = 0 ; }
    if( !( defined( $sort ))){ $sort = "DESC" ; }
    if( !( defined( $sr   ))){ $sr   = "0" ; }
    if( $sort eq "1" ){ $sort = "DESC"; }
    elsif( $sort eq "0" ){ $sort = "ASC"; }

    my $q = " ";

    if( $sr eq "NO" ){ $q = " WHERE remote_addr != 'UNKNOWN' OR referer != 'UNKNOWN' "; }
    if( $p eq 'date'  ){ $mysql::sth = $mysql::dbh -> prepare( "SELECT * FROM ljstat $q ORDER BY mydate $sort LIMIT $page, 100;" ); }
    elsif( $p eq 'ra'    ){ $mysql::sth = $mysql::dbh -> prepare( "SELECT * FROM ljstat $q ORDER BY remote_addr $sort LIMIT $page, 100;" ); }
    elsif( $p eq 'ref'   ){ $mysql::sth = $mysql::dbh -> prepare( "SELECT * FROM ljstat $q ORDER BY referer $sort LIMIT $page, 100;" ); }
    elsif( $p eq 'topic' ){ $mysql::sth = $mysql::dbh -> prepare( "SELECT * FROM ljstat $q ORDER BY topic $sort LIMIT $page, 100;" ); }
    elsif( $p eq 'id'    ){ $mysql::sth = $mysql::dbh -> prepare( "SELECT * FROM ljstat $q ORDER BY id $sort LIMIT $page, 100;" ); }
    else{ $mysql::sth =  $mysql::dbh -> prepare( "SELECT * FROM ljstat $q ORDER BY id $sort LIMIT $page, 100;" ); }
    if(  defined ( $mysql::sth )){
        $mysql::sth -> execute || die $mysql::DBD::errstr;
        if( !( defined( $mysql::DBD ))){ undef $mysql::DBD; }
        my ( $t , $c );
        my ( $telo , $iaddr );
        if( $sort = "DESC" ){ $c = $counter; }
        else{ $c = 0; }
        my ( $my_id , $my_topic , $my_referer , $my_mail , $my_remote_address , $my_remote_host , $my_mydate );
        while(( $my_id , $my_topic , $my_referer , $my_mail , $my_remote_address , $my_remote_host , $my_mydate  ) = $mysql::sth -> fetchrow ){


            print "
            <tr>
                <td>$c</td>
                <td>$my_id</td>
                <td>$my_topic</td>
            ";
            $whois = "";

            $iaddr = inet_aton( $my_remote_address );
            $telo = gethostbyaddr( $iaddr , AF_INET );
            $telo = "" if( !( length( $telo) ));
                if( $my_referer eq "0" || $my_referer eq "UNKNOWN" ){
                    print "<td>&nbsp;</td>";
                }
                else{
                    $t = "<a href=\"$my_referer\">$my_referer</a>";
                    print "<td>$t</td>";
                }
            $whois = $telo;
            if( !(length( $whois ))){
                $whois = "#";
            }
            else{
                $whois = "http://".$whois;
            }
            print "
                <!--<td>$my_mail</td>-->
                <td  alt=\"$telo\" title=\"$telo\"><a class=\"tooltip\" href=\"$whois\">#####<span>$whois</span></a><div> <b> $telo </b></div> $my_remote_address </td>
                ";
            print "
                <!--<td>$my_remote_host</td>-->
                <td><time>$my_mydate</time></td>
            </tr>
                ";
            if( $sort = "DESC" ){ $c--; }
            else{ $c++; }
            }
    }
    return 0;
}

exit 1;







 arviol.ru, 2006-2018

Докер -- Сильно. Выгодно. Надежно