Zur Navigation

Bot-Catcher [3]

21 C)-(iLL@

Den Catcher habe ich getestet, der funktioniert ganz gut - habe jedenfalls keinen Fehler entdeckt. So wie ich das sehe, kannst Du den ruhig überall einbinden und mal Daten sammeln lassen.

Den Viewer stelle ich bald hier rein, er wird sehr nett, wie Du sehen kannst :) Nur muss er zuerst gut laufen, so was halbgares mag ich nicht ^^.

27.04.2006 23:39

22 C)-(iLL@

So, weil ich sowieso nicht schlafen kann, bevor ich was nicht fertig habe, hier mal eine funktionierende Version, die eigentlich alles hat, was ich mir vorstellte.

Der Botcatcher besteht aus 4 Dateien: der Konstantendatei mit den Zugangsdaten 'bc_config.php', dem catcher 'botcatcher.php', dem viewer 'botviewer.php' und der CSS-Datei für den viewser 'botview.css'. Der Botcatcher ist im Grunde unverändert, er macht immer noch dasselbe wie am Anfang, nur habe ich den Code etwas überarbeitet (und getestet, natürlich). Die Zugangsdaten müssen nun nur mehr in die Konstantendatei, die anderen Dateien werden einfach abgespeichert und in dasselbe Verzeichnis gegeben (ich schlage die Server-Root vor, ist aber nicht zwingend notwendig). Es gibt nun auch einen Usernamen und ein Passwort für den botviewer, damit man einen eigenen Zugang hat um die Daten zu pflegen, und trotzdem die Seite selbst nicht wegsperren muss. Diese können frei gewählt werden.

Der Botcatcher wird, nachdem die Tabellen angelegt wurden und die Konstantendatei korrekt ausgefüllt ist, überall dort eingebunden, wo er Daten sammeln kann, d.h. wo er eine andere URL bekommt. Bei Systemen mit vielen Seiten ohne mod_rewrite in jede PHP-Seite, bei Seiten mit mod_rewrite reicht dann das Einbinden in die Seite, auf die intern die Aufrufe umgeleitet werden.

DB-Struktur (importieren z.B. mit PHPMyAdmin):
CREATE TABLE ua_ips (
 id int(11) unsigned  NOT NULL auto_increment,
 uaid smallint(6) unsigned NOT NULL default '0',
 ip varchar(15) NOT NULL default '',
 PRIMARY KEY  (id)
) TYPE=MyISAM PACK_KEYS=0;


CREATE TABLE ua_pool (
 id smallint(6) unsigned NOT NULL auto_increment,
 user_agent varchar(150) NOT NULL default '',
 type enum('B','R','U') NOT NULL default 'U',
 PRIMARY KEY  (id),
 UNIQUE KEY user_agent (user_agent)
) TYPE=MyISAM;

CREATE TABLE ua_sites (
 id smallint(6) unsigned NOT NULL auto_increment,
 url varchar(150) NOT NULL default '',
 noindex tinyint(1) NOT NULL default '0',
 PRIMARY KEY  (id),
 UNIQUE KEY url (url)
) TYPE=MyISAM;

CREATE TABLE ua_visits (
 ipid smallint(6) unsigned NOT NULL default '0',
 siteid smallint(6) unsigned NOT NULL default '0',
 visits mediumint(9) unsigned NOT NULL default '0',
 lastvisit datetime NOT NULL default '0000-00-00 00:00:00',
 PRIMARY KEY  (ipid,siteid)
) TYPE=MyISAM;


Speichern als 'bc_config.php' und Daten eintragen:

<?php

  define('BC_MYSQLUSER', 'username'); //MySQL-Username
  define('BC_MYSQLPASS', 'passwort');  //MySQL-Password
  define('BC_MYSQLHOST', 'localhost'); //MySQL-Hostname
  define('BC_MYSQLDB', 'dbname');    //MySQL-Databasename

  define('BC_USER', 'botviewer');  //User defined: Botviewer-Username
  define('BC_PASSWORD', 'passwort'); //User defined: Botviewer-Passwort

?>

Speichern als 'botcatcher.php':

<?php
  require 'bc_config.php';

  define('BC_DEBUG', false);  //Show all executed Queries - only true for debug!

  $conn = mysql_connect(BC_MYSQLHOST, BC_MYSQLUSER, BC_MYSQLPASS) or die(mysql_error());
  mysql_select_db(BC_MYSQLDB, $conn);

  function query($query, $show=BC_DEBUG) {
    global $conn;
    $res = mysql_query($query, $conn);
    if (mysql_errno($conn)) {
      die('<p class="mysql_error"><em>'.mysql_error($conn).'</em></p>');
    }
    if ($show) echo '<p class="mysql_debug"><strong>'.$query.'</strong></p>';
    return $res;
  }
  
  $ua = addslashes($_SERVER['HTTP_USER_AGENT']);
  $res = query("SELECT id, type FROM ua_pool WHERE user_agent='".$ua."'");
  
  if (@mysql_num_rows($res)) {
    list($ua_id, $ua_type) = mysql_fetch_row($res);
  } else {
    query("INSERT INTO ua_pool (user_agent) VALUES ('".$ua."')");
    list($ua_id, $ua_type) = array(mysql_insert_id($conn), 'U');
  } 

  if ($ua_type != 'B') { //is not a known browser  

    $url = $_SERVER['REQUEST_URI'];
    if (strpos('?', $url) !== FALSE) $url = substr($url, strpos($url, '?'));
    $ua_ip = $_SERVER['REMOTE_ADDR'];
    $now = date("Y-m-d H:i:s");

    $res = query("SELECT id FROM ua_ips WHERE uaid=$ua_id AND ip='$ua_ip'");
    if (@mysql_num_rows($res)) {
      $ip_id = mysql_result($res, 0, 0);
    } else {
      query("INSERT INTO ua_ips (uaid, ip) VALUES ($ua_id, '$ua_ip')");
      $ip_id = mysql_insert_id($conn);
    }
    $res = query("SELECT id, noindex FROM ua_sites WHERE url='$url'");
    if (@mysql_num_rows($res)) {
      list($site_id, $noindex) = mysql_fetch_row($res);
    } else {
      query("INSERT INTO ua_sites (url) VALUES ('$url')");
      $site_id = mysql_insert_id($conn);
    }
    $res = query("SELECT visits FROM ua_visits WHERE ipid=$ip_id AND siteid=$site_id");
    if (@mysql_num_rows($res)) {
      query("UPDATE ua_visits SET visits=visits+1, lastvisit='$now' WHERE ipid=$ip_id AND siteid=$site_id");
    } else {
      query("INSERT INTO ua_visits (ipid, siteid, visits, lastvisit) VALUES ($ip_id, $site_id, 1, '$now')");
    }  
  }
   mysql_close($conn);
  
?>

Speichern als botviewer.php:

<?php

  require 'bc_config.php';

  define('BC_DEBUG', false);  //Show all executed Queries - only true for debug!

  $conn = mysql_connect(BC_MYSQLHOST, BC_MYSQLUSER, BC_MYSQLPASS) or die(mysql_error());
  mysql_select_db(BC_MYSQLDB, $conn);
  
  $logged = $_COOKIE['bc_login'] == sha1(BC_USER).sha1(BC_PASSWORD);

  /*basic login*/
  if (strlen(trim($_POST['bc_user'])) && strlen(trim($_POST['bc_password']))) {
    if (trim($_POST['bc_user']) == BC_USER && trim($_POST['bc_password']) == BC_PASSWORD) {
      setcookie('bc_login', sha1(trim($_POST['bc_user'])).sha1(trim($_POST['bc_password'])), time()+3600, '/', '');
      header("Location: ". $_SERVER['HTTP_REFERER']);
    }
  } elseif (strlen(trim($_POST['bc_logout']))) {
    setcookie('bc_login', '-', time()-3600, '/', '');
    header("Location: ". $_SERVER['HTTP_REFERER']);
  }
  
    
  if ($logged) {
    if ($_POST['changeindex'] > 0) {
      $siteid = $_POST['changeindex'];
      $noindex = $_POST['index'] == 1 ? '0' : '1';
      query("UPDATE ua_sites SET noindex='$noindex' WHERE id=$siteid"); 
      header("Location: ". $_SERVER['HTTP_REFERER'].'#site'.$siteid);
    } elseif ($_POST['changetype'] > 0) {
      $ua_id = $_POST['changetype'];
      $type = $_POST['newtype'];
      if ($type == 'B') {
        $ipres = query("SELECT id, ip FROM ua_ips WHERE uaid=$ua_id ORDER BY uaid");
        $ipids = array();
        if (@mysql_num_rows($ipres)) {
          while (list($ipid, $ip) = mysql_fetch_row($ipres)) {
            $ipids[] = $ipid;
          }
        }
        if (!empty($ipids)) {
          $ipin = '('.implode(',', $ipids).')';
          query("DELETE FROM ua_visits WHERE ipid IN $ipin");
        }
        query("DELETE FROM ua_ips WHERE uaid=".$ua_id);
      }
      query("UPDATE ua_pool SET type='$type' WHERE id=$ua_id");
      header("Location: ". $_SERVER['HTTP_REFERER'].'#ua'.$ua_id);
    }
  }
  
  function query($query, $show=BC_DEBUG) {
    global $conn;
    $res = mysql_query($query, $conn);
    if( mysql_errno($conn)) {
      die('<p class="mysql_error">'.mysql_error($conn).'</p>');
    }
    if ($show) echo '<p class="mysql_debug">'.$query.'</p>';
    return $res;
  }
  
  $now = mktime();
  
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <meta http-equiv="content-type" content="text/html;  charset=ISO-8859-1" />
  <title>Bot-Viewer on <?php echo $_SERVER['HTTP_HOST'] ?></title>
  <link rel="stylesheet" href="botview.css" type="text/css" />
</head>
<body>
<h1>Bot-Viewer on <?php echo $_SERVER['HTTP_HOST'] ?></h1>
<div id="login">
<form method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>">
<?php if (!$logged)  { ?>
<p>
<label for="bc_user">Username:</label>
<input type="text" id="bc_user" name="bc_user" />
<label for="bc_user">Password:</label>
<input type="password" id="bc_password" name="bc_password" />
<input type="submit" value="Login" />
</p>
<?php } else { ?>
<p><input type="submit" name="bc_logout" value="Logout" /></p>
<?php } ?>
</form>
</div>
<div id="kr">
<h2>Known Bots</h2>
<?php
  $res = query("SELECT id, user_agent FROM ua_pool WHERE type='R' ORDER BY user_agent");
  if (@mysql_num_rows($res)) { 
    while(list($id, $ua) = mysql_fetch_row($res)) {
      $ipres = query("SELECT id, ip FROM ua_ips WHERE uaid=$id ORDER BY uaid");
      $ipids = array();
      if (@mysql_num_rows($ipres)) {
        while (list($ipid, $ip) = mysql_fetch_row($ipres)) {
          $ipids[] = $ipid;
        }
      }
      ?>
<h3><a id="ua<?php echo $id; ?>"><?php echo stripslashes($ua) ?></a></h3>
<?php if ($logged) { ?>
<div class="categorize">
  <form method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>#ua<?php echo $id ?>">
   <p>
     <input type="hidden" name="changetype" value="<?php echo $id ?>" />
     <button type="submit" name="newtype" value="B">Browser</button>
     <button type="submit" name="newtype" value="U">Unknown</button>
   </p>
  </form>
</div>
<?php } ?>
  <div class="visits">
<?php
     if (!empty($ipids)) {
       $ipin = '('.implode(',', $ipids).')';
       ?>
    <table>
      <tr>
        <th class="ip">IP</th>
        <th class="url">URL</th>
        <th class="visits">Visits</th>
        <th class="lastvisit">Last visit</th>
      </tr>
<?php
       $resvisits = query('SELECT ip, visits, lastvisit, url, noindex FROM ua_visits, ua_sites, ua_ips WHERE siteid = ua_sites.id AND ipid = ua_ips.id AND ipid IN '.$ipin.' ORDER BY ip, lastvisit'); 
       if (@mysql_num_rows($resvisits)) {
       list($actip, $showip, $rowclass) = array(NULL, NULL, NULL);
       while(list($ip, $visits, $lastvisit, $url, $noindex) = mysql_fetch_row($resvisits)) {
          $showip = ($actip != $ip) ? $ip : NULL;
         if (!is_null($showip))  { 
            $actip = $ip;
            $rowclass = is_null($rowclass) ? ' class="contrast"' : NULL;
         } 
         if (!$noindex && (($now - strtotime($lastvisit)) / 86400) <= 1) {
           $rowclass = ' class="newvisit"';
         }

      ?>
      <tr<?php echo $noindex == 0 ? $rowclass : ' class="badboy"' ?>>
        <td><?php echo($showip) ?></td>
        <td><?php echo($url) ?></td>
        <td><?php echo($visits) ?></td>
        <td><?php echo($lastvisit) ?></td>
      </tr>
<?php 
       }
    } ?>
    </table>
<?php } else {?>
  <p><strong>No indexed Pages!</strong></p>
<?php }  ?>
  </div><!--visits-->
<?php
   } 
 } else { ?>
<p><strong>No known Bot found.</strong></p>
<?php } ?>
</div><!--kr-->
<div id="uua">
<h2>Unknown UAs</h2>
<?php
  $res = query("SELECT id, user_agent FROM ua_pool WHERE type='U' ORDER BY user_agent");
  if (@mysql_num_rows($res)) { 
    while(list($id, $ua) = mysql_fetch_row($res)) {
?>
<h3><a id="ua<?php echo $id; ?>"><?php echo stripslashes($ua) ?></a></h3>
<?php if ($logged) { ?>
<div class="categorize">
  <form method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>#ua<?php echo $id ?>">
   <p>
     <input type="hidden" name="changetype" value="<?php echo $id ?>" />
     <button type="submit" name="newtype" value="B">Browser</button>
     <button type="submit" name="newtype" value="R">Bot</button>
   </p>
  </form>
</div>
<?php } ?>
<div class="visits">
<?php 
    $ipres = query("SELECT id, ip FROM ua_ips WHERE uaid=$id ORDER BY uaid");
    $ipids = array();
    if (@mysql_num_rows($ipres)) {
      $ipids = array();
      if (@mysql_num_rows($ipres)) {
        while (list($ipid, $ip) = mysql_fetch_row($ipres)) {
          $ipids[] = $ipid;
        }
      }
    }
    if (!empty($ipids)) {
      $ipin = '('.implode(',', $ipids).')';
?>
<table>
<tr>
 <th class="ip">IP</th>
 <th class="url">URL</th>
 <th class="visits">Visits</th>
 <th class="lastvisit">Last visit</th>
</tr>
<?php
  $resvisits = query('SELECT ip, visits, lastvisit, url, noindex FROM ua_visits, ua_sites, ua_ips WHERE siteid = ua_sites.id AND ipid = ua_ips.id AND ipid IN '.$ipin.' ORDER BY ip, lastvisit'); 
  if (@mysql_num_rows($resvisits)) {
    list($actip, $showip, $rowclass) = array(NULL, NULL, NULL);
    while(list($ip, $visits, $lastvisit, $url, $noindex) = mysql_fetch_row($resvisits)) {
      $showip = ($actip != $ip) ? $ip : NULL;
         if (!is_null($showip))  { 
            $actip = $ip;
            $rowclass = is_null($rowclass) ? ' class="contrast"' : NULL;
         } 
         if (!$noindex && (($now - strtotime($lastvisit)) / 86400) <= 1) {
           $rowclass = ' class="newvisit"';
         }
?>
<tr<?php echo $noindex == 0 ? $rowclass : ' class="badboy"' ?>>
<td><?php echo($showip) ?></td>
<td><?php echo($url) ?></td>
<td><?php echo($visits) ?></td>
<td><?php echo($lastvisit) ?></td>
</tr>
<?php 
   }
  } ?>
</table>
<?php } else {?>
 <strong>No indexed Pages!</strong>
<?php } ?>
</div><!--visits-->
<?php
    } 
?>
<?php } else { ?>
<p><strong>No unknown User-Agent found.</strong></p>
<?php } ?>
</div><!--uua-->
<div id="kb">
<h2>Known Browsers</h2>
<?php
  $res = query("SELECT id, user_agent FROM ua_pool WHERE type='B' ORDER BY user_agent");
  if (@mysql_num_rows($res)) { 
   while(list($id, $ua) = mysql_fetch_row($res)) {
?>
<h3><a id="ua<?php echo $id; ?>"><?php echo stripslashes($ua) ?></a></h3>
<?php if ($logged) { ?>
<div class="categorize">
  <form method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>#ua<?php echo $id ?>">
   <p>
     <input type="hidden" name="changetype" value="<?php echo $id ?>" />
     <button type="submit" name="newtype" value="R">Bot</button>
     <button type="submit" name="newtype" value="U">Unknown</button>
   </p>
  </form>
</div>
<?php } 
    }
 } else { ?>
<p><strong>No known Browser found.</strong></p>
<?php } ?>
</div><!--kb-->

<div id="pr">
<h1>Known sites on <?=$_SERVER['SERVER_NAME']?></h1>
<?php
  $res = query("SELECT id, url, noindex FROM ua_sites ORDER BY url");
  if (@mysql_num_rows($res)) {
?>
<table>
  <tr>
    <th class="url">URL</th>
    <th class="noindex">Index</th>
  </tr>
<?php
   $counter = 0;
   while (list($id, $url, $noindex) = mysql_fetch_row($res)) {
   $rowclass = ($counter++ % 2 == 0) ? ' class="contrast"' : '';
   if ($noindex) $rowclass=' class="excluded"';
?>
<tr<?php echo $rowclass ?>>
  <td><a id="site<?php echo $id ?>"><?php echo $url ?></a></td>
  <td>
<?php if ($logged) { ?>
    <form method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>#site<?php echo $id ?>">
     <p>
       <input type="checkbox" name="index" value="1" onchange="this.form.submit()" <?php echo $noindex ? '' : ' checked="checked"'; ?> />
       <input type="hidden" name="changeindex" value="<?php echo $id ?>" />
     </p>
    </form>
<?php } else { 
     echo $noindex ? 'no' : 'yes'; 
} ?></td>
</tr>
<?php      
    }
  }
?>
</table>
</div><!--pr-->

</body>
</html>
<?php mysql_close($conn) ?>

Speichern als 'botview.css':

body { font-family: Arial, Helvetica, sans-serif; font-size: 12px; }
h1 { font-size: 20px; }
h2 { margin: 15px 0 5px 0; font-size: 16px; }
h3 { font-size: 14px; }
table { width: 100%; border: 1px dotted; background: #F5F5F5; }
td { white-space: nowrap; }
th.ip { width: 8em; }
th.visits { width: 5em; }
th.lastvisit { width: 14em; }
th { background: #D3D3D3; text-align: left; }
th.indexed { background: #FFFFFF; }
p strong { color: #FF0000; }
td.ua { color: #0000FF; font-size: 13px; }
table td table { margin-bottom: 10px; background: #FFFFFF; }
div.categorize form,
div.categorize form p{ margin: 0; padding: 0; }
div#kb div.categorize { margin-bottom: 5px; border-bottom:1px solid green; padding-bottom:3px; }
div#kr tr { background: #FFFFE0; }
div#kr tr.contrast { background: white; }
div#kr h3 { margin-bottom: 3px; color: #B22222; }
div#kr table { background: white; }
div#kr th { background: #FFD700; }
div#kb h3 { margin: 3px; color: green; font-size: 13px; }
div#uua h3 { margin-bottom: 3px; color: #00008B; }
div#uua table { background: white; }
div#uua tr { background: #F0F8FF; }
div#uua tr.contrast { background: white; }
div#uua th { background: #87CEEB; }
div#kr tr.badboy,
div#uua tr.badboy { background: Red; color: white; }
div#pr table { background: white; }
div#pr table th.noindex { width: 10em; }
div#pr table th { background: #2E8B57; color: white; }
div#pr table tr{ background: #CAE1CD; }
div#pr table tr.contrast{ background: white; }
div#pr table tr.excluded{ background: Yellow; }
div#pr table td form,
div#pr table td form p,
div#pr table td form input { padding: 0; margin: 0; }
div#kr table tr.newvisit td{  background:#FFE4B5;}
div#kr table tr:hover td,
div table tr:hover td {background:#FFE4E1;}

Update 29.04:
- Die Bot-Besuche der letzten 24 Stunden werden hervorgehoben
- Div-Schachtelungsfehler korrigiert
(geändert: botviewer.php, botview.css)

28.04.2006 01:06 | geändert: 29.04.2006 14:51

23 Mario

guten Morgen Rudi

wow, das ging aber speditiv. Herzlichen Dank, ich werde den botcacher dann mal auf ein paar Domains installieren und die Daten sammeln.

Mario

28.04.2006 07:37

24 Mario

Ich möchte lieber ein eigenes Verzeichnis unter der web-root erstellen, das dann alle cacher-Dateien enthält. Wo überall muss ich die Pfade ändern?

Mario

28.04.2006 09:29

25 Mario

das Verzeichnis soll botcacher heissen, dort sollen alle 4 Dateien liegen...

Warum?

1. ist es logischer anwendungsorientierte Verzeichnisse zu schaffen
2. möchte ich das Verzeichnis in der robots.txt für die bots sperren
3. wer sich nicht daran hält, tappt in die spider-trap

also Gründe genug, eine eigenes Verzeichnis für die botcacher-Dateien zu erstellen

Mario

28.04.2006 12:47

26 C)-(iLL@

Ich möchte lieber ein eigenes Verzeichnis unter der web-root erstellen, das dann alle cacher-Dateien enthält. Wo überall muss ich die Pfade ändern?
Nirgends. Es ist egal, wo Du die Skripts hintust, solange sie beieinander bleiben. Ich sagte
Zitat von mir
(ich schlage die Server-Root vor, ist aber nicht zwingend notwendig).

Das meinte ich auch so :) Sag, wenns Probleme gibt - glaub ich aber nicht. Das mit dem Sperren ist gut. Du hast gesehen, wie das im viewer funktioniert?

28.04.2006 13:06

27 Jörg Kruse

Zitat von Mario
das Verzeichnis soll botcacher heissen

Eines Tages werden die Bots so intelligent sein, und Verzeichnisse und Dateien, die 'bottrap' oder ähnlich heißen, nicht zu spidern

28.04.2006 13:16

28 Mario

ok ok, auch nicht das include?

include '/botcacher/botcatcher.php';

;)



Mario

28.04.2006 13:18

29 Mario

die bleiben für ewig dumm :)) , denn sonst wäre das viel zu schön

Mario

28.04.2006 13:19 | geändert: 28.04.2006 13:20

30 Jörg Kruse

Von etwaigen intelligenten bekommst du u.U. gar nichts mit ;) Der Pfad für das Include ist egal. Aber die URL für die Bottrap würde ich neutral bezeichnen

28.04.2006 13:23