Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4183801
  • 博文数量: 601
  • 博客积分: 15410
  • 博客等级: 上将
  • 技术积分: 6884
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-16 08:11
个人简介

独学而无友,则孤陋而寡闻!

文章分类

全部博文(601)

文章存档

2020年(1)

2018年(4)

2017年(7)

2016年(42)

2015年(25)

2014年(15)

2013年(36)

2012年(46)

2011年(117)

2010年(148)

2009年(82)

2008年(37)

2007年(41)

分类: PHP

2017-07-20 15:59:47

 %E7%9A%84%E5%A4%9A%E7%BA%BF%E7%A8%8Bcurl_multi    2015-04-27
这篇日志发布时间已经超过一年,许多内容可能已经失效,请读者酌情参考。

最近在写python,做了一个抓取150个网站标题的脚本,被python的便捷、多线程和执行稳定性感动的要哭。然后想起了去年草稿箱里面的这篇文章,回首再看感慨万分,赶紧把剩下的部分写完,关于php多线程curl的几个大坑。


需求大概是这样:

要查询150个网站的存活情况,并抓取目标网站的标题。

实现起来很简单,将150个网站添加到循环,逐一执行curl即可。但是作为一个有点追求的码农,即便是150个网站也想着怎么去优化下,于是便使用了php的多线程curl(即curl_multi_*),没想到手册资料少,坑踩了一个又一个。

特此记录,分享&备忘。

0x01 相关函数 curl_multi_*

php手册参考:

http://php.net/manual/zh/book.curl.php

curl_multi_init()相关:

http://php.net/manual/zh/function.curl-multi-init.php

手册说的很简单,并附了一个演示脚本,如下。

 0);

// 关闭全部句柄
curl_multi_remove_handle($mh, $ch1);
curl_multi_remove_handle($mh, $ch2);
curl_multi_close($mh);

演示脚本说明了工作流程。

1、curl_multi_init,初始化多线程curl批处理会话。

2、curl_multi_add_handle,将具体执行任务的curl添加到multi_curl批处理会话。

3、curl_multi_exec,真正开始执行curl任务。

4、curl_multi_getcontent,获取执行结果。

5、curl_multi_remove_handle,将完成了的curl移出批处理会话。

6、curl_multi_close,关闭curl批处理会话。

0x02 问题来了

好了,问题来了,不是挖掘机。

使用以上脚本会出现多个问题:

1、在执行do...while语句时经常死循环,无法停止。

2、为每个curl实例设置了超时时间,但是整个脚本执行时间跟这个超时时间竟然相同!这不科学!

3、多线程在哪里?php自动处理的?

4、(增强版问题)改进了脚本,但是执行结果不稳定。添加150个目标时,经常只有140+个目标有返回结果,出现随机丢掉几个任务的情况。

0x03 踩坑和解决问题

关于这个死循环的问题,很多人也遇到过,在php的在线手册上,就有评论说明了这个问题:

http://php.net/manual/zh/function.curl-multi-init.php#115055

http://php.net/manual/zh/function.curl-multi-select.php#110869

http://php.net/manual/zh/function.curl-multi-select.php#108928


	

简单来说就是curl_multi_select()可能会一直返回-1,如果写出了类似上面的代码可能就会遇到死循环了。注意这些问题除了受代码编写的影响,还受php和libcurl版本的影响,总而言之升级版本吧。

至于像 CURLM_CALL_MULTI_PERFORM 之类的预定义常量,php方面并没有详细解释,多半靠看名字猜,呵呵。

其实这些常量是由libcurl库定义的,参考地址:

http://curl.haxx.se/libcurl/c/libcurl-errors.html

CURLM_CALL_MULTI_PERFORM (-1)

This is not really an error. It means you should call curl_multi_perform again without doing select() or similar in between. Before version 7.20.0 this could be returned by curl_multi_perform, but in later versions this return code is never used.

当返回值为-1时,并不意味着这是一个错误,只是说明select时没有并没有完成excute,描述给的建议是不要执行select等阻塞操作,立即exec。

但是在libcurl的7.20版本之后,不再使用这个返回值了,原因是这个循环libcurl自己做了,就不再需要我们手动循环了。

同时注意curl_multi_select,其实还有第二个参数timeout,根据语焉不详的手册,这货应该是自带阻塞,所以就不再需要手动sleep了。

综上我们的代码看起来应该是这样的:

(.*)<\/title>/isU", $html, $title);
    return empty($title[1]) ? '未能获取标题' : $title[1];
}

echo "抓取完成!\n";
$end_time = microtime();
$start_time = explode(" ", $start_time);
$end_time = explode(" ",$end_time);
$execute_time = round(($end_time[0] - $start_time[0] + $end_time[1] - $start_time[1]) * 1000) / 1000;
$execute_time = sprintf("%s", $execute_time);
echo "脚本运行时间:{$execute_time} 秒\n";

