记录我工作学习生活中的点点滴滴......

php小偷之模拟登陆并抓取附件--论坛小偷
发布于 网络采集
2010-03-07 01:20:17
5001
0
研究小偷很长时间了,这次为了做一个论坛的,本来以为抓取只是简单的用file_get_contents,然后正则就OK了,但随着逐步的深入发现模拟登陆,模拟发帖,模拟蜘蛛貌似都很必要……

简单描述一下计划的模拟登陆……

首先抓取列表页(这一步以后可以使用模拟蜘蛛抓取以防止被封杀IP),抓取页面登陆地址,抓取登陆页面内容,取出所有隐藏表单及表单地址(对于用AJAX和JS提交的页面先暂不考虑,毕竟我现在主要目标是论坛~~~),从数据库取出对应的用户名和密码构成post字符串,与得到的隐藏表单内容连接成新字符串,构造头部信息,用curl提交该信息,并获取cookie

跳回列表页,分析各个帖子地址,重新构造头部,并加入COOKIE,接下来就跟以前一样了~~~

看起来似乎整个代码要重构,其实不然,只要修改获取列表页的get_file_contents为模拟登陆并抓取然后返回内容,然后修改获取帖子页内容的get_file_contents为模拟cookie并抓取然后返回页面内容就OK了~~

php小偷之模拟登陆

我封装了三个函数,一个用于登录并保存cookie,一个用于根据cookie取得对应页面的内容,最后一个用于删除临时的cookie文件

模拟登陆的目的就是为了要抓取那些必须登录才能抓取的附件,这里使用的部分函数可以在模拟登陆部分中找到,还是封装成了函数方便调用

function vlogin($url,$request){
$cookie_jar = tempnam('./tmp','cookie');//在当前目录下生成一个随机文件名的临时文件
$ch = curl_init(); //初始化curl模块
curl_setopt($ch,CURLOPT_URL,$url);//登录页地址
curl_setopt($ch, CURLOPT_POST, 1);//post方式提交
curl_setopt($ch, CURLOPT_POSTFIELDS, $request);//要提交的内容
//把返回$cookie_jar来的cookie信息保存在$cookie_jar文件中
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_jar);
//设定返回的数据是否自动显示
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
//设定是否显示头信息
curl_setopt($ch, CURLOPT_HEADER, false);
//设定是否输出页面内容
curl_setopt($ch, CURLOPT_NOBODY, false);
curl_exec ($ch);
curl_close($ch); //get data after login
return $cookie_jar;
}

//登录成功后通过cookies获取页面内容
function get_content_by_cookie($url,$cookie_jar){
$ch2 = curl_init();
curl_setopt($ch2, CURLOPT_URL, $url);
curl_setopt($ch2, CURLOPT_HEADER, false);
curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch2, CURLOPT_COOKIEFILE, $cookie_jar);
$orders=curl_exec($ch2);
curl_close($ch2);
return $orders;
}

function clear_cookie($cookie_tmp_name){
@unlink($cookie_tmp_name);
}

//使用的例子
$list_url='要抓取的帖子地址';
$login_url='登录地址';
$post_data='loginfield=username&loginmode&loginsubmit=提交&password=1234&questionid=0&username=yangqianyi';
$cookie_file=vlogin($login_url,$post_data); //模拟登陆
$tmp_curl=get_content_by_cookie($list_url,$cookie_file);//这里获得帖子内容
clear_cookie($cookie_file);//删除cookies临时文件
php小偷之论坛附件抓取
还是贴代码

