大家有什么好方法,防止页面被机器人curl抓取?

原创
2016-06-23 14:05:01 2062浏览

以前一直研究抓取别人的页面内容,现在想请教一下各位前辈,根据大家的经验,有什么好方法防止页面被抓取吗?

我以前遇到2种情况,比较难抓取(指的是自动程序批量获取).
第一种:生成复杂的cookie验证,每几分钟过期一次,要curl抓取的话,每次都要手动粘贴cookie到CURLOPT_HEADER里。

第二种:如果一个IP在一段时间内频繁的抓取(指的是类似GOOGLE的搜索结果页面),那么隔一段会跳出一个验证码,需要人工验证。

别的,像内容写进js里,再打印到页面里之类的都可以破解以后批量获取。

各位前辈,请教你们的高见,最好能秀一下大家的代码,供大家切磋、破解。


回复讨论(解决方案)

图片....

我知道一个貌似是reboot.txt的文件放在网站中可以设置搜索引擎抓取页面。

期待牛人来回答一下,我就学习一下。

curl好像可以跟浏览者搞的一模一样啊,可以用其他思路搞,比如加很多垃圾代码,里面有你域名的连接,无规则的加入,像织梦防采集那样,这样采集你的人,他很难通过正则匹配到你的内容,就算匹配的差不多,每个页面都会有几个你的链接,如果他愿意采集,呵呵,免费外链也可以啊。

只要是浏览器能看到的,curl就能抓到
可灵活使用 CURLOPT_COOKIESESSION、CURLOPT_COOKIE、CURLOPT_COOKIEFILE、CURLOPT_COOKIEJAR 来处理 cookie,并不存在楼主的“每次都要手动粘贴cookie到CURLOPT_HEADER里”
虽然可以用js代码验证并跳转来阻止curl的动作,但下载 linux 下执行js代码的php扩展业已存在了

唯一可行的方案就是检测访问的频率
目前很多网站都是用此法,不过不是弹出对话框。而是输出无效的页面内容,让你白干一场

唯一可行的方案就是检测访问的频率
目前很多网站都是用此法,不过不是弹出对话框。而是输出无效的页面内容,让你白干一场

伟大的斑竹大人,有没有实例代码供小弟学习一下?谢谢。

我也来这里学习一下啊

应该无法防止机器人抓取网页吧,唯一可行的方案就是检测访问的频率~~~

如何最经济的检测访问的频率呢?建立临时数据表?session?

我大概几年前遇到过一个机票网站,它们采用了一种方法,令我当时束手无策。

刚才又回忆了一下,现在发出来,大家一起探讨下。
当然我不能完全获悉它的所有细节,只是根据当时的研究自己做了一些推测,他使用的方法大概如下:

页面第一次加载后,只是将一些基本的html加载出来,js会通过ajax将关键性的数据通过ajax请求回来,返回的可以是json,然后在通过页面js讲json解析,绘制到页面上。问题的关键是做那次ajax请求时,需要带一个特殊的参数过去才能正确获得数据。并且这个通过这个参数获得数据后,这个参数就失效了。而这个参数应该是有一定算法的,并且由这个动态页面在加载的同时生成。


做法大概可以这样:
1 请求页面a.php,a.php页面在生成需要返回浏览器的html的同时,根据特定算法生成一个验证串abc123(并且每次请求都生成的串都不一样),并写入数据库或第三方存储媒介。

2 a.php的html返回浏览器后,拼接出需要ajax请求的url,并且最后附加一个参数,就是刚才生成的验证串abc123。然后执行ajax的请求。接收ajax请求的页面,去数据库中查询一下这个abc123是否存在。如果存在,则返回正确的json数据,否则不返回或返回异常。


这样做,对于采集的一方来说。当你去抓那个a.php时,抓到之后ajax会自动执行一次。这个时候,你抓回来的页面虽然可以看到a.php中ajax请求得到的数据,你会发现源代码中没有它。而你想再用a.php的html源代码中的ajax url去请求一次,发现生成的那个abc123验证串已经失效了。


