欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

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

程序员文章站 2022-05-26 11:10:19
...
以前一直研究抓取别人的页面内容,现在想请教一下各位前辈,根据大家的经验,有什么好方法防止页面被抓取吗?

我以前遇到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($次数 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全部是动态的 还各种包含,呵呵,头大