然后就遇到了问题2和3。

整个脚本的执行时间就是30秒多一点,刚好是为每个curl设置的超时时间,这显然不科学啊。

执行速度确实挺快,30秒也获取到了相当数量的标题,但是多线程体现在哪?这是多少线程?

这俩问题曾经困扰我很长一段时间。。。其实答案很简单。。。

线程数就是150,所以这150个请求在同时完成,整个脚本的执行时间就是30秒多一点。

但其实php并不能很好的处理这150个线程,导致很多目标获取标题失败了。另外我如果有150k目标要请求,难道要开150k个线程?

这就需要自己实现一个线程池,来掌控任务进度。

思路就是用curl_multi_remove_handle一次添加n个url到multi_curl中,这个n就是线程数,这n个的组合队列就是线程池。

每执行完毕一个任务,就将对应的curl移除队列并销毁,同时加入新的目标,直至150个对象依次执行完毕。这样做的好处是,能保证线程池中始终有n个任务在进行,不必等这n个任务执行完毕后再执行下n个任务。

思路有了,所以我们的代码看来是这样的:

 0)
            break;
    }while($running);

    while($info = curl_multi_info_read($mh)){
        $ch = $info['handle'];
        $url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
        $total_time += curl_getinfo($ch, CURLINFO_TOTAL_TIME);
        
        if($info['result'] == CURLE_OK){
            $content = curl_multi_getcontent($ch);
            $detail = getTitle($content);
        }else
            $detail =  'cURL Error(' . curl_error($ch) . ").";    
        
        curl_multi_remove_handle($mh, $ch);
        curl_close($ch);
        unset($ch);
        
        if($targets){
            $new_task = curl_init(array_pop($targets));
            curl_setopt_array($new_task, $opt);
            curl_multi_add_handle($mh, $new_task);//要设置每个curl实例的属性
            // 手动执行,保证 $running 更新,感谢@rainyluo 反馈,update@2016-04-16。
            curl_multi_exec($mh, $running);
        }
        echo "[$running][$index][", date('H:i:s'), "]{$url}:{$detail}\n";
        $index++;
    }

}while($running);

curl_multi_close($mh);

function getTitle($html){
    preg_match("/(.*)<\/title>/isU", $html, $title);
    return empty($title[1]) ? '未能获取标题' : $title[1];
}

echo "抓取完成!\n";
$end_time = microtime();
$start_time = explode(" ", $start_time);
$end_time = explode(" ", $end_time);
$execute_time = round(($end_time[0] - $start_time[0] + $end_time[1] - $start_time[1]) * 1000) / 1000;
$execute_time = sprintf("%s", $execute_time);
echo "http请求时间:{$total_time} 秒\n";
echo "脚本运行时间:{$execute_time} 秒\n";</pre>
	<p style="text-align:center;">
		<img src="https://crazydb.com/upload/image/20150428/1430159608673505.png" title="1430159608673505.png" alt="1430159608673505.png" style="white-space:normal;width:480px;height:344px;" border="0" height="344" vspace="0" width="480" />
	</p>
	<p>
		上面的代码执行效果就比较理想了,问题3也解决了,只需要注意两个小地方。
	</p>
	<p>
		一是关于multi_curl_select函数,这个函数手册是这么说的:
	</p>
	<blockquote>
		<p>
			成功时返回描述符集合中描述符的数量。失败时,select失败时返回-1,否则返回超时(从底层的select系统调用).
		</p>
		<p>
			On
 success, returns the number of descriptors contained in the descriptor 
sets. This may be 0 if there was no activity on any of the descriptors. 
On failure, this function will return -1 on a select failure (from the 
underlying select system call).
		</p>
	</blockquote>
	<p>
		擦他大爷,这中文翻译绝壁是机翻,对我造成了100000000点伤害。
	</p>
	<p>
		-1
的返回值是从系统底层调用产生的,应该是libcurl给php的返回结果,这说明select执行失败,需要阻塞一段时间后再次执行;0是没有任务活动
链接,据我观察(- 
-目测的,深究的话得去看php的代码,请路过的大牛们指正),应该是底层请求处于阻塞状态,可能是正在解析域名或者timeout进行中,或者mh中所
有任务执行完毕;正整数表示有正常的活动链接,说明mh中还有未完成的任务。
	</p>
	<p>
		为了细化处理多线程curl每个请求的执行结果,我在curl_multi_select的返回值大于0的时候也跳出了当前exec循环,并通过curl_multi_info_read来获取已经完成的任务信息。这里封装下就可以做个回调,精细化处理每个任务。
	</p>
	<p>
		另
