如何给网站加入优雅的反爬虫策略

你的网站内容很有价值,希望被google,百度等正规搜索引擎爬虫收录,却不想让那些无节操的山寨爬虫把你的数据扒走坐享其成。本文将探讨如何在网站中加入优雅的反爬虫策略。

【思路】

反爬虫策略要考虑以下几点:

  1. 能被google、百度等正规搜索引擎爬虫抓取,不限流量和并发数;
  2. 阻止山寨爬虫的抓取;
  3. 反爬虫策略应该是实时检测的,而不是通过一段时间后的访问统计分析得出;
  4. 误判后的人性化处理(优雅之所在);

大部分的爬虫不是以浏览器方式来访问页面的,爬虫只下载网页的html源代码,不加载包含在页面中的js/css/图片,这是区分爬虫与否的一个关键。一个请求被识别出来不是浏览器访问,一定是爬虫,为了满足上面所说的第1点和第2点,进一步对http头agent进行验证,是否标记为google、百度的spider,严格一点的话应该判别来源IP是否为google、baidu的爬虫IP,这些IP在网上都可以找到。校验出来IP不在白名单就可以阻止访问内容。

当然,有一部分爬虫是以浏览器载入的方式来抓取内容的,所以,即使被识别出来是浏览器访问的来源ip。还要检测这个个ip在一个时间片内的并发数,超过一定阀值,可以认为是爬虫,阻止访问内容。

由于我们的反爬虫策略是基于IP的,会出现误判,尤其是并发量限制的判别。我们需要一种友好的方式来阻止访问。直接返回50x/40x空白或者错误页面是很粗鲁的,当真正的用户被误判阻止访问时能够手动解锁继续访问才是比较优雅的方式,大家不约而同的会想到验证码,对!让用户输入图形中的验证码解锁,可是我们平常见到的验证码都还是野蛮的,验证码技术从一开始的简单的数字,发展今天有输入汉字的、输入数学计算结果的等等五花八门,不仅以复杂的验证码刁难用户,还要加上各种干扰字符,美其名曰提高安全性,实际上是开发工程师脑残扎堆钻牛角尖的产物,用户是怨声载道。验证码的目的是区分人工和机器,要做到机器无法自动操作,同时让人工操作很方便、优雅。在本文的案例中,我们采用了一种比较有趣的验证码,让人识别物体,在验证码系统中预存了大量的事物,包括动物、植物、家具等等日常遇到的东西,验证用户的过程就是系统从这些事物中随机选出少量图形,并要求用户选中预设答案中的某一个即可解锁。

回到识别爬虫的步骤,我们用流程图理一下:

【实现】

我们用nodejs(express)和redis来实现反爬虫系统,redis用来存放一些计数。

1、判别是否为浏览器访问

返回页面请求时,在redis中给该IP的页面访问计数+1。在每个页面中会引入一个js,当请求这个js文件时在redis中给该IP页面访问计数-1,这样,如果不是浏览器的请求,redis中的页面计数会不断增大,如果是浏览器请求,下载页面源代码时增1,随后浏览器加载js文件时减1,redis中的页面计数会归零。我们只需要判断页面计数是否为0来区分是否为浏览器访问,我们还可以给页面下载完了但是js没有加载这种特殊情况留点余地,设定一个阀值,例如:5,页面计数大于5就判别出该IP内有爬虫访问。

2、爬虫白名单识别

如果上一步被识别为爬虫访问,则进一步检测用户http头的user-agent、ip,判断是否在预设的白名单内。如果不在则阻止访问显示验证码。这个步骤很简单,不用多说。

3、浏览器访问下的并发量限制

同样在 redis下给每个IP做计数,和上面不同的是利用redis key的过期机制,每次计数累加时将key设定在一定的时间内过期,比如5秒,这个相当于一个时间片,如果5秒内有另外一个请求,会计数增加1,过期时间会延长5秒,如果在一个5秒内没有其他请求,这个key就会消失。此后一个请求进来计数从1开始。我们可以设定一个阀值,比如20,任意5秒内有20个请求进来为超限,阻止访问显示验证码。

4、优雅的验证码