做个实验吧,先不考虑验ajax证串的问题:
1.php(也就是我们抓取方的脚本)

 $url,			CURLOPT_HEADER => 0,			//CURLOPT_HTTPHEADER => $header,			CURLOPT_NOBODY => 0,			CURLOPT_PORT => 80,			CURLOPT_POST => 1,			CURLOPT_POSTFIELDS => $postfields,			CURLOPT_RETURNTRANSFER => 1,			CURLOPT_FOLLOWLOCATION => 1,			//CURLOPT_COOKIEJAR => $cookie_jar,			//CURLOPT_COOKIEFILE => $cookie_jar,			//CURLOPT_SSL_VERIFYPEER => 0,			//CURLOPT_SSL_VERIFYHOST => 1,			CURLOPT_TIMEOUT => 30		);		curl_setopt_array($ch, $options);		$code = curl_exec($ch);		curl_close($ch);		return $code;}$url = 'http://www.angryfrog.com/2.php';echo request($url);



2.php(被抓取方的页面)



3.php(被抓取方的ajax接口)
 'data1',	'node2' => 'data2');echo json_encode($a);



当我执行1.php后,页面显示的结果如下:


但我查看1.php的源代码,发现:


请问,1.php页面上显示的那个data1在哪?

修改一下我的1.php,将抓取后的结果写入一个临时文件。也就是将
echo request($url);
改为
file_put_contents('./tmp', request($url););

再次执行1.php后,查看文件tmp:


发现结果还是一样,无法得到那个data1



上面的例子证明通过一次请求对方的脚本页面,虽然浏览器上可以看到我们想要的数据,但源代码里是没有的。因为那数据是通过第二次ajax请求得到的。

那么我们也模拟下它的ajax,再做一次请求如何?

假如按我开头的思路,ajax请求地址中有个参数是验证串的话,通过查看2.php的源代码,ajax模拟请求的url为
http://www.angryfrog.com/3.php?vcode=123abc

想再模拟ajax做一次请求时,3.php去数据库中检查这个123abc的验证串,发现那个123abc已经过期了(在数据库中不存在了),因为第一次请求成功后,3.php会将它从数据库中删除。那么既然过期了,3.php可以选择不返回正确数据了。


这就造成了个矛盾,我不请求2.php,就没法生成那个验证串。但我请求了2.php,它又会自动去请求下3.php,把生成的验证串给废掉。

所以我当时没有成功,不知这种情况如何破。大家有没有啥好办法?能否成功请求2.php,让他生成出验证串,但又不自动去做ajax请求以至于不废掉生成出来的串,被我们得到后自己拿着去做ajax请求?

sorry,突然想到,我那个想法是有bug的。

我只测试了同域的情况下,如果是真实情况的话,ajax请求是不可能被自动执行成功的。因为跨域了。

当我们从自己的服务器去请求对方页面,得到html和js,包括那些ajax。回到我们的浏览器后。域就变成我们自己服务器的了,这时页面上的ajax不可能跨域请求成功对方的ajax接口。也就谈不上那个生成的验证码被自动销毁的情况了。看来这种方法不行。

又仔细回忆了一下(时间实在太久远了)。当时我确实是看到对方页面上的ajax,并且包含了一个可疑的参数,每次请求得到后的都不同(而且确认不是为了防止cache用的),但当我自己尝试带着这个参数去请求这个url时,什么结果也得不到。而通过它的页面,请求这个地址却可以返回数据。不知是什么原因,referer、user-agent我也都模拟了。

可不可以这样?a.php页面生成验证串写入数据库后,这个串在很短的时间内就会失效。比如1秒。这点用redis或memcached就很容易做到,给那个key做一个失效时间。

当我们通过脚本抓取a.php后,需要分析它的源代码,找出ajax请求的地址,再去请求,时间会比较长,这个过程可能会超过1秒,那个验证串已经失效了,ajax接口则不会成功返回数据。而正常访问它的页面,页面执行ajax,速度会比较快不会超过1秒,因此能成功得到数据。


明天有时间,我把完整的代码写下试试看。

php_v8js 的作用是在 php 中执行 js 代码
而楼上的描述使用 php 去模拟 js 的动作

这显然不是一回事