一个地方就是注意curl_multi_info_read需要多次调用。这个函数每次调用返回已经完成的任务信息,直至没有已完成的任务。问题4产生的
原因就是因为我当时用了if没用while,这是一个小坑,但坑了我相当长的时间。当时非常无奈的解决方式是监控了整个执行过程,在所有任务完成后清点队
列,把遗漏的再取出来。。。
	</p>
	<h3 style="white-space:normal;text-align:left;">
		0x04 总结
	</h3>
	<p>
		上面的代码只是demo,按照面向过程的方式写了出来。如果要用在其他地方,还得把线程池管理,任务回调等再封装下。然后配合一些html解析库就能做个小爬虫自娱自乐了。。。
	</p>
	<p>
		php在多线程和异步等方面存在天生的缺陷,很多东西php能写,但是效果不如python,python实现起来可能更容易、更轻松。还是得看使用场景啊,不过php依然是最好的语言 :)
	</p>
</div>
<br />
</pre>                                   </div>
            <!-- <div class="Blog_con3_1">管理员在2009年8月13日编辑了该文章文章。</div> -->
            <div class="Blog_con2_1 Blog_con3_2">
              <div>
			  <!--<img src="/image/default/tu_8.png">-->
			  <!-- JiaThis Button BEGIN -->
				<div class="bdsharebuttonbox"><A class=bds_more href="#" data-cmd="more"></A><A class=bds_qzone title=分享到QQ空间 href="#" data-cmd="qzone"></A><A class=bds_tsina title=分享到新浪微博 href="#" data-cmd="tsina"></A><A class=bds_tqq title=分享到腾讯微博 href="#" data-cmd="tqq"></A><A class=bds_renren title=分享到人人网 href="#" data-cmd="renren"></A><A class=bds_weixin title=分享到微信 href="#" data-cmd="weixin"></A></div>
<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
				<!-- JiaThis Button END -->
			  </div>
              阅读(1797) | 评论(0) | 转发(0) |
			                <div class="HT_line3"></div>
            </div>
            
            <div class="Blog_con3_3">
              <div><span id='digg_num'>0</span><a href="javascript:void(0)" id='digg' bid='5767968' url='/blog/digg.html' ></a></div>
              <p>上一篇:<a href="/uid-20332519-id-5766857.html">在Linode中使用FreeBSD11</a></p>
              <p>下一篇:<a href="/uid-20332519-id-5783895.html">mpd5报错tried to use non-present sched_yield</a></p>    
            </div>
            
          </div>
          		  <!--
          <div class="Blog_con3_4 Blog_con3_5">
            <div class="Blog_tit2 Blog_tit7">热门推荐</div>
            <ul>
			              <li><a href="" title="" target='blank' ></a></li>
			            </ul>
          </div>
		  -->
        </div>
      </div>
      <div class="Blog_right1_7" id='replyList'>
		<div class="Blog_tit3">给主人留下些什么吧!~~</div>
				<!--暂无内容-->
				<!-- 评论分页-->
		<div class="Blog_right1_6 Blog_right1_12">
        		</div>
		<!-- 评论分页-->
        <div class="Blog_right1_10" style="display:none">
          <div class="Blog_tit3">评论热议</div>
		  		 <!--未登录 -->
        <div class="Blog_right1_8">
          <div class="nologin_con1"> 请登录后评论。
            <p><a href="http://account.chinaunix.net/login"  onclick="link(this)">登录</a> <a href="http://account.chinaunix.net/register?url=http%3a%2f%2fblog.chinaunix.net">注册</a></p>
          </div>
        </div>
       
		
        </div>
        <div style="text-align:center;margin-top:10px;">
            <script type="text/javascript" smua="d=p&s=b&u=u3118759&w=960&h=90" src="//www.nkscdn.com/smu0/o.js"></script>
        </div>
      </div>
    </div>
  </div>
  <input type='hidden' id='report_url' value='/blog/ViewReport.html' />