系统预设了很多图片,每个图片是一个动物、植物、家具等,每个图片有一个ID值。从这些图片中任意抽出3个,并且选中其中一个为标准答案,注意这个过程都是程序后台进行,将标准答案ID放在session中。前台页面显示了这3幅图片,并根据预设的答案要求用户选择其中一个,用户只要选中对应的图片,将表单提交到后台,系统将提交的ID与session中ID比较判别是否正确。当然,每个图片都有一个固定的ID值有被穷举的漏洞,有很多改进的余地,此处仅讨论原型不做过多探讨。

效果如图:

好了,接下来我会贴出一些实现的代码,如果你想看看实现后的效果,可以访问碰头吧(http://www.pengtouba.com/)试验一下,首页没有加反爬虫策略。打开微信广场http://www.pengtouba.com/weixin/cast-c1p1s1.html 然后用F5强暴刷新你就会看到效果了。

【代码】

拦截请求(其他语言类似,例如java可以用拦截器)

app.get(‘/weixin/*‘, antiCrawler.openDoor);//需要保护的目录
app.get(‘/helper/close-door.js‘, antiCrawler.closeDoor); //伪js文件

antiCrawler.js

/**
 * anti crawler
 * Created by Cherokee on 14-7-13.
 */
var settings = require("../settings.json");
var redis = require("redis");
var cache = require("../lib/cache.js");
var vcode = require(‘../lib/vcode.js‘);
var ac_redis_cli = redis.createClient(settings[‘anti_crawler_redis_db‘][‘port‘],settings[‘anti_crawler_redis_db‘][‘host‘]);
var IP_RECORD_EXPIRE = settings[‘anti_crawler_redis_db‘][‘ip_record_expire‘];
var IP_LOCK_EXPIRE = settings[‘anti_crawler_redis_db‘][‘ip_lock_expire‘];
var IP_HAIR_EXPIRE = settings[‘anti_crawler_redis_db‘][‘ip_hair_expire‘];
var DOOR_THRESHOLD = settings[‘anti_crawler_redis_db‘][‘door_threshold‘];
var HAIR_THRESHOLD = settings[‘anti_crawler_redis_db‘][‘hair_threshold‘];

ac_redis_cli.on(‘ready‘,function(){
    console.log(‘redis for anti-crawler is ready‘);
});

ac_redis_cli.on(‘error‘,function(err){
    console.error(‘redis for anti-crawler error‘+err);
});

ac_redis_cli.on(‘end‘,function(){
    console.error(‘redis for anti-crawler closed‘);
});

ac_redis_cli.select(settings[‘anti_crawler_redis_db‘][‘db‘],function(err){
    if(err)throw err;
    else {
        cache.set(‘ac_redis_cli‘,ac_redis_cli,77760000);
        console.log(‘redis for anti-crawler switch db :‘+settings[‘anti_crawler_redis_db‘][‘db‘]);
    }
});

exports.openDoor = function(req, res, next) {
    var ua = req.get(‘User-Agent‘);
    var ip = req.ip;
    var url = req.url;

    if(/\/weixin\//.test(url)){
        ac_redis_cli.exists(‘lock:‘+ip,function(err,bol){
            if(bol){
                send421(req,res);
            }else{
                ac_redis_cli.get(‘door:‘+ip,function(err,d_num){
                    if(d_num>DOOR_THRESHOLD){//some one didn‘t use browser
                        if(isTrustSpider(ua,ip)){//it‘s trusted spider
                            kickDoor(ip,function(val){
                                leaveHair(ip,function(val){
                                    next();
                                });
                            });
                        }else{
                            blockIt(req,res);
                        }
                    }else{//perhaps using simulated browser to crawl pages
                        ac_redis_cli.get(‘hair:‘+ip,function(err,h_num){
                            if(h_num>HAIR_THRESHOLD){//suspicious
                                blockIt(req,res);
                            }else {
                                kickDoor(ip,function(val){
                                    leaveHair(ip,function(val){
                                        next();
                                    });
                                });
                            }
                        });
                    }
                });
            }
        });
    }
};

exports.closeDoor = function(req,res){
    ac_redis_cli.multi()
        .decr(‘door:‘+req.ip)
        .expire(‘door:‘+req.ip,IP_RECORD_EXPIRE)
        .exec(function(err, replies){
            if(replies&&parseInt(replies[0])<0){
                ac_redis_cli.set(‘door:‘+req.ip,0,function(err){
                    res.set(‘Content-Type‘, ‘application/x-javascript‘);
                    res.send(200,‘{"zeroize":true}‘);
                });
            }else{
                res.set(‘Content-Type‘, ‘application/x-javascript‘);
                res.send(200,‘{"zeroize":false}‘);
            }
        });
}

exports.verify = function(req,res){
    var vcode = req.body.vcode;
    var origin_url = req.body.origin_url;
    if(req.session.vcode&&vcode==req.session.vcode){
        req.session.vcode = null;
        ac_redis_cli.multi()
            .del(‘lock:‘+req.ip)
            .del(‘door:‘+req.ip)
            .del(‘hair:‘+req.ip)
            .exec(function(err, replies){
                res.redirect(origin_url);
            });
    }else send421(req,res,origin_url);

}

var blockIt = function(req,res){
    ac_redis_cli.multi()
        .set(‘lock:‘+req.ip,1)
        .expire(‘lock:‘+req.ip,IP_LOCK_EXPIRE)
        .exec(function(err, replies){
            send421(req,res);
        });
}

var send421 = function(req,res,origin_url){
    var code_map = {};
    var code_arr = [];

    while(code_arr.length<3){
        var rindex = Math.ceil(Math.random() * vcode.list.length) - 1;
        if(!code_map[rindex]){
            code_map[rindex] = true;
            code_arr.push(rindex);
        }
    }
    var answer = code_arr[Math.ceil(Math.random() * 3) - 1];
    req.session.vcode = answer;
    res.status(421).render(‘weixin/421‘,{‘code_list‘:code_arr,‘code_label‘:vcode.list[answer],‘origin_url‘:origin_url||req.url});
}

var isTrustSpider = function(ua,ip){
    var trustBots  = [
        /Baiduspider/ig,
        /Googlebot/ig,
        /Slurp/ig,
        /Yahoo/ig,
        /iaskspider/ig,
        /Sogou/ig,
        /YodaoBot/ig,
        /msnbot/ig,
        /360Spider/ig
    ];
    for(var i=0;i<trustBots.length;i++){
        if(trustBots[i].test(ua))return true;
    }
    return false;
}

var kickDoor = function(ip,callback){
    ac_redis_cli.multi()
        .incr(‘door:‘+ip)
        .expire(‘door:‘+ip,IP_RECORD_EXPIRE)
        .exec(function(err, replies){
            if(callback)callback(replies?replies[0]:null);
        });
}

var leaveHair = function(ip,callback){
    ac_redis_cli.multi()
        .incr(‘hair:‘+ip)
        .expire(‘hair:‘+ip,IP_HAIR_EXPIRE)
        .exec(function(err, replies){
            if(callback)callback(replies?replies[0]:null);
        });
}

实际应用中不仅要检测User-agent,还要有IP白名单检测,以上代码并没有包含 IP白名单。

send421函数就是显示验证码的步骤,verify函数是检验用户输入的验证码。

如何给网站加入优雅的反爬虫策略

时间: 2024-10-06 21:05:45

如何给网站加入优雅的反爬虫策略的相关文章

给网站加入优雅的实时反爬虫策略

你的网站内容很有价值,希望被google,百度等正规搜索引擎爬虫收录,却不想让那些无节操的山寨爬虫把你的数据扒走坐享其成.本文将探讨如何在网站中加入优雅的反爬虫策略. [思路] 反爬虫策略要考虑以下几点: 能被google.百度等正规搜索引擎爬虫抓取,不限流量和并发数: 阻止山寨爬虫的抓取: 反爬虫策略应该是实时检测的,而不是通过一段时间后的访问统计分析得出: 误判后的人性化处理(优雅之所在): 大部分的爬虫不是以浏览器方式来访问页面的,爬虫只下载网页的html源代码,不加载包含在页面中的js/

python解决网站的反爬虫策略

网站的反爬虫策略: 从功能上来讲,爬虫一般分为数据采集,处理,储存三个部分.这里我们只讨论数据采集部分. 一般网站从三个方面反爬虫:用户请求的Headers,用户行为,网站目录和数据加载方式.前两种比较容易遇到,大多数网站都从这些角度来反爬虫.第三种一些应用ajax的网站会采用,这样增大了爬取的难度(防止静态爬虫使用ajax技术动态加载页面). 1.从用户请求的Headers反爬虫是最常见的反爬虫策略. 伪装header.很多网站都会对Headers的User-Agent进行检测,还有一部分网站

反击“猫眼电影”网站的反爬虫策略

0×01 前言 前两天在百家号上看到一篇名为<反击爬虫,前端工程师的脑洞可以有多大?>的文章,文章从多方面结合实际情况列举了包括猫眼电影.美团.去哪儿等大型电商网站的反爬虫机制.的确,如文章所说,对于一张网页,我们往往希望它是结构良好,内容清晰的,这样搜索引擎才能准确地认知它:而反过来,又有一些情景,我们不希望内容能被轻易获取,比方说电商网站的交易额,高等学校网站的题目等.因为这些内容,往往是一个产品的生命线,必须做到有效地保护.这就是爬虫与反爬虫这一话题的由来.本文就以做的较好的"

网站反爬虫策略

反爬虫策略,表面上看似乎跟WEB系统优化没有关系,经过分析,发现该策略是可以归到WEB性能优化的系列之中. 通过分析apache日志发现,某系统40%的带宽和服务器资源都消耗在爬虫上,如果除去10%-15%搜索引擎的爬虫,做好反爬虫策略,能节省20%-25%的资源,其实是变向优化了web系统. 一.爬虫请求与正常用户请求的区别 爬虫请求是类似httpClient的机制或curl,wget的命令,用户请求一般走浏览器. 区别:爬虫请求一般不会执行页面里的异步JavaScript操作,而用户请求则执

互联网网站的反爬虫策略浅析

因为搜索引擎的流行,网络爬虫已经成了很普及网络技术,除了专门做搜索的Google,Yahoo,微软,百度以外,几乎每个大型门户网站都有自己的搜索引擎,大大小小叫得出来名字得就几十种,还有各种不知名的几千几万种,对于一个内容型驱动的网站来说,受到网络爬虫的光顾是不可避免的. 一些智能的搜索引擎爬虫的爬取频率比较合理,对网站资源消耗比较少,但是很多糟糕的网络爬虫,对网页爬取能力很差,经常并发几十上百个请求循环重复抓取,这种爬虫对中小型网站往往是毁灭性打击,特别是一些缺乏爬虫编写经验的程序员写出来的爬

关于反爬虫策略

一.为什么要反爬虫 1.爬虫占总PV比例较高,这样浪费钱(尤其是三月份爬虫). 三月份爬虫是个什么概念呢?每年的三月份我们会迎接一次爬虫高峰期. 最初我们百思不得其解.直到有一次,四月份的时候,我们删除了一个url,然后有个爬虫不断的爬取url,导致大量报错,测试开始找我们麻烦.我们只好特意为这个爬虫发布了一次站点,把删除的url又恢复回去了. 但是当时我们的一个组员表示很不服,说,我们不能干掉爬虫,也就罢了,还要专门为它发布,这实在是太没面子了.于是出了个主意,说:url可以上,但是,绝对不给

解决猫眼网反爬虫策略的爬虫

项目代码:Github [目录] 一.引入问题 二.分步实现 1.页面爬取 2.woff下载 3.字体解析规则 一.引入问题 可以看到,猫眼网电影评分,票房等的数据在响应的html中并不是直接提供给你的.这里的xefcf,xef87等数据,是以'特殊符号'的形式显示出来的. 可以发现这里请求了一个woff字体文件,而xefcf,xef87等数据的规则就是在这其中的.所以我们只需要在请求这个网页的同时,截去这部分style,然后获得woff地址,将它下载到本地,进行解析,就可以实现对评分的解密了.

遇到的反爬虫策略以及解决方法?

通过headers反爬虫:自定义headers,添加网页中的headers数据. 基于用户行为的反爬虫(封IP):可以使用多个代理IP爬取或者将爬取的频率降低. 动态网页反爬虫(JS或者Ajax请求数据):动态网页可以使用 selenium + phantomjs 抓取. 对部分数据加密处理(数据乱码):找到加密方法进行逆向推理. 原文地址:https://www.cnblogs.com/sea-stream/p/11192544.html

前端反爬虫策略--font-face 猫眼数据爬取

1 .font-face定义了字符集,通过unicode去印射展示. 2 .font-face加载网络字体,我么可以自己创建一套字体,然后自定义一套字符映射关系表例如设置0xefab是映射字符1,0xeba2是映射字符2,以此类推.当需要显示字符1时,网页的源码只会是0xefab,被采集的也只会是 0xefab,并不是1 3 .但是对于正常的用户来说则没有影响,因为浏览器会加载css的font字体为我们渲染好,实时显示在网页中. 4 .所以我们需要做的是,如何在判断请求web字体的是机器人或者是