这个问题其实说到底是看哪一方不惜工本
防的一方不惜工本,迫使抓的一方在考虑成本效益后放弃
抓的一方不惜工本,防的一方在考虑用户流失后放弃

你们还没见过那些疯狂的人而已

总的来说,在open web时代,抓的一方所花费成本要低很多

刘明,等看

采用加密算法生成相对复杂的认证标志,只有带这个标志才可访问,同时入口处提供验证码识别。
而验证码识别会降低用户体验。
而只是使用复杂的认证标志,一旦生成算法破解,一样可以批量发起请求。
总的来说,人能通过浏览器访问无非就是发送http请求,那么机器同样可以做到,无非是机器要先采集数据,分析清楚访问流程而已。

@ShadowSniper, 很好的实例哦,开阔思路了。
这就想到于:
浏览器访问A页面时,生成一个随机码,该随机码作为ajax参数传递到B页面,B页面需要验证这个随机码的合法性,然后返回数据到A页面。
所以直接curl A页面的话,什么也得不到。
直接curl B页面(ajax页面的话),由于没有合法的随机码,所以还是什么也得不到。

@ShadowSniper, 很好的实例哦,开阔思路了。
这就想到于:
浏览器访问A页面时,生成一个随机码,该随机码作为ajax参数传递到B页面,B页面需要验证这个随机码的合法性,然后返回数据到A页面。
所以直接curl A页面的话,什么也得不到。
直接curl B页面(ajax页面的话),由于没有合法的随机码,所以还是什么也得不到。

错了吧?
直接curlA,得到了一个随机码,然后模拟curlB并带着刚才那个随机码,不就获取最终想要的数据了么。
同时,你说的“B页面需要验证这个随机码的合法性,然后返回数据到A页面”,这个数据就是最终想要的数据,而这个数据只需要带上随机码就可以了。

@ShadowSniper, 很好的实例哦,开阔思路了。
这就想到于:
浏览器访问A页面时,生成一个随机码,该随机码作为ajax参数传递到B页面,B页面需要验证这个随机码的合法性,然后返回数据到A页面。
所以直接curl A页面的话,什么也得不到。
直接curl B页面(ajax页面的话),由于没有合法的随机码,所以还是什么也得不到。

难道你防的仅仅是curl?那整个造盾思路就错了
要防的是这个 点击看维基-webkit
现在基于webkit的矛(非GUI浏览器)多的是,远超curl了


csdn
都被抓取
http://s.yanghao.org/program/viewdetail.php?i=393421

引用 18 楼 changjay 的回复:@ShadowSniper, 很好的实例哦,开阔思路了。
这就想到于:
浏览器访问A页面时,生成一个随机码,该随机码作为ajax参数传递到B页面,B页面需要验证这个随机码的合法性,然后返回数据到A页面。
所以直接curl A页面的话,什么也得不到。
直接curl B页面(ajax页面的话),由于没有合法的随机码,所以还是什……


我说的那个方法,大概思路是对的。但我一开始没考虑跨域的问题。

a页面生成验证码,写入db。a带着这个验证码去请求b接口,b接口拿到a传过来的验证码,去db里查询是否存在,如果存在,就返回正确数据,并且从db中清除掉这个验证码。

所以由a页面生成的验证码,只能使用一次。

当你模拟请求a页面,a不光会生成一个验证码,同时会把这个流程走完,并且销毁掉这个验证码。也会返回数据,但这一次你无法得到a页面返回的数据(只能看到,但得不到。不信拿我的那几个例子去试验下)

而一般来说,页面中含有ajax的二次请求,我们应该在第一次请求得到页面中ajax请求的地址后,用正则分析出a页面生成的验证码和他ajax请求的地址,再用php带着这个验证码去请求一次。希望通过这样,来得到正确的数据。但可惜的是,在你第一次模拟请求a页面时,他已经把整个流程走完了,包括页面中的ajax会自动执行。那个生成验证码也被销毁了。所以这里就造成了个矛盾的问题。
这种情况,抓取方和被抓取方处于同域的情况下,是可以实现的。