<script type="text/javascript">
  	//测试字符串的长度 一个汉字算2个字节
	function mb_strlen(str)
	{
		var len=str.length;
		var totalCount=0;
		for(var i=0;i<len;i++)
		{
			var c = str.charCodeAt(i);
			if ((c >= 0x0001 && c <= 0x007e) || (0xff60<=c && c<=0xff9f)) {
				totalCount++;
			}else{
				totalCount+=2;
			}
		}
		return totalCount;
	}
	/*
	var Util = {};
	Util.calWbText = function (text, max){
		if(max === undefined)
			max = 500;
		var cLen=0;
		var matcher = text.match(/[^\x00-\xff]/g),
			wlen  = (matcher && matcher.length) || 0;
		//匹配url链接正则 http://***
		var pattern = /http:\/\/([\w-]+\.)+[\w-]+(\/*[\w-\.\/\?%&=][^\s^\u4e00-\u9fa5]*)?/gi;
		//匹配的数据存入数组
		var arrPt = new Array();
		var i = 0;
		while((result = pattern.exec(text)) != null){
			arrPt[i] = result[0];
			i++;
		}
		//替换掉原文本中的链接
		for(var j = 0;j<arrPt.length;j++){
			text = text.replace(arrPt[j],"");
		}
		//减掉链接所占的长度
		return Math.floor((max*2 - text.length - wlen)/2 - 12*i);
	};
	*/
	var allowComment = '0';
	
	//举报弹出层
	function showJuBao(url, cid){
		
			$.cover(false);
			asyncbox.open({
				id  : 'report_thickbox',
				url : url,
				title : '举报违规',
				width : 378,
				height : 240,
				scroll : 'no',
				data : {
					'cid'	 : cid,
					'idtype' : 2 ,
                                        'blogurl' : window.location.href
				},
				callback : function(action){
					if(action == 'close'){
						$.cover(false);
					}
				}
			});
	}

	$(function(){

		//创建管理员删除的弹出层
		$('#admin_article_del').click(function(){
			$.cover(false);
			asyncbox.open({
				id : 'class_thickbox',
				html : '<div class="HT_layer3_1"><ul><li class="HT_li1">操作原因:<select class="HT_sel7" id="del_type" name="del_type"><option value="广告文章">广告文章</option><option value="违规内容">违规内容</option><option value="标题不明">标题不明</option><option value="文不对题">文不对题</option></select></li><li class="HT_li1" ><input class="HT_btn4" id="admin_delete"  type="button" /></li></ul></div>',
				title : '选择类型',
				width : 300,
				height : 150,
				scroll : 'no',
				callback : function(action){
					if(action == 'close'){
						$.cover(false);
					}
				}
			});
		});
		$('#admin_delete').live('click' , function(){
			///blog/logicdel/id/3480184/url/%252Fblog%252Findex.html.html
			var type = $('#del_type').val();
			var url = '/blog/logicdel/id/5767968/url/%252Fuid%252F20332519.html.html';
			window.location.href= url + '?type=' + type;
		});


		//顶  js中暂未添加&过滤 
		$('#digg').live('click' , function(){

			if(isOnLine == '' )
			{
				//showErrorMsg('登录之后才能进行此操作' , '消息提示');
				showErrorMsg('操作失败,您需要先登录!', '消息提示', 'http://account.chinaunix.net/login');
				return false;
			}

			var bid = $('#digg').attr('bid');
			var url = $('#digg').attr('url');

			var digg_str = $.cookie('digg_id');
			if(digg_str != null)
			{
				var arr= new Array(); //定义一数组

				arr = digg_str.split(","); //字符分割     
				for( i=0 ; i < arr.length ; i++ )   
				{   
					if(bid == arr[i])
					{
						showErrorMsg('已经赞过该文章', '消息提示'); 
						return false;
					}
				} 
			}
			$.ajax({
				type:"POST",
				url:url,	
				data: {
					'bid' : bid 
				},
				dataType: 'json',
				success:function(msg){
					if(msg.error == 0)
					{
						var num = parseInt($('#digg_num').html(),10);
						num += 1;
						$('#digg_num').html(num);
						$('#digg').die();

						if(digg_str == null)
						{
							$.cookie('digg_id', bid, {expires: 30 , path: '/'});
						}
						else
						{
							$.cookie('digg_id', digg_str + ',' + bid, {expires: 30 , path: '/'});
						}
						showSucceedMsg('谢谢' , '消息提示');
					}
					else if(msg.error == 1)
					{
						//showErrorMsg(msg.error_content , '消息提示'); 
						showErrorMsg('操作失败,您需要先登录!', '消息提示', 'http://account.chinaunix.net/login');
					}
					else if(msg.error == 2)
					{
						showErrorMsg(msg.error_content , '消息提示'); 
					}
				}
			});
		});
		//举报弹出层
		/*$('.box_report').live('click' , function(){
			if(isOnLine == '' )
			{
				//showErrorMsg('登录之后才能进行此操作' , '消息提示');
				showErrorMsg('操作失败,您需要先登录!', '消息提示', 'http://account.chinaunix.net/login');
				return false;
			}
			var url = $('#report_url').val();
			var cid = $(this).attr('cid');
			$.cover(false);
			asyncbox.open({
				id  : 'report_thickbox',
				url : url,
				title : '举报违规',
				width : 378,
				height : 240,
				scroll : 'no',
				data : {
					'cid'	 : cid,
					'idtype' : 2 
				},
				callback : function(action){
					if(action == 'close'){
						$.cover(false);
					}
				}
			});
		});*/


		//评论相关代码
		
		//点击回复显示评论框
		$('.Blog_a10').live('click' , function(){
			if(isOnLine == '' )
			{
				//showErrorMsg('登录之后才能进行此操作' , '消息提示');
				showErrorMsg('操作失败,您需要先登录!', '消息提示', 'http://account.chinaunix.net/login');
				return false;
			}

			if(allowComment == 1)
			{
				showErrorMsg('该博文不允许评论' , '消息提示'); 
				return false;
			}
			var tid = $(this).attr('toid');//留言作者id
			var bid = $(this).attr('bid');//blogid
			var cid = $(this).attr('cid');//留言id
			var tname = $(this).attr('tname');
                         			var tpl  = '<div class="Blog_right1_9">';
				tpl +=	 '<div class="div2">';
				tpl +=     '<textarea name="" cols="" rows="" class="Blog_ar1_1" id="rmsg">文明上网,理性发言...</textarea>';
				tpl +=	 '</div>';
				tpl +=   '<div class="div3">';
				tpl +=		'<div class="div3_2"><a href="javascript:void(0);" class="Blog_a11" id="quota_sbumit" url="/Comment/PostComment.html" tid="'+tid+'" bid="'+bid+'" cid="'+cid+'" tname="'+tname+'" ></a><a href="javascript:void(0)" id="qx_comment" class="Blog_a12"></a></div>';
				tpl +=		'<div class="div3_1"><a href="javascript:void(0);" id="mface"><span></span>表情</a></div>';
				tpl +=		'<div class="clear"></div>';
				tpl +=	 '</div>';
				tpl +=  '</div>';
                                 			$('.z_move_comment').html('');
			$(this).parents('.Blog_right1_8').find('.z_move_comment').html(tpl).show();
		});
		//引用的评论提交
		$('#quota_sbumit').live('click' , function(){

			if(isOnLine == '' )
			{
				//showErrorMsg('登录之后才能进行此操作' , '消息提示');
				showErrorMsg('操作失败,您需要先登录!', '消息提示', 'http://account.chinaunix.net/login');
				return false;
			}

			var bid   = $(this).attr('bid');
			var tid   = $(this).attr('tid');//被引用人的id
			var qid   = $(this).attr('cid');//引用的id
			var url = $(this).attr('url');
			var text = $('#rmsg').val();
			var tname = $(this).attr('tname');
			if(text == '' || text=='文明上网,理性发言...')
			{
				showErrorMsg('评论内容不能为空!' , '消息提示'); 
				return false;
			}
			else
			{
				if(mb_strlen(text) > 1000){
					showErrorMsg('评论内容不能超过500个汉字' , '消息提示'); 
					return false;
				}
			}
		    $.ajax({
		        type: "post",
			    url: url ,
			    data: {'bid': bid , 'to' : tid , 'qid' : qid , 'text': text , 'tname' : tname },
				dataType: 'json',
			    success: function(data){
				    if(data.code == 1){
						var tpl =  '<div class="Blog_right1_8">';
							tpl+=     '<div class="Blog_right_img1"><a href="' +data.info.url+ '" >' + data.info.header + '</a></div>';
							tpl+=     '<div class="Blog_right_font1">';
							tpl+=         '<p class="Blog_p5"><span><a href="' +data.info.url+ '" >' + data.info.username + '</a></span>' + data.info.dateline + '</p>';
							tpl+=         '<p class="Blog_p7"><a href="' + data.info.quota.url + '">' + data.info.quota.username + '</a>:'+ data.info.quota.content + '</p>';
							tpl+=         '<p class="Blog_p8">' + data.info.content + '</p><span class="span_text1"><a href="javascript:void(0);" class="Blog_a10" toid=' + data.info.fuid + ' bid=' + data.info.bid + ' cid=' + data.info.cid + '  tname = ' + data.info.username + ' >回复</a> |  <a class="comment_del_mark" style="cursor:pointer" url="' + data.info.delurl + '" >删除</a> |  <a href="javascript:showJuBao(\'/blog/ViewReport.html\','+data.info.cid+')" class="box_report" cid="' + data.info.cid + '" >举报</a></span></div>';
							tpl+=         '<div class="z_move_comment" style="display:none"></div>';
							tpl+=	      '<div class="Blog_line1"></div></div>';
							$('#replyList .Blog_right1_8:first').before(tpl);
							$('.z_move_comment').html('').hide();
				    }
					else if(data.code == -1){
						//showErrorMsg(data.info , '消息提示'); 
						showErrorMsg('操作失败,您需要先登录!', '消息提示', 'http://account.chinaunix.net/login');
			        }
                },
			    error: function(){//请求出错处理
                        
                }
			});
		});
		//底部发表评论
		$('#submitmsg').click(function(){
			if(allowComment == 1)
			{
				showErrorMsg('该博文不允许评论' , '消息提示'); 
				return false;
			}
			var bid   = $(this).attr('bid');
			var toid  = $(this).attr('toid');
			var text = $('#reply').val();
			var url = $(this).attr('url');
			if(text == '' || text=='文明上网,理性发言...')
			{
				showErrorMsg('评论内容不能为空!' , '消息提示'); 
				return false;
			}
			else
			{
				if(mb_strlen(text) > 1000){
					showErrorMsg('评论内容不能超过500个汉字' , '消息提示'); 
					return false;
				}
			}
		    $.ajax({
		        type: "post",
			    url: url ,
			    data: {'bid': bid , 'to' : toid ,'text': text},
				dataType: 'json',
			    success: function(data){
				    if(data.code == 1)
					{
						var tpl   = '<div class="Blog_right1_8">';
							tpl  +=   '<div class="Blog_right_img1"><a href="' +data.info.url+ '" >' + data.info.header + '</a></div>';
							tpl  +=	  '<div class="Blog_right_font1">';
							tpl  +=       '<p class="Blog_p5"><span><a href="' +data.info.url+ '" >' + data.info.username + '</a></span>' + data.info.dateline + '</p>';
							tpl  +=       '<p class="Blog_p6">' + data.info.content + '</p>';
							tpl  +=		  '<div class="div1"><a href="javascript:void(0);" class="Blog_a10"  toid=' + data.info.fuid + ' bid=' + data.info.bid + ' cid=' + data.info.cid + '>回复</a> |  <a class="comment_del_mark" style="cursor:pointer" url="' + data.info.delurl + '">删除</a> |  <a href="javascript:showJuBao(\'/blog/ViewReport.html\','+data.info.cid+')" class="box_report" cid="' + data.info.cid + '">举报</a></div>';
							tpl  +=		  '<div class="z_move_comment" style="display:none"></div>';
							tpl  +=    '</div><div class="Blog_line1"></div></div>';
							$('.Blog_tit3:first').after(tpl);
							$('#reply').val('文明上网,理性发言...');
					}
					else if(data.code == -1)
					{
						showErrorMsg(data.info , '消息提示'); 
			        }
                },
			    error: function(){//请求出错处理
                        
                }
		    });
			
		});
		//底部评论重置
		$('#reset_comment').click(function(){
			$('#reply').val('文明上网,理性发言...');
		});
		//取消回复
		$('#qx_comment').live('click' , function(){
			$('.z_move_comment').html('').hide();
		});


		$('#rmsg, #reply').live({
		    focus:function(){
			    if($(this).val() == '文明上网,理性发言...'){
			        $(this).val('');
			    }
			},
			blur:function(){
		        if($(this).val() == ''){
			        $(this).val('文明上网,理性发言...');
			    }
			}
		});
		//删除留言确认
		$('.comment_del_mark').live('click' , function(){
			var url = $(this).attr('url');
			asyncbox.confirm('删除留言','确认', function(action){
				if(action == 'ok')
				{
					location.href = url;
				}
			});
		});
		//删除时间确认
		$('.del_article_id').click(function(){
			var delurl = $(this).attr('delurl');
			asyncbox.confirm('删除文章','确认', function(action){
				if(action == 'ok')
				{
					location.href = delurl;
				}
			});
		});
		/*
		//字数限制
		$('#rmsg, #reply').live('keyup', function(){
			var id = $(this).attr('id');
			var left = Util.calWbText($(this).val(), 500);
			var eid = '#errmsg';
			
			if(id == 'reply') eid =  '#rerrmsg';
			if (left >= 0)
		        $(eid).html('您还可以输入<span>' + left + '</span>字');
		    else
		        $(eid).html('<font color="red">您已超出<span>' + Math.abs(left) + '</span>字 </font>');
		});
		*/
		//加载表情
	    $('#face').qqFace({id : 'facebox1', assign : 'reply', path : '/image/qqface/'});
	    $('#mface').qqFace({id : 'facebox', assign : 'rmsg', path:'/image/qqface/'});
	    
		/*
		$('#class_one_id').change(function(){
			alert(123213);
			var id = parseInt($(this).val() , 10);
			if(id == 0) return false;
			$('.hidden_son_class span').each(function( index , dom ){
				if( dom.attr('cid')  == id )
				{
				}
			});
		});
		*/
		//转载文章
		var turn_url = "/blog/viewClassPart.html";
		$('#repost_bar').click(function(){
			if(isOnLine == '' )
			{
				//showErrorMsg('登录之后才能进行此操作' , '消息提示');
				showErrorMsg('操作失败,您需要先登录!', '消息提示', 'http://account.chinaunix.net/login');
				return false;
			}
			var id = $(this).attr('bid');
			asyncbox.open({
				id  : 'turn_class_thickbox',
				url : turn_url,
				title : '转载文章',
				width : 330,
				height : 131,
				scroll : 'no',
				data : {
					'id'	 : id
				},
				callback : function(action){
					if(action == 'close'){
						$.cover(false);
					}
				}
			});
		});
		/*
	    //转发文章
	    $('#repost_bar').live('click' , function(){
			if(isOnLine == '' )
			{
				//showErrorMsg('登录之后才能进行此操作' , '消息提示');
				showErrorMsg('操作失败,您需要先登录!', '消息提示', 'http://account.chinaunix.net/login');
				return false;
			}
	    	var bid  = $(this).attr('bid');
	    	var url  = $(this).attr('url');
	    	asyncbox.confirm('转载文章','确认', function(action){
	    	  if(action == 'ok'){
	    	    $.ajax({
				type:"POST",
				url:url,	
				data: {
					'bid' : bid 
				},
				dataType: 'json',
				success:function(msg){
				  if(msg.error == 0){
				    showSucceedMsg('转发成功!', '消息提示');
				  }else if(msg.error == 1){
				    //location.href = '/index.php?r=site/login';
					showErrorMsg('操作失败,您需要先登录!', '消息提示', 'http://account.chinaunix.net/login');
				  }else{
				    showErrorMsg(msg.error_content, '消息提示');
			      }
				}
			  });
	    	  }
	    	});

		});
		*/

	});