//对帖子中的附件进行处理,图片直接显示,其它类型则提供下载链接
//$str为单个帖子的内容
//$attach_path为附件存放的路径,这个可以放进数据库
//$attach_base_url为附件的网络路径
//$base_url为当前网站的根路径
//$cookie_file为之前获取的cookie文件
function post_attach_change($str,$attach_path,$attach_base_url,$base_url,$cookie_file){
preg_match_all('附件的规则',$str,$attach);//取得所有附件链接及标题
//DZ6.0的附件规则:/href="(attachment.php?aid=d*.*?)".*?>(.*?)</a>/
$attach_str='';
$attach_path.=date("Y_m").'/';//生成一个当前月份为名字的目录用于存放附件
if(!is_dir ($attach_path)){
mkdir($attach_path,0777); }
if($attach){
foreach($attach['1'] as $attach_key =>$attach_value){
$attach_name=date('U').$attach_key;//文件名为当前时间加$attach_key
$picture=$attach['2'][$attach_key];
$pos = strrpos($picture, '.');//取得"."在字符串中的位置
$ext_name = strtolower(substr($picture, $pos+1, (strlen($picture)-$pos-1)));//得到后缀名
$attach_name=$attach_path.$attach_name.'.'.$ext_name;
file_put_contents($attach_name,get_content_by_cookie($base_url.$attach['1'][$attach_key],$cookie_file));
//根据后缀名判断是否为图片
if(in_array($ext_name,array('gif','jpg','png','bmp'))){
$attach_str.="<p><img src='".$attach_base_url.$attach_name."'></p>";
}else{
$attach_str.="<p><a href='".$attach_base_url.$attach_name."'>{$attach['2'][$attach_key]}</a></p>";
}
}
return $attach_str;
}
}
使用实例
$cookie_file=vlogin($login_url,$post_data); //模拟登陆
$tmp_curl=get_content_by_cookie($list_url,$cookie_file);//这里是要取得帖子地址,也就是那些需要登录才能看得帖子地址
preg_match_all('帖子规则',$tmp_curl,$attachlist);//将帖子页拆分成多个独立的主贴及回帖
foreach($attachlist['1'] as $key =>$value){
$tmp_curl=post_attach_change($value,'tmp/','http://pakey.net/',$base_url,$cookie_file);
$attachlist['1'][$key].=$tmp_curl;
}
php小偷之fread结束判断 楠哥给了个程序,不知道为什么用fsocket链接,然后用fread或fgets读数据,每一步都是跟手册一致,但是feof总是没有办法正确判断已经到文件尾部,必须要等到超时才推出……所以速度超级慢…… 构造头部部分及链接部分手册有很多例子,不再赘述……

解决办法如下:
$sock = fsockopen($host_url,80,$errno,$errstr,5);
stream_set_timeout($sock, 1);//设置流超时,如果对方服务器缓慢的话你可以设的值大一些~~
$temp='';
while (($temp=fread($sock,8096))) {//每次读取8096字节,如果对方的网速偏慢的话也适当减小
$body.=$temp;
}
echo $body;
ok,很简单吧~~~设置流超时为1秒,然后每次判断fread获取的是否为空,若为空的话则说明已经读取结束,若不设置流超时,默认可能要等待60秒吧…… 终于知道为什么之前没办法判断结束了…… 在构造头部的时候没有加入Connection: Closernrn这一句做结束,所以feof就总是无法判断结束,貌似是持久链接的问题,具体rfc描述如下: 一些客户机和服务器可能希望和一些对以前实现HTTP/1.0持续连接的客户机和服务器兼容。单个持续连接不是缺省的行为的时候,它就被明确的越过。HTTP/1.0持续连接的实验性实现是有缺陷的,在HTTP/1.1中设计新的简单的来纠正这些问题。问题是一些现有的1.0客户机可能发送Keep-Alive给一个不明白这种连接的代理服务器,那么就错误地将它传向下一个接收服务器,它将建立一个Keep-Alive连接并导致一个挂着的HTTP/1.0代理等待关闭的应答。结果是HTTP/1.0客户机必须禁止使用Keep-Alive和代理交谈。 然而,和代理交谈是持续连接最重要的用处,所以禁止很明显是无法接受的。因此,我们需要一些其它的机制来表明渴望持续连接,甚至当和一个忽略Connection的老代理交谈这样使用也是安全的。对HTTP/1. 0消息持续连接是缺省的;我们引入一个新的关键字(Connection:close)来申明非持续。 最新测试发现只要传递Connection: 任意字符甚至可以为空rnrn都feof都能正常获取结束标志……看来是头部构造的不完整…… 不知道,我也不知道,哪位大仙知道为啥跟我说一下吧~~~~ 发个正确的函数(不是我写~~~)

function get_content($url){
$urlparts = parse_url($url);
$script_name = $urlparts['path'];
$host_url = $urlparts['host'];
$fp = fsockopen($host_url, 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)
n";
} else {
$out = "GET / HTTP/1.1rn";
$out .= "Host: www.ea3w.comrn";
//$out .= "添加头部的其他部分";
$out .= "Connection: Closernrn";
fwrite($fp, $out);
while (!feof($fp)) {
echo fgets($fp, 1028);
}
fclose($fp);
}
}

分享本文到:
除非特殊注明,本文版权归原作者所有,欢迎转载!转载请注明版权以及本文地址,谢谢。
转载保留版权:Pakey's BLog >>网络采集 >>php小偷之模拟登陆并抓取附件--论坛小偷
本文地址:http://www.pakey.net/blog/php-login-getfujian.html