但实际情况中,抓取方不可能和被抓取方处于同域,所以请求a页面后,可以得到生成的验证码,但是ajax请求不会成功,因为跨域。所以那个生成的验证码也不会被销毁。那么我们就可以拿到验证码后,再用php去请求一下那个ajax地址来获得正确的数据。

引用 18 楼 changjay 的回复:@ShadowSniper, 很好的实例哦,开阔思路了。
这就想到于:
浏览器访问A页面时,生成一个随机码,该随机码作为ajax参数传递到B页面,B页面需要验证这个随机码的合法性,然后返回数据到A页面。
所以直接curl A页面的话,什么也得不到。
直接curl B页面(ajax页面的话),由于没有合法的随机码,所以还是什……


a.php

function request( $url, $header = '', $postfields = '' ){		$ch = curl_init();		$options = array(			CURLOPT_URL => $url,			CURLOPT_HEADER => 0,			//CURLOPT_HTTPHEADER => $header,			CURLOPT_NOBODY => 0,			CURLOPT_PORT => 80,			CURLOPT_POST => 1,			CURLOPT_POSTFIELDS => $postfields,			CURLOPT_RETURNTRANSFER => 1,			CURLOPT_FOLLOWLOCATION => 1,			//CURLOPT_COOKIEJAR => $cookie_jar,			//CURLOPT_COOKIEFILE => $cookie_jar,			//CURLOPT_SSL_VERIFYPEER => 0,			//CURLOPT_SSL_VERIFYHOST => 1,			CURLOPT_TIMEOUT => 30		);		curl_setopt_array($ch, $options);		$code = curl_exec($ch);		curl_close($ch);		return $code;}$url = 'http://www.angryfrog.com/b.php';echo request($url);


b.php



执行一下a.php,b.php中的js返回后会被自动执行。

ShadowSniper研究的比较深,谢谢。好好再琢磨一下。

抓数据,一般只抓页面,页面里的图片,CSS,JS等等都是不会继续向服务器请求的。所以很容易判断哪些用户的请求是爬虫,哪些不是:)

访问频率检查方案方面,请问大家有什么经验指点?

//第一步:入口文件检测IP黑名单实现禁止访问

//第二步:访问频率检测
$ip = get_client_ip(); //获取访问者IP
$ipCode = md5($ip);
$次数 = intval($cache->get($ipCode)); //把null转成0
if($次数 < 5)$cache->set($ipCode, ++$次数, 3000); //缓存过期时间为3000毫秒=3秒
else{
//将该IP加入黑名单,N天/N个小时后解除黑名单
}

建议在 .htaccess直接屏蔽对方的 IP地址段。
当然,也可以考虑iptables;

用js joson可以增加难度

抓数据,一般只抓页面,页面里的图片,CSS,JS等等都是不会继续向服务器请求的。所以很容易判断哪些用户的请求是爬虫,哪些不是:)
怎么理解,分析客户端是否下载了CSS,JS文件?这个怎么判断?还有怎么可以分辨出是搜索引擎爬虫还是不良爬虫?

引用 25 楼 LuciferStar 的回复:抓数据,一般只抓页面,页面里的图片,CSS,JS等等都是不会继续向服务器请求的。所以很容易判断哪些用户的请求是爬虫,哪些不是:)
怎么理解,分析客户端是否下载了CSS,JS文件?这个怎么判断?还有怎么可以分辨出是搜索引擎爬虫还是不良爬虫? 这个应该是服务器上防止采集的一个策略吧。
估计得服务器软件进行行为控制了。

mark 我也碰到过这样的问题 好像大多防这个都是服务器去处理了

sorry,突然想到,我那个想法是有bug的。

我只测试了同域的情况下,如果是真实情况的话,ajax请求是不可能被自动执行成功的。因为跨域了。

当我们从自己的服务器去请求对方页面,得到html和js,包括那些ajax。回到我们的浏览器后。域就变成我们自己服务器的了,这时页面上的ajax不可能跨域请求成功对方的ajax接口。也就谈不上那个生成的验证码被自动销毁……
大牛,腾讯新闻的好像也是这种高法,内容关键性的js全部是动态的 还各种包含,呵呵,头大

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。