</script>
<!--该部分应该放在输出代码块的后面才起作用 -->
<script type="text/javascript">

SyntaxHighlighter.autoloader(
	'applescript			/highlight/scripts/shBrushAppleScript.js',
	'actionscript3 as3		/highlight/scripts/shBrushAS3.js',
	'bash shell				/highlight/scripts/shBrushBash.js',
	'coldfusion cf			/highlight/scripts/shBrushColdFusion.js',
	'cpp c					/highlight/scripts/shBrushCpp.js',
	'c# c-sharp csharp		/highlight/scripts/shBrushCSharp.js',
	'css					/highlight/scripts/shBrushCss.js',
	'delphi pascal			/highlight/scripts/shBrushDelphi.js',
	'diff patch pas			/highlight/scripts/shBrushDiff.js',
	'erl erlang				/highlight/scripts/shBrushErlang.js',
	'groovy					/highlight/scripts/shBrushGroovy.js',
	'java					/highlight/scripts/shBrushJava.js',
	'jfx javafx				/highlight/scripts/shBrushJavaFX.js',
	'js jscript javascript	/highlight/scripts/shBrushJScript.js',
	'perl pl				/highlight/scripts/shBrushPerl.js',
	'php					/highlight/scripts/shBrushPhp.js',
	'text plain				/highlight/scripts/shBrushPlain.js',
	'py python				/highlight/scripts/shBrushPython.js',
	'ruby rails ror rb		/highlight/scripts/shBrushRuby.js',
	'scala					/highlight/scripts/shBrushScala.js',
	'sql					/highlight/scripts/shBrushSql.js',
	'vb vbnet				/highlight/scripts/shBrushVb.js',
	'xml xhtml xslt html	/highlight/scripts/shBrushXml.js'
);
SyntaxHighlighter.all();


