本文已參與好文召集令活動,點擊查看:後端、大前端雙賽道投稿,2萬元獎池等你挑戰!php
做爲一個長期從事seo工做的開發者,網站的原創資源就是咱們的資本,但很難避免被其餘爬蟲程序,幾個小時以內所有爬走,與爬蟲之間的博弈也就變成了一場持久的拉鋸戰。在長期的博弈中,也總結了一個比較實用的反爬蟲方法,雖然不能保證防住百分之百的爬蟲程序,但也能防住大部分,或者增長爬蟲者的採集成本。前端
識別爬蟲程序最主要的標識有兩個:python
一、user-agent,用戶頭信息,正常的用戶訪問都會帶有瀏覽器信息,例如redis
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36
複製代碼
爬蟲程序在請求的時候,若是沒有刻意僞造,會帶上所使用的語言標識,例如python request請求頭信息爲:算法
python-requests/2.25.1
複製代碼
PHP curl請求,若是php.ini配置文件中的user_agent打開的話會爲: PHP
,沒打開會沒有user-agent數據庫
user-agent的僞造應該是最簡單也是0成本的,因此除非是特別明顯的標識直接攔截,通常不根據這個去作限制。後端
二、ip,用戶訪問都會攜帶用戶ip,咱們能夠根據同一個ip在短期內訪問的次數去判斷是否是爬蟲程序,固然ip也可使用代理ip進行僞造。數組
主要利用kafka高併發和穩定的特性,在不影響業務的前提下,實現訪問量的統計。用redis的有序列表,統計出指定時間段內訪問量最高的ip,達到預警的目的。 以PHP網站爲例瀏覽器
一、在網站程序的入口處判斷用戶IP是否在違禁IP的Redis集合裏markdown
$ip = Tools::getClientIp();
$res = $redis->get('ip:f:' . $ip);
if ($res) {
return false;
}
複製代碼
Tools::getClientIp()的代碼爲:
public static function getClientIp() {
$ip = '';
if (getenv('HTTP_CLIENT_IP')) {
$ip = getenv('HTTP_CLIENT_IP');
} else if (getenv('HTTP_X_FORWARDED_FOR')) {
$ip = getenv('HTTP_X_FORWARDED_FOR');
$ips = explode(',', $ip);
$ip = $ips[0];
} else if (getenv('REMOTE_ADDR')) {
$ip = getenv('REMOTE_ADDR');
}
return $ip;
}
複製代碼
二、將合法的ip推送到kafka消息隊列。不用kafka也能夠,可使用其餘的消息隊列,例如RabbitMq,但因爲是網站入口,併發量比較大,kafka在穩定性和處理大併發量上具備更大的優點,因此選擇了kafka。
三、在kafka消費者程序中,將接收到的用戶ip推到redis以小時爲單位的有序集合中,這一步主要是爲了統計每一個時間段,訪問量最高的ip,經過人爲觀察或者腳本定時統計,封禁不合法的ip或者發郵件提醒。
// 集合key
$key = 'list:' . date('YmdH');
$redis->zincrby($key, 1, $ip);
複製代碼
接下來使用redis漏斗添加限制不正常ip的訪問速率。
Redis4.0以後提供了一個限流模塊:redis-cell,該模塊使用漏斗算法並提供原子性的限流指令。 這個模塊須要單獨安裝,具體安裝方法可參照網上的一些安裝方法,這裏就不細說了。 說一下這個模塊的參數
> cl.throttle 127.0.0.1 14 30 60 1
1) (integer) 0 # 0表示容許,1表示拒絕
2) (integer) 15 # 漏斗容量capacity
3) (integer) 14 # 漏斗剩餘空間left_quota
4) (integer) -1 # 若是拒絕了,須要多長時間後再重試,單位秒
5) (integer) 2 # 多長時間後,漏斗徹底空出來,單位秒
複製代碼
該指令意思爲,容許127.0.0.1的ip 行爲的頻率爲每60s最多30次,漏斗初始容量爲15(由於是從0開始計數,到14爲15個),默認每一個行爲佔據的空間爲1(可選參數)。 若是被拒絕,取返回數組的第四個值進行sleep便可做爲重試時間,也能夠異步定時任務來重試。
$res = $redis->executeRaw(['cl.throttle', $ip, 14, 30, 60]);
if ($res[0]) {
$redis->setex('ip:f:' . $ip, $res[3], 1);
}
複製代碼
一、天天各個時間段ip訪問次數的有序集合數據,訪問量最高的數據應按期寫入到數據庫,並按期刪除。
二、ip漏斗限流是個很是危險的操做,仍是應該常常觀察訪問日誌找出爬蟲程序的規律,肯定合理的限流參數。