最近公司需要做一个信息发布网站,任务分配下来之后确定我来主导这个项目。由于之前做小说采集网站的时候多用的是CMS自带的采集系统,但是本人实在不怎么感冒cms,所以在网上查看了一些资料,很神奇的找到了Querylist这个东西,今天就将我昨晚这次项目的里程分享一下。首先,querylist官网(http://www.querylist.cc),直接点击文档走起。大概看了一下,可以满足我的需求。okay,thinkphp5框架搭起来,使用composer安装querylist,安装之后本地出现gaegar文件夹,进去之后就会看到querylist了。至此,完成了安装的步骤啦,就是这么简单!!!接下来就是使用Querylist类啦,一步一步来走:首先引入类useQL\QueryList;然后重点$html="http://www.***.com";//选择大的区域$selector="body";//获取当前所选区域html$content=QueryList::get($html)->find($selector)->html();//最外层规则筛选$rules=array('plist'=>['dt','html']);//获取分类数据、处理/采集数据$data=$this->dealData($content,$rules);$html是你要采集的目标站点,$content是获取到的html页面源码,$rules是即将要用的筛选规则。主要看一下处理数据的方法dealData();publicfunctiondealData($content,$rules){$data=QueryList::html($content)->rules($rules)->range('')->queryData(function($item){$item=QueryList::html($item)->rules(array('title'=>array('a','text'),'original_link'=>array('a','href')))->range('')->queryData();return$item;});return$data;}该方法中由于我要采集的目标站需要两层进去,所以又写了一次内部回调。将之前获取到的页面源码$content放入html()方法中去,使用rules()方法匹配dt标签中的html源码,range()方法是分片的,我这里没有用到,就留空,但是这个方法一定要写在这儿。在回调中,将之前获取到的html源码定在在$item中,然后进行二次rule规则筛选,将原站中的标题与链接采集到,此时的$item中就已经保存了我们想要的数据了,然后我们就可以将数据存库或是做其他处理了。至此,一次简单的采集就完成了。后附多线程采集的源码,原理与上面一样,需要注意的是,多线程采集时,需要引入对应的类,看下,如果大家有需要的话,我会再写一篇关于querylist多线程采集的文章。useQL\QueryList;useQL\Ext\CurlMulti;usethink\Db;classGatherextendsAdmin{/***@throws\think\db\exception\DataNotFoundException*@throws\think\db\exception\ModelNotFoundException*@throws\think\exception\DbException*采集文章信息*@authorlyf*@date2019-5-10*/publicfunctionindex(){$param=$this->request->get();$ql=QueryList::getInstance();$ql->use(CurlMulti::class);$htmls=array();$map['status']=0;$html=Db::name('city_cate_url')->where($map)->order("id","ASC")->limit("0","10")->select();foreach($htmlas$item){$url=explode('/',$item['url']);$link=explode('.',$url[3]);$htmls[]=$url[0].'//'.$url[2].'/'.$link[0].'P'.$item['page'].'.'.$link[1];}$data=$this->gather($htmls,$ql);$ids=array_column($html,"id");$res_list=db("city_cate_url")->where("id","in",$ids)->where('status','-1')->select();$k=0;if($htmls){$k++;if($res_list){$this->success("正在采集第{$k}次","gather/index","",60);}else{$this->success("正在采集第{$k}次","gather/index","",30);}}else{$this->success("采集成功");}}/***@param$html*@param$city_name*@param$cate_name*@param$num*@returnstring*采集列表信息*@authorlyf*@date2019-5-8*/publicfunctiongather($html,$ql){$ql->curlMulti($html)->success(function(QueryList$ql,CurlMulti$curl,$r){//echo$r['info']['url'];echo"<br>";//初始化$content1='';$content2='';//获取城市ID$city=explode('/',$r['info']['url']);$cityId=$this->getCityId($city[0].'//'.$city[2].'/');$msg='';$selector1="#main";$selector2="tbody";try{$content1=$ql->get($r['info']['url'])->find($selector1)->html();$content2=$ql->get($r['info']['url'])->find($selector2)->html();}catch(\Exception$e){}$rules=['title'=>['.t>div>a','title'],'original_link'=>['.t>div>a','href'],'img'=>['.i>a>img','src'],'brief'=>['.t>div','html','-#wrapv1'],'price'=>['.p','text'],'date'=>['.u','text']];if($content1&&$content2){//处理数据信息$data=$this->dealData($content2,$rules,$ql,$cityId,$r['info']['url']);//数据入库if($data){$res=$this->saveData($data,$r['info']['url']);}}elseif($content1&&!$content2){$this->endNow($r['info']['url'],$status='1');}else{$this->endNow($r['info']['url'],$staus='-1');}$ql->destruct();})->start([//最大并发数,这个值可以运行中动态改变。'maxThread'=>10,//触发curl错误或用户错误之前最大重试次数,超过次数$error指定的回调会被调用。'maxTry'=>3,//全局CURLOPT_*'opt'=>[CURLOPT_TIMEOUT=>10,CURLOPT_CONNECTTIMEOUT=>1,CURLOPT_RETURNTRANSFER=>true]]);}大家有什么不满意的地方,可以随时指正,也希望大家多多包涵。有问题随时可以私我,看到的话一定会回复哒
判断用户是PC端访问还是手机端访问?这个是我们做web研发时经常遇到的一个功能点。也是一个很简单的功能点,解决方案也有很多种,比如使用原生PHP通过分析UA来判断。但如果你的后端用的是ThinkPHP5框架的话,解决这个功能点就更简单了,因为TP5的源码中已经封装好了,我们可以直接使用。publicfunctionindex(){if(request()->isMobile()){var_dump('手机端访问');}else{var_dump('PC端访问');}}上面的举例代码就是使用了TP5封装好的方法,这个方法的源代码在request文件中,方法名称isMobile(),原理同样是通过分析用户的UA来判断访问来源,有兴趣的可以去看看这个方法的源代码,加深自己的理解。
一、比较标签如:{比较标签name="变量"value="值"} 内容 {/比较标签}{eqname="name"value="value"}相等{else/}不相等{/eq}二、判断标签1、SWITCH标签{switchname="变量"}{casevalue="值1|值3"break="0或1"}输出内容1{/case}{casevalue="值2"}输出内容2{/case}{default/}默认情况{/switch}2、IF标签{ifcondition="($name==1)OR($name>100)"}value1{elseifcondition="$nameeq2"/}value2{else/}value3{/if}3、IN和NOTIN1.in标签来判断模板变量是否在某个范围内{inname="id"value="1,2,3"}id在范围内{/in}2.如果判断不在某个范围内,可以使用notin标签:{notinname="id"value="1,2,3"}id不在范围内{/notin}4、BETWEEN和NOTBETWEEN1.between标签来判断变量是否在某个区间范围内:{betweenname="id"value="1,10"}输出内容1{/between}2.notbetween标签来判断变量不在某个范围内:{notbetweenname="id"value="1,10"}输出内容2{/notbetween}5、RANGE、PRESENT、NOTPRESENT标签1.range标签,替换前面的判断用法:{rangename="id"value="1,2,3"type="in"}输出内容1{/range}2.present标签用于判断某个变量已经定义,用法:{presentname="name"}name已经赋值{/present}3.notpresent标签用于判断某个变量是没有定义,用法:{notpresentname="name"}name还没有赋值{/notpresent}6、EMPTY、NOTEMPTY、DEFINED、NOTDEFINED标签1.empty标签用于判断某个变量为空:{emptyname="name"}name为空值{/empty}2.notempty标签用于判断某个变量不为空:{notemptyname="name"}name不为空{/notempty}3.DEFINED标签用于判断某个常量有定义:{definedname="NAME"}NAME常量已经定义{/defined}4.NOTDEFINED标签用于判断某个常量没有定义:{notdefinedname="NAME"}NAME常量未定义{/notdefined}
//获取今天的站三界导航数据Db::table('think_blog')->whereTime('create_time','today')->select();//获取昨天的站三界导航数据Db::table('think_blog')->whereTime('create_time','yesterday')->select();//获取本周的站三界导航数据Db::table('think_blog')->whereTime('create_time','week')->select();//获取上周的站三界导航数据Db::table('think_blog')->whereTime('create_time','lastweek')->select();//获取本月的站三界导航数据Db::table('think_blog')->whereTime('create_time','month')->select();//获取上月的站三界导航数据Db::table('think_blog')->whereTime('create_time','lastmonth')->select();//获取今年的站三界导航数据Db::table('think_blog')->whereTime('create_time','year')->select();//获取去年的站三界导航数据Db::table('think_blog')->whereTime('create_time','lastyear')->select();如果查询当天、本周、本月和今年的时间,还可以简化为://获取今天的站三界导航数据Db::table('think_blog')->whereTime('create_time','d')->select();//获取本周的站三界导航数据Db::table('think_blog')->whereTime('create_time','w')->select();//获取本月的站三界导航数据Db::table('think_blog')->whereTime('create_time','m')->select();//获取今年的站三界导航数据Db::table('think_blog')->whereTime('create_time','y')->select();V5.0.5+版本开始,还可以使用下面的方式进行时间查询//查询两个小时内的站三界导航数据Db::table('think_blog')->whereTime('create_time','-2hours')->select();
/***将多维数组继续分页,自定义分页效果*@paramarray&$array数组*@paramint$page当前页数*@paramint$limit每页页数*@paramint$order0-不变1-反序*@parambool$preserveKeytrue-保留键名false-默认。重置键名*/functionarrayToPage(Array&$array,int$page=1,int$limit=20,int$order=0,bool$preserveKey=false){$start=($page-1)*$limit;//计算每次分页的开始位置//反序if($order==1)$array=array_reverse($array);$array=array_slice($array,$start,$limit,$preserveKey);}
/***一维/二维数组根据关键字对数组字段进行相似度排序*@paramarray$array原数组*@paramstring$keyword关键字*@paramstring$field要匹配的数组字段名,不传则表示一维数组*@paramint$limit限制次数,-1不限制*@returnarray排序好的数组*/functionsimilarArrSort(array$array,string$keyword,$field='',$limit=-1){//数组key小于3,直接返回,不符合排序要求(特例,可不写)if(count($array)<=3||($limit!=-1&&$limit<=3)){return$array;}$newField=$field;//赋值//如果传入的field为空则表示一维数组,生成field为label的二维数组if($field==''){$label2=[];foreach($arrayas$k=>$value)$label2[$k]['label']=$value;$array=$label2;$newField='label';}//数组相似度处理foreach($arrayas$key=>$value){similar_text($value[$newField],$keyword,$percent);$value['percent']=$percent;$data[]=$value;}//取出数组中percent的一列,返回一维数组$percent=array_column($data,'percent');//排序,根据percent排序array_multisort($percent,SORT_DESC,$data);//一维数组处理返回一维数组if($field==''){$newData=[];foreach($dataas$k=>$v)$newData[$k]=$v[$newField];$data=$newData;}//限制返回数量if($limit!=-1){$data=array_slice($data,0,$limit);}return$data;}
/***二维数组根据键值排序*@paramarray$array要排序的数组*@paramstring$keys要用来排序的键名*@paramstring$type默认为降序排序*@returnarray*/functionarraySort($array,$keys,$type='desc'){//将排序的键名作为键值$keysValue=$newArray=[];foreach($arrayas$k=>$v)$keysValue[$k]=$v[$keys];($type=='asc'||$type=='ASC')?asort($keysValue):arsort($keysValue);reset($keysValue);//重置指针foreach($keysValueas$k=>$v)$newArray[$k]=$array[$k];returnarray_values($newArray);//重置键值}
/***根据相关键值生成父子关系*@paramarray$arr1数组1*@paramarray$arr2数组2*@paramstring$arr1_key数组1对应的键值*@paramstring$arr2_key数组2对应的父级键值*@paramstring$child合并的数组键值*/functionlistToTree2(&$arr1,$arr2,$arr1_key='id',$arr2_key='pid',$child='children'){foreach($arr1as$i=>&$item1){foreach($arr2as$j=>$item2){if($item1[$arr1_key]==$item2[$arr2_key]){if(!isset($item1[$child])||!is_array($item1[$child]))$item1[$child]=[];$item1[$child][]=$item2;}}}}
functionlogs($filename,$data){file_put_contents('./logs/'.$filename,$data);}
functionXmlToArr($xml){if($xml=='')return'';libxml_disable_entity_loader(true);$arr=json_decode(json_encode(simplexml_load_string($xml,'SimpleXMLElement',LIBXML_NOCDATA)),true);return$arr;}