function code_hide(id){
	var code = document.getElementById(id).style.display;
	if(code == 'none'){
		document.getElementById(id).style.display = 'block';
	}else{
		document.getElementById(id).style.display = 'none';
	}
}
</script>
<!--回顶部js2011.12.30-->
<script language="javascript">
 lastScrollY=0;
 function heartBeat(){
 var diffY;
 if (document.documentElement && document.documentElement.scrollTop)
     diffY = document.documentElement.scrollTop;
 else if (document.body)
     diffY = document.body.scrollTop
 else
     {/*Netscape stuff*/}
 percent=.1*(diffY-lastScrollY);
 if(percent>0)percent=Math.ceil(percent);
 else percent=Math.floor(percent);
 document.getElementById("full").style.top=parseInt(document.getElementById("full").style.top)+percent+"px";
 lastScrollY=lastScrollY+percent;
 if(lastScrollY<200)
 {
 document.getElementById("full").style.display="none";
 }
 else
 {
 document.getElementById("full").style.display="block";
 }
 }
 var gkuan=document.body.clientWidth;
 var ks=(gkuan-960)/2-30;
 suspendcode="<div id=\"full\" style='right:-30px;POSITION:absolute;TOP:500px;z-index:100;width:26px; height:86px;cursor:pointer;'><a href=\"javascript:void(0)\" onclick=\"window.scrollTo(0,0);\"><img src=\"\/image\/top.png\" /></a></div>"
 document.write(suspendcode);
 window.setInterval("heartBeat()",1);
 </script>
  <!-- footer -->
  <div class="Blog_footer" style='clear:both'>
    <div><a href="http://www.chinaunix.net/about/index.shtml" target="_blank" rel="nofollow">关于我们</a> | <a href="http://www.it168.com/bottomfile/it168.shtml" target="_blank" rel="nofollow">关于IT168</a> | <a href="http://www.chinaunix.net/about/connect.html" target="_blank" rel="nofollow">联系方式</a> | <a href="http://www.chinaunix.net/about/service.html" target="_blank" rel="nofollow">广告合作</a> | <a href="http://www.it168.com//bottomfile/flgw/fl.htm" target="_blank" rel="nofollow">法律声明</a> | <a href="http://account.chinaunix.net/register?url=http%3a%2f%2fblog.chinaunix.net" target="_blank" rel="nofollow">免费注册</a>
      <p>Copyright  2001-2010 ChinaUnix.net All Rights Reserved 北京皓辰网域网络信息技术有限公司. 版权所有 </p>
      <div>感谢所有关心和支持过ChinaUnix的朋友们
        <p><a href="http://beian.miit.gov.cn/">16024965号-6 </a></p>
      </div>
    </div>
  </div>
