如何检测网站虚假用户(爬虫)与cURL请求

原创 tiangr  2016-10-29 16:11  阅读 12,353 次

http://stackoverflow.com/questions/12257584/how-to-detect-fake-users-crawlers-and-curl

还没有任何一种方法可以避免自动抓取网页的爬虫,每一个人类可以做的事情,理论上机器都可以。我们能做的只有将爬虫抓取网页变得更困难,困难到只有少数Geek可以通过它。事实上,想要更深入的理解防爬虫/垃圾流量,只有自己开发爬虫。


Sessions per IP/单个IP创建session数量

如果一个用户在一分钟内,新建了50次新的session,你就可以判定,这是一个没有处理cookie的爬虫。当然,curl可以非常有效的管理cookies,但是如果你结合 单个用户/会话的访问计数器(后面会进行解释),或者访问爬虫开发者还是个初出茅庐的菜鸟,不懂如何处理cookie,这种方法就变得非常有效。

很难想象同时有50个用户使用共享的连接同时访问你的网站(当然,这取决于你服务器的带宽流量),你需要在这种情况下显示锁定页面,直到访问者通过验证码验证。

1.首先,创建两张表:一张表用户存储禁止的IP,另一张表用户保存IP和Sessions。

  1. create table if not exists sessions_per_ip (
  2.   ip int unsigned,
  3.   session_id varchar(32),
  4.   creation timestamp default current_timestamp,
  5.   primary key(ip, session_id)
  6. );
  7. create table if not exists banned_ips (
  8.   ip int unsigned,
  9.   creation timestamp default current_timestamp,
  10.   primary key(ip)
  11. );

2.在脚本开始之前需要删除掉过期的记录。

3.接下来验证访问者的IP地址是否被禁。

4.如果没有,计算出该IP地址下新建了多少sessions。

5.如果新建的sessions超过一定数量,将其插入到禁止访问的 banned_ips 表中标记。

