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

去圣西罗-为尤文喝彩 2013-03-30 03:28:57
以前一直研究抓取别人的页面内容,现在想请教一下各位前辈,根据大家的经验,有什么好方法防止页面被抓取吗?

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

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

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

各位前辈,请教你们的高见,最好能秀一下大家的代码,供大家切磋、破解。
...全文
2387 33 打赏 收藏 转发到动态 举报
写回复
用AI写文章
33 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
引用 11 楼 ShadowSniper 的回复:
sorry,突然想到,我那个想法是有bug的。 我只测试了同域的情况下,如果是真实情况的话,ajax请求是不可能被自动执行成功的。因为跨域了。 当我们从自己的服务器去请求对方页面,得到html和js,包括那些ajax。回到我们的浏览器后。域就变成我们自己服务器的了,这时页面上的ajax不可能跨域请求成功对方的ajax接口。也就谈不上那个生成的验证码被自动销毁……
大牛,腾讯新闻的好像也是这种高法,内容关键性的js全部是动态的 还各种包含,呵呵,头大
LNMP自学者 2013-04-03
  • 打赏
  • 举报
回复
建议在 .htaccess直接屏蔽对方的 IP地址段。 当然,也可以考虑iptables;
blizzf99 2013-04-03
  • 打赏
  • 举报
回复
顶贴收藏~~
K-dash 2013-04-03
  • 打赏
  • 举报
回复
访问频率检查方案方面,请问大家有什么经验指点? //第一步:入口文件检测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个小时后解除黑名单 }
LuciferStar 2013-04-03
  • 打赏
  • 举报
回复 1
抓数据,一般只抓页面,页面里的图片,CSS,JS等等都是不会继续向服务器请求的。所以很容易判断哪些用户的请求是爬虫,哪些不是:)
森之树 2013-04-03
  • 打赏
  • 举报
回复 1
mark 我也碰到过这样的问题 好像大多防这个都是服务器去处理了
LuciferStar 2013-04-03
  • 打赏
  • 举报
回复
引用 30 楼 changjay 的回复:
引用 25 楼 LuciferStar 的回复:抓数据,一般只抓页面,页面里的图片,CSS,JS等等都是不会继续向服务器请求的。所以很容易判断哪些用户的请求是爬虫,哪些不是:) 怎么理解,分析客户端是否下载了CSS,JS文件?这个怎么判断?还有怎么可以分辨出是搜索引擎爬虫还是不良爬虫?
这个应该是服务器上防止采集的一个策略吧。 估计得服务器软件进行行为控制了。
  • 打赏
  • 举报
回复
引用 25 楼 LuciferStar 的回复:
抓数据,一般只抓页面,页面里的图片,CSS,JS等等都是不会继续向服务器请求的。所以很容易判断哪些用户的请求是爬虫,哪些不是:)
怎么理解,分析客户端是否下载了CSS,JS文件?这个怎么判断?还有怎么可以分辨出是搜索引擎爬虫还是不良爬虫?
lang11zi 2013-04-03
  • 打赏
  • 举报
回复
用js joson可以增加难度
月蚀 2013-04-02
  • 打赏
  • 举报
回复
刘明,等看
ImN1 2013-04-02
  • 打赏
  • 举报
回复
这个问题其实说到底是看哪一方不惜工本 防的一方不惜工本,迫使抓的一方在考虑成本效益后放弃 抓的一方不惜工本,防的一方在考虑用户流失后放弃 你们还没见过那些疯狂的人而已 总的来说,在open web时代,抓的一方所花费成本要低很多
  • 打赏
  • 举报
回复
ShadowSniper研究的比较深,谢谢。好好再琢磨一下。
an9ryfr09 2013-04-02
  • 打赏
  • 举报
回复
引用 19 楼 hellodifa 的回复:
引用 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

<script>alert(123);</script>
执行一下a.php,b.php中的js返回后会被自动执行。
an9ryfr09 2013-04-02
  • 打赏
  • 举报
回复
引用 19 楼 hellodifa 的回复:
引用 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地址来获得正确的数据。
xuzuning 2013-04-02
  • 打赏
  • 举报
回复
php_v8js 的作用是在 php 中执行 js 代码 而楼上的描述使用 php 去模拟 js 的动作 这显然不是一回事
「已注销」 2013-04-02
  • 打赏
  • 举报
回复
csdn 都被抓取 http://s.yanghao.org/program/viewdetail.php?i=393421
ImN1 2013-04-02
  • 打赏
  • 举报
回复
引用 18 楼 changjay 的回复:
@ShadowSniper, 很好的实例哦,开阔思路了。 这就想到于: 浏览器访问A页面时,生成一个随机码,该随机码作为ajax参数传递到B页面,B页面需要验证这个随机码的合法性,然后返回数据到A页面。 所以直接curl A页面的话,什么也得不到。 直接curl B页面(ajax页面的话),由于没有合法的随机码,所以还是什么也得不到。
难道你防的仅仅是curl?那整个造盾思路就错了 要防的是这个 点击看维基-webkit 现在基于webkit的矛(非GUI浏览器)多的是,远超curl了
an9ryfr09 2013-04-02
  • 打赏
  • 举报
回复
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秒,因此能成功得到数据。 明天有时间,我把完整的代码写下试试看。
夏之冰雪 2013-04-02
  • 打赏
  • 举报
回复
引用 18 楼 changjay 的回复:
@ShadowSniper, 很好的实例哦,开阔思路了。 这就想到于: 浏览器访问A页面时,生成一个随机码,该随机码作为ajax参数传递到B页面,B页面需要验证这个随机码的合法性,然后返回数据到A页面。 所以直接curl A页面的话,什么也得不到。 直接curl B页面(ajax页面的话),由于没有合法的随机码,所以还是什么也得不到。
错了吧? 直接curlA,得到了一个随机码,然后模拟curlB并带着刚才那个随机码,不就获取最终想要的数据了么。 同时,你说的“B页面需要验证这个随机码的合法性,然后返回数据到A页面”,这个数据就是最终想要的数据,而这个数据只需要带上随机码就可以了。
an9ryfr09 2013-04-02
  • 打赏
  • 举报
回复
我大概几年前遇到过一个机票网站,它们采用了一种方法,令我当时束手无策。

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

页面第一次加载后,只是将一些基本的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(也就是我们抓取方的脚本)

<?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/2.php';

echo request($url);



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

<script src="./js/jquery-1.7.1.min.js"></script>
<script>
$(document).ready(function(){
$.get('http://www.angryfrog.com/3.php', '', function(data){ d=$.parseJSON(data);$('#tmp').html(d.node1);});
});
</script>

<a id="tmp"></a>



3.php(被抓取方的ajax接口)

<?php
$a = array(
'node1' => '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请求?
加载更多回复(11)

21,886

社区成员

发帖
与我相关
我的任务
社区描述
从PHP安装配置,PHP入门,PHP基础到PHP应用
社区管理员
  • 基础编程社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