</div>
<script language="javascript">

//全局错误提示弹出框
function showErrorMsg(content, title, url){
	var url = url || '';
	var title = title || '消息提示';
	var html = '';
	html += '<div class="HT_layer3_1 HT_layer3_2"><ul><li><p><span class="login_span1"></span>' + content + '</p></li>';
	if(url == '' || url.length == 0){
		html += '<li class="HT_li1"><input type="button" class="HT_btn2"  onclick=\'close_windows("error_msg")\'></li>';
	} else {
		html += '<li class="HT_li1"><input type="button" class="login_btn1" onclick="location.href=\'' + url + '\'"></li>';
	}
	html += '</ul></div>';
	$.cover(true);
	   asyncbox.open({
		 id: 'error_msg',
		 title : title,
		 html : html,
		 'callback' : function(action){
			 if(action == 'close'){
				 $.cover(false);
			 }
		 }
	});
}

//全局正确提示
function showSucceedMsg(content, title , url ){
	var url = url || '';
	var title = title || '消息提示';
	var html = '';
	html += '<div class="HT_layer3_1 HT_layer3_2"><ul><li><p><span class="login_span2"></span>' + content + '</p></li>';
	if(url == '' || url.length == 0){
		html += '<li class="HT_li1"><input type="button" class="HT_btn2"  onclick=\'close_windows("error_msg")\'></li>';
	} else {
		html += '<li class="HT_li1"><input type="button" class="HT_btn2" onclick="location.href=\'' + url + '\'"></li>';
	}
	html += '</ul></div>';
	$.cover(true);
	asyncbox.open({
		 id: 'error_msg',
		 title : title,
		 html : html,
		 'callback' : function(action){
			 if(action == 'close'){
				 $.cover(false);
			 }
		 }
	});
}