6.如果该IP地址和session没有插入到sessions_per_ip表中,则插入记录
代码示例:

  1. <?php
  2. try
  3. {
  4.     // Some configuration (small values for demo)
  5.     $max_sessions = 5; // 5 sessions/ip simultaneousely allowed
  6.     $check_duration = 30; // 30 secs max lifetime of an ip on the sessions_per_ip table
  7.     $lock_duration = 60; // time to lock your website for this ip if max_sessions is reached
  8.     // Mysql connection
  9.     require_once("config.php");
  10.     $dbh = new PDO("mysql:host={$host};dbname={$base}"$user$password);
  11.     $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  12.     // Delete old entries in tables
  13.     $query = "delete from sessions_per_ip where timestampdiff(second, creation, now()) > {$check_duration}";
  14.     $dbh->exec($query);
  15.     $query = "delete from banned_ips where timestampdiff(second, creation, now()) > {$lock_duration}";
  16.     $dbh->exec($query);
  17.     // Get useful info attached to our user...
  18.     session_start();
  19.     $ip = ip2long($_SERVER['REMOTE_ADDR']);
  20.     $session_id = session_id();
  21.     // Check if IP is already banned
  22.     $banned = false;
  23.     $count = $dbh->query("select count(*) from banned_ips where ip = '{$ip}'")->fetchColumn();
  24.     if ($count > 0)
  25.     {
  26.         $banned = true;
  27.     }
  28.     else
  29.     {
  30.         // Count entries in our db for this ip
  31.         $query = "select count(*)  from sessions_per_ip where ip = '{$ip}'";
  32.         $count = $dbh->query($query)->fetchColumn();
  33.         if ($count >= $max_sessions)
  34.         {
  35.             // Lock website for this ip
  36.             $query = "insert ignore into banned_ips ( ip ) values ( '{$ip}' )";
  37.             $dbh->exec($query);
  38.             $banned = true;
  39.         }
  40.         // Insert a new entry on our db if user's session is not already recorded
  41.         $query = "insert ignore into sessions_per_ip ( ip, session_id ) values ('{$ip}', '{$session_id}')";
  42.         $dbh->exec($query);
  43.     }
  44.     // At this point you have a $banned if your user is banned or not.
  45.     // The following code will allow us to test it...
  46.     // We do not display anything now because we'll play with sessions :
  47.     // to make the demo more readable I prefer going step by step like
  48.     // this.
  49.     ob_start();
  50.     // Displays your current sessions
  51.     echo "Your current sessions keys are : <br/>";
  52.     $query = "select session_id from sessions_per_ip where ip = '{$ip}'";
  53.     foreach ($dbh->query($queryas $row) {
  54.         echo "{$row['session_id']}<br/>";
  55.     }
  56.     // Display and handle a way to create new sessions
  57.     echo str_repeat('<br/>', 2);
  58.     echo '<a href="' . basename(__FILE__) . '?new=1">Create a new session / reload</a>';
  59.     if (isset($_GET['new']))
  60.     {
  61.         session_regenerate_id();
  62.         session_destroy();
  63.         header("Location: " . basename(__FILE__));
  64.         die();
  65.     }
  66.     // Display if you're banned or not
  67.     echo str_repeat('<br/>', 2);
  68.     if ($banned)
  69.     {
  70.         echo '<span style="color:red;">You are banned: wait 60secs to be unbanned... a captcha must be more friendly of course!</span>';
  71.         echo '<br/>';
  72.         echo '<img src="http://4.bp.blogspot.com/-PezlYVgEEvg/TadW2e4OyHI/AAAAAAAAAAg/QHZPVQcBNeg/s1600/feu-rouge.png" />';
  73.     }
  74.     else
  75.     {
  76.         echo '<span style="color:blue;">You are not banned!</span>';
  77.         echo '<br/>';
  78.         echo '<img src="http://identityspecialist.files.wordpress.com/2010/06/traffic_light_green.png" />';
  79.     }
  80.     ob_end_flush();
  81. }
  82. catch (PDOException $e)
  83. {
  84.     /*echo*/ $e->getMessage();
  85. }
  86. ?>

Visit Counter/访问计数器

前文提到过 访问计数器,如果你的用户使用相同的cookie来抓取你的页面,你可以根据它的session来屏蔽。原理十分简单,一个正常的用户是不会再60秒内连续访问60个页面的。

  1. 在用户的session中创建一个包含了访问时间戳的数组。
  2. 在session中保存的此数组中移除超过指定时间的访问记录。
  3. 为每一次新的访问记录一条新记录。
  4. 计算此数组的记录数
  5. 当超过指定的访问页面数时屏蔽掉此IP地址。

代码示例:

  1. <?php
  2. $visit_counter_pages = 5; // maximum number of pages to load
  3. $visit_counter_secs = 10; // maximum amount of time before cleaning visits
  4. session_start();
  5. // initialize an array for our visit counter
  6. if (array_key_exists('visit_counter', $_SESSION) == false)
  7. {
  8.     $_SESSION['visit_counter'] = array();
  9. }
  10. // clean old visits
  11. foreach ($_SESSION['visit_counter'] as $key => $time)
  12. {
  13.     if ((time() - $time) > $visit_counter_secs) {
  14.         unset($_SESSION['visit_counter'][$key]);
  15.     }
  16. }
  17. // we add the current visit into our array
  18. $_SESSION['visit_counter'][] = time();
  19. // check if user has reached limit of visited pages
  20. $banned = false;
  21. if (count($_SESSION['visit_counter']) > $visit_counter_pages)
  22. {
  23.     // puts ip of our user on the same "banned table" as earlier...
  24.     $banned = true;
  25. }
  26. // At this point you have a $banned if your user is banned or not.
  27. // The following code will allow us to test it...
  28. echo '<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>';
  29. // Display counter
  30. $count = count($_SESSION['visit_counter']);
  31. echo "You visited {$count} pages.";
  32. echo str_repeat('<br/>', 2);
  33. echo <<< EOT
  34. <a id="reload" href="#">Reload</a>
  35. <script type="text/javascript">
  36.   $('#reload').click(function(e) {
  37.     e.preventDefault();
  38.     window.location.reload();
  39.   });
  40. </script>
  41. EOT;
  42. echo str_repeat('<br/>', 2);
  43. // Display if you're banned or not
  44. echo str_repeat('<br/>', 2);
  45. if ($banned)
  46. {
  47.     echo '<span style="color:red;">You are banned! Wait for a short while (10 secs in this demo)...</span>';
  48.     echo '<br/>';
  49.     echo '<img src="http://4.bp.blogspot.com/-PezlYVgEEvg/TadW2e4OyHI/AAAAAAAAAAg/QHZPVQcBNeg/s1600/feu-rouge.png" />';
  50. }
  51. else
  52. {
  53.     echo '<span style="color:blue;">You are not banned!</span>';
  54.     echo '<br/>';
  55.     echo '<img src="http://identityspecialist.files.wordpress.com/2010/06/traffic_light_green.png" />';
  56. }
  57. ?>

An image to download/图片下载标记

当爬虫想要在尽可能短的时间内进行大量数据抓取时,是不会下载网页上图片的,因为它耗费了过多的带宽,也让爬虫抓取变得更慢了。

此方法非常巧妙,也非常容易接入,使用mod_rewrite (URL重写模块) ,将PHP脚本的后缀伪装成 .jpg/.png/ 后缀的图片文件。此文件应该放置于每一个你想要保护的页面上:比如Logo,尽量挑选尺寸较小的图片(因为这张图片将不会被缓存)。

1.添加URL重写配置至你的 .htaccess 文件:

  1. RewriteEngine On
  2. RewriteBase /tests/anticrawl/
  3. RewriteRule ^logo\.jpg$ logo.php

2.创建你的 logo.php 文件

  1. <?php
  2. // start session and reset counter
  3. session_start();
  4. $_SESSION['no_logo_count'] = 0;
  5. // forces image to reload next time
  6. header("Cache-Control: no-store, no-cache, must-revalidate");
  7. // displays image
  8. header("Content-type: image/jpg");
  9. readfile("logo.jpg");
  10. die();

3.用户到达每个页面,session中的no_logo_count计数将递增1,检查此计数是否到达了你设置的限制值。

代码示例:

  1. <?php
  2. $no_logo_limit = 5; // number of allowd pages without logo
  3. // start session and initialize
  4. session_start();
  5. if (array_key_exists('no_logo_count', $_SESSION) == false)
  6. {
  7. $_SESSION['no_logo_count'] = 0;
  8. }
  9. else
  10. {
  11. $_SESSION['no_logo_count']++;
  12. }
  13. // check if user has reached limit of "undownloaded image"
  14. $banned = false;
  15. if ($_SESSION['no_logo_count'] >= $no_logo_limit)
  16. {
  17. // puts ip of our user on the same "banned table" as earlier...
  18. $banned = true;
  19. }
  20. // At this point you have a $banned if your user is banned or not.
  21. // The following code will allow us to test it...
  22. echo '<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>';
  23. // Display counter
  24. echo "You did not loaded image {$_SESSION['no_logo_count']} times.";
  25. echo str_repeat('<br/>', 2);
  26. // Display "reload" link
  27. echo <<< EOT
  28. <a id="reload" href="#">Reload</a>
  29. <script type="text/javascript">
  30.   $('#reload').click(function(e) {
  31.     e.preventDefault();
  32.     window.location.reload();
  33.   });
  34. </script>
  35. EOT;
  36. echo str_repeat('<br/>', 2);
  37. // Display "show image" link : note that we're using .jpg file
  38. echo <<< EOT
  39. <div id="image_container">
  40.     <a id="image_load" href="#">Load image</a>
  41. </div>
  42. <br/>
  43. <script type="text/javascript">
  44. // On your implementation, you'llO of course use <img src="logo.jpg" />
  45.   $('#image_load').click(function(e) {
  46.     e.preventDefault();
  47.     $('#image_load').html('<img src="logo.jpg" />');
  48.   });
  49. </script>
  50. EOT;

Cookie check/Cookie 检查

你可以在前端使用JavaScript创建cookie,然后在服务器端检查cookie是否有值,(使用cURL的爬虫不会执行页面上的JS代码。)

代码示例:

1.设置一个 $_SESSION 值为1,在每次访问一个新页面时,都递增1。

2.如果cookie不存在(使用JavaScript设置),将此$_SESSION的值设置为0。

3.如果这个值达到一定上限,禁止用户访问。

代码示例:

  1. <?php
  2. $no_cookie_limit = 5; // number of allowd pages without cookie set check
  3. // Start session and reset counter
  4. session_start();
  5. if (array_key_exists('cookie_check_count', $_SESSION) == false)
  6. {
  7.     $_SESSION['cookie_check_count'] = 0;
  8. }
  9. // Initializes cookie (note: rename it to a more discrete name of course) or check cookie value
  10. if ((array_key_exists('cookie_check', $_COOKIE) == false) || ($_COOKIE['cookie_check'] != 42))
  11. {
  12.     // Cookie does not exist or is incorrect...
  13.     $_SESSION['cookie_check_count']++;
  14. }
  15. else
  16. {
  17.     // Cookie is properly set so we reset counter
  18.     $_SESSION['cookie_check_count'] = 0;
  19. }
  20. // Check if user has reached limit of "cookie check"
  21. $banned = false;
  22. if ($_SESSION['cookie_check_count'] >= $no_cookie_limit)
  23. {
  24.     // puts ip of our user on the same "banned table" as earlier...
  25.     $banned = true;
  26. }
  27. // At this point you have a $banned if your user is banned or not.
  28. // The following code will allow us to test it...
  29. echo '<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>';
  30. // Display counter
  31. echo "Cookie check failed {$_SESSION['cookie_check_count']} times.";
  32. echo str_repeat('<br/>', 2);
  33. // Display "reload" link
  34. echo <<< EOT
  35. <br/>
  36. <a id="reload" href="#">Reload</a>
  37. <br/>
  38. <script type="text/javascript">
  39.   $('#reload').click(function(e) {
  40.     e.preventDefault();
  41.     window.location.reload();
  42.   });
  43. </script>
  44. EOT;
  45. // Display "set cookie" link
  46. echo <<< EOT
  47. <br/>
  48. <a id="cookie_link" href="#">Set cookie</a>
  49. <br/>
  50. <script type="text/javascript">
  51.   // On your implementation, you'll of course put the cookie set on a $(document).ready()
  52.   $('#cookie_link').click(function(e) {
  53.     e.preventDefault();
  54.     var expires = new Date();
  55.     expires.setTime(new Date().getTime() + 3600000);
  56.     document.cookie="cookie_check=42;expires=" + expires.toGMTString();
  57.   });
  58. </script>
  59. EOT;
  60. // Display "unset cookie" link
  61. echo <<< EOT
  62. <br/>
  63. <a id="unset_cookie" href="#">Unset cookie</a>
  64. <br/>
  65. <script type="text/javascript">
  66.   // On your implementation, you'll of course put the cookie set on a $(document).ready()
  67.   $('#unset_cookie').click(function(e) {
  68.     e.preventDefault();
  69.     document.cookie="cookie_check=;expires=Thu, 01 Jan 1970 00:00:01 GMT";
  70.   });
  71. </script>
  72. EOT;
  73. // Display if you're banned or not
  74. echo str_repeat('<br/>', 2);
  75. if ($banned)
  76. {
  77.     echo '<span style="color:red;">You are banned: click on "Set cookie" and reload...</span>';
  78.     echo '<br/>';
  79.     echo '<img src="http://4.bp.blogspot.com/-PezlYVgEEvg/TadW2e4OyHI/AAAAAAAAAAg/QHZPVQcBNeg/s1600/feu-rouge.png" />';
  80. }
  81. else
  82. {
  83.     echo '<span style="color:blue;">You are not banned!</span>';
  84.     echo '<br/>';
  85.     echo '<img src="http://identityspecialist.files.wordpress.com/2010/06/traffic_light_green.png" />';
  86. }

Protection against proxies/保护代理

使用代理访问网站有以下几种情况:

1.正常的代理可以显示用户的连接信息(比如IP地址)

2.匿名代理不会显示IP地址,但是会在头部给出关于代理的使用信息

3.高匿代理不会显示IP地址,也不会发送任何信息至浏览器

代理很容易找到,但是高匿代理却很少。

如果用户使用代理访问,$_SERVER变量可能包含如下一些信息:

  • CLIENT_IP
  • FORWARDED
  • FORWARDED_FOR
  • FORWARDED_FOR_IP
  • HTTP_CLIENT_IP
  • HTTP_FORWARDED
  • HTTP_FORWARDED_FOR
  • HTTP_FORWARDED_FOR_IP
  • HTTP_PC_REMOTE_ADDR
  • HTTP_PROXY_CONNECTION'
  • HTTP_VIA
  • HTTP_X_FORWARDED
  • HTTP_X_FORWARDED_FOR
  • HTTP_X_FORWARDED_FOR_IP
  • HTTP_X_IMFORWARDS
  • HTTP_XROXY_CONNECTION
  • VIA
  • X_FORWARDED
  • X_FORWARDED_FOR

PHP:判断客户端是否使用代理服务器及其匿名级别


总结

还有很多方法检测爬虫,但是你需要准确定义自己网站的用途以确保自己不会被伪装的"正常"用户。

特别提示:本站资源全部免费下载,因服务器需经费维护,文中部分外链点击后会进入广告,请耐心等待5秒即可跳过广告进入目标页面。如遇页面外链打不开或下载地址失效,您可以在评论中指出错误,或扫描页面底部二维码。
本文地址:http://www.tiangr.com/ru-he-jian-ce-wang-zhan-xu-jia-yong-hu-pa-chong-curl.html
版权声明:本文为原创文章,版权归 tiangr 所有,欢迎分享本文,转载请保留出处!

发表评论


表情