//关闭指定id的窗口
function close_windows(id){
	$.cover(false);
	$.close(id);
}


//公告
var tID;
var tn;                        // 高度
var nStopTime = 5000;        // 不同行间滚动时间隔的时间,值越小,移动越快
var nSpeed = 50;            // 滚动时,向上移动一像素间隔的时间,值越小,移动越快
var isMove = true;
var nHeight = 25;
var nS = 0;
var nNewsCount = 3;

/**
 * n 用于表示是否为第一次运行
 **/
function moveT(n)
{
    clearTimeout(tID)
    var noticev2 = document.getElementById("noticev2")
    nS = nSpeed;

    // 只在第一次调用时运行,初始化环境(有没有参数)
    if (n)
    {
        // 设置行高
        noticev2.style.lineHeight = nHeight + "px";
        // 初始化显示位置
        tn = 0;
        // 刚进入时在第一行停止时间
        nS = nStopTime;
    }

    // 判断鼠标是否指向层
    if (isMove)
    {
        // 向上移动一像素
        tn--;
        // 如果移动到最下面一行了,则移到顶行
        if (Math.abs(tn) == nNewsCount * nHeight)
        {
            tn = 0;
        }
        // 设置位置
        noticev2.style.marginTop = tn + "px";
        // 完整显示一行时,停止一段时间
        if (tn % nHeight == 0)
        {
            nS = nStopTime;
        }
    }

    tID = setTimeout("moveT()", nS);
}
moveT(1);    // 此处可以传入任何参数
</script>
<script type="text/javascript">
//  var _gaq = _gaq || [];
//  _gaq.push(['_setAccount', 'UA-20237423-2']);
//  _gaq.push(['_setDomainName', '.chinaunix.net']);
//  _gaq.push(['_trackPageview']);
//
//  (function() {
//    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
//    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
//    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
//  })();

</script>
<script type="text/javascript">
var _bdhmProtocol = (("https:" == document.location.protocol) ? " https://" : " http://");
document.write(unescape("%3Cscript src='" + _bdhmProtocol + "hm.baidu.com/h.js%3F0ee5e8cdc4d43389b3d1bfd76e83216b' type='text/javascript'%3E%3C/script%3E"));

function link(t){
     var href= $(t).attr('href');
     href+="?url="+encodeURIComponent(location.href);
     $(t).attr('href',href);
    //setCookie("returnOutUrl", location.href, 60, "/");
}
</script>
<script type="text/javascript" src="/js/jquery.qqFace.js"></script>
</body>
</html>