PHP如何采集网站数据
需求:
对于刚搭建的网站,数据比较单一,那么如何采集点数据呢。
前言:
这里我们可以用PHP写的一个框架QueryList,官网文档:http://www.querylist.cc/docs/guide/v4/overview;
说明:
如果你之前没有做过数据采集,希望快速的理解,和入门。那么下面的文字可以给你带来帮助。
什么是数据采集:
数据采集就是通过代码,模拟浏览器请求,将第三方网站上资源(图片资源,文字资源),采集到本地。简而言之,比如有一个线上的博客网站,你自己也搭建了一个网站,但是没有博客怎么办。一般你可以通过复制粘贴,搬运到自己的网站上。所谓的数据采集,就是通过代码实现刚才的操作流程。
核心处理点:
这里使用代码操作,主要就是两块:
一个是,如何按规则选中我们要采集的网站字段;这个主要是用 DOM内容选择:CSS选择器
一个是,如何将选中的数据采集下来;这个就是通过 HTTP客户端:GuzzleHTTP
简而言之,代码主要是用css选择器按规则,选中需要采集的数据,然后用模拟HTTP客户端请求,采集数据。
我们要做什么:
现在,我们要将一个第三方网站的数据,采集下来,然后写入到自己数据库中。
我们大致怎么做:
步骤一,按官方文档,在自己项目(这边项目是用tp6.0搭建的项目)中安装QueryList扩展;
步骤二,采集博客列表,然后循环列表,采集列表详情,而后将详情里面的图片下载到本地,最后对数据进行处理,自己需要哪些字段就存取哪些字段;
步骤三,对上面的数据进行格式化后,写入到自己数据库中;
上代码:
1、在ThinkPHP6.0项目中通过Composer安装QueryList扩展
composer require jaeger/querylist
2、DataCj.php控制器中引入 use QL\QueryList类,按步骤处理,用redis做数据中转,最后拿到具体格式数据。对于选择器的使用具体见QueryList文档。
<?php /* * @Fun: 数据采集 * @User: JessieK * @Date: 2021-11-03 15:50:22 */ namespace app\v1\controller; use think\Request; use QL\QueryList; use think\facade\Cache; use think\facade\Db; use lib\XFace; use think\Model; class DataCj { protected $api_url;//接口域名 public function __construct() { $host = 1;//环境切换 1:test环境;2:prod环境 if($host == 1){ $this->api_url = 'https://a.cn'; }else{ $this->api_url = 'https://b.cn'; } } /** * @name: 采集寻找朋友数据 列表 * @param {Request} $request * @return {*} */ public function xunrenla_xunzhaopengyou(Request $request) { $page = $request->param('page', 1); $renewal = $request->param('renewal', 0); //放入缓存 $redis = Cache::store('redis'); $key = $request->action() .'_'. $page; $data_list = $redis->get($key); if(!$data_list || $renewal){ //采集网址 $url = 'http://www.xunrenla.com/xunzhaopengyou/list_6_'. $page .'.html'; //元素采集规则 $rules = [ //姓名 'name' => ['.siteContent a', 'title'], //详情地址 'url' => ['.siteContent a', 'href'], //图片地址 'img' => ['.siteImg img', 'src'], ]; //切片选择器,选择列表范围 $range = '.layui-col-md8 .layui-card-body>div'; //开始采集 $rt = QueryList::get($url)->rules($rules)->range($range)->queryData(); $rt_key = count($rt) - 1; unset($rt[$rt_key]); // d($rt); $res = $redis->set($key, $rt, 2592000); echo '开始写入缓存'.$res; exit('写入缓存,数量值='.$rt_key); }else{ exit('已有缓存,数量值='. count($data_list)); } } /** * @name: 采集寻找朋友数据 下载图片到本地 * @param {Request} $request * @return {*} */ public function xunrenla_xr(Request $request) { $page = $request->param('page', 1); $key = 'xunrenla_xunzhaopengyou_'. $page; //取的缓存里面的数据 $redis = Cache::store('redis'); $xr_list = $redis->get($key); if($xr_list){ lg('已取出,开始处理图片='. count($xr_list)); //定义图片存放路径 $img_path = runtime_path() . 'xrl_img/'; $time = time(); foreach($xr_list as $key => $value){ //判断图片地址是否存在 if(strstr($value['img'], 'http')){ $img_string = file_get_contents($value['img']); lg('准备下载会员='.$value['name']); //保存图片 $name = md5($value['name'] . $time . $key).'.jpg'; $img_name = $img_path . $name; $put_res = file_put_contents($img_name, $img_string); lg('下载结果='.$put_res); //追加路径 $xr_list[$key]['img_path'] = $img_name; }else{ //删除图片不存在的 unset($xr_list[$key]); lg('当前图片路径不存在,跳过'); } } lg('准备写入缓存,实际下载数量='.count($xr_list)); $res = $redis->set($request->action() . '_'. $page, $xr_list, 2592000); exit('处理完成res='.$res); }else{ exit('暂无数据'); } } /** * @name: 图片上传 test服务器 * @param {Request} $request * @return {*} */ public function xunrenla_upd_img(Request $request) { $page = $request->param('page', 1); $key = 'xunrenla_xr_'. $page; //取的缓存里面的数据 $redis = Cache::store('redis'); $xr_list = $redis->get($key); if(!$xr_list){ exit('暂无数据'); } $upd_key = $request->action() . '_'. $page; $upd_value = $redis->get($upd_key); if($upd_value){ exit('已处理'.count($upd_value)); } lg('开始处理图片,待处理数='.count($xr_list)); foreach($xr_list as $key => $value){ //获取图片base64字符 $base_sting = imgToBase64($value['img_path']); if($base_sting){ lg('获取图片base64字符成功name'.$value['name']); lg('准备上传test服务器'); $https_res = httpCurl($this->api_url . '/index/baseStringApi', ['chars' => $base_sting, 'filename' => md5($value['name'])], 1); if($https_res && $https_res['code'] == 200){ lg('上传成功path='.$https_res['data']['path']); $xr_list[$key]['test_img_path'] = $https_res['data']['path'];//追加到新元素 }else{ unset($xr_list[$key]); lg('上传失败https_res='.$https_res['msg']); } }else{ unset($xr_list[$key]); lg('获取图片base64失败'); } } lg('准备写入缓存,实际上传数='.count($xr_list)); $res = $redis->set($upd_key, $xr_list, 2592000); exit('处理完成res='.$res); } /** * @name: 采集寻人朋友 列表详情 * @param {Request} $request * @return {*} */ public function xunrenla_details(Request $request) { $page = $request->param('page', 1); $key = 'xunrenla_upd_img_'. $page; //取的缓存里面的数据 $redis = Cache::store('redis'); $xr_list = $redis->get($key); if(!$xr_list){ exit('暂无数据'); } foreach($xr_list as $key => $value){ $ql = QueryList::get($value['url']); lg('开始采集详情='.$value['name']); $rt = []; $rt['name'] = jq_zh($ql->find('.layui-col-md9 li:nth-child(1)')->text(), ':'); $rt['sex'] = jq_zh($ql->find('.layui-col-md9 li:nth-child(2)')->text(), ':'); $rt['age'] = jq_zh($ql->find('.layui-col-md9 li:nth-child(3)')->text(), ':'); $rt['lost_time'] = jq_zh($ql->find('.layui-col-md9 li:nth-child(4)')->text(), ':'); $rt['jg'] = jq_zh($ql->find('.layui-col-md9 li:nth-child(5)')->text(), ':'); $rt['szdd'] = jq_zh($ql->find('.layui-col-md9 li:nth-child(6)')->text(), ':'); $rt['sg'] = jq_zq(jq_zh($ql->find('.layui-col-md9 li:nth-child(7)')->text(), ':'), 'C'); $rt['lxr'] = jq_zh($ql->find('.layui-col-md9 li:nth-child(9)')->text(), ':'); $rt['ph'] = jq_zq(jq_zh($ql->find('.layui-col-md9 li:nth-child(10)')->text(), ':'), '提'); $rt['fp'] = jq_zh($ql->find('.layui-col-md8 .layui-hide-xs')->text(), ':'); $rt['ms'] = jq_zh($ql->find('.article-content div:nth-child(1) p')->text(), ':'); //数组详情追加 $xr_list[$key]['detaile'] = $rt; } lg('采集完成,准备写入缓存'); $res = $redis->set($request->action() . '_'. $page, $xr_list, 2592000); exit('处理完成res='.$res); } /** * @name: 对采集数据进行清洗,格式化经纬度 * @param {Request} $request * @return {*} */ public function xunrenla_data_up(Request $request) { $page = $request->param('page', 1); $key = 'xunrenla_details_'. $page; //取的缓存里面的数据 $redis = Cache::store('redis'); $xr_list = $redis->get($key); if(!$xr_list){ exit('暂无数据'); } $upd_key = $request->action() . '_'. $page; $upd_value = $redis->get($upd_key); // d($upd_value); if($upd_value){ exit('已处理'.count($upd_value)); } // d($xr_list); lg('开始处理数据,待处理数='.count($xr_list)); $data_list = []; foreach($xr_list as $key => $value){ //解析地址 lg('准备解析地址'); $dzz = str_replace(' ', '', $value['detaile']['jg']); $addres_res = XFace::showLocation($dzz); if($addres_res['status']){ lg('地址解析失败,res='.$addres_res['msg']); unset($xr_list[$key]); }else{ lg('解析成功,res='.$addres_res['msg']); $data_list[$key]['member_id'] = 8;//会员uid $data_list[$key]['member_name'] = '人人寻客服-小寻';//会员昵称 $data_list[$key]['member_headr'] = 'https://thirdwx.qlogo.cn/mmopen/vi_32/IxNUxicGvfiaibrSkUd9wMYibhOicua4icbwNUr8VQKKePQTnpUxxUL9kYTRBakq2Nsg9QxJVx4gpM5FHbibrOFloJwKQ/132';//会员头像 $data_list[$key]['lost_type'] = 1;//寻人类型,0找亲人,1帮忙找', // $data_list[$key]['title'] = $value['name'];//寻人标题 $data_list[$key]['name'] = $value['name'];//姓名 if($value['detaile']['sex'] == '男'){//性别(1男,2女 $data_list[$key]['sex'] = 1; }else{ $data_list[$key]['sex'] = 2; } $data_list[$key]['age'] = getAge(strtotime($value['detaile']['age']));//年龄 $data_list[$key]['height'] = $value['detaile']['sg'];//身高 $data_list[$key]['face_img'] = $value['test_img_path'];//图片 // $data_list[$key]['phone'] = '';//手机号id // $data_list[$key]['lost_trait'] = $value['detaile']['ms'];//外貌特征 $data_list[$key]['lost_time'] = strtotime($value['detaile']['lost_time'] . '14:35');//走失时间 $data_list[$key]['lost_lon'] = $addres_res['data']['lon'];//走失地点(经度', $data_list[$key]['lost_lat'] = $addres_res['data']['lat'];//走失地点(纬度' // $data_list[$key]['address'] = $value['detaile']['szdd'];//具体位置(通过经纬度解析) // $data_list[$key]['province_id'] = 1;//省id', // $data_list[$key]['city_id'] = 1;//市id', // $data_list[$key]['district_id'] = 1;//区id', $data_list[$key]['address_detail'] =$dzz;//详细地址(预留字段', $data_list[$key]['describe'] = str_replace(' ', '', $value['detaile']['ms']);//走失描述 $data_list[$key]['insert_time'] = strtotime($value['detaile']['fp']);//创建时间 $data_list[$key]['make_type'] = 2;//创建类型(1会员发布,2采集发布,默认为1', $data_list[$key]['m_phone'] = $value['detaile']['ph'];//采集发布,手机号', $data_list[$key]['laiyuan'] = '寻人啦www.xunrenla.com';//来源 } } lg('采集完成,准备写入缓存,实际写入数='.count($data_list)); $res = $redis->set($upd_key, $data_list, 2592000); exit('处理完成res='.$res); } /** * @name: 将采集数据 写入test数据库 * @param {Request} $request * @return {*} */ public function xunrenla_data_insert(Request $request) { $page = $request->param('page', 1); $key = 'xunrenla_data_up_'. $page; //取的缓存里面的数据 $redis = Cache::store('redis'); $xr_list = $redis->get($key); // d($xr_list);die; if(!$xr_list){ exit('暂无数据'); } $debug = $request->param('debug', 0); if($debug != 99){ d($xr_list); die; } lg('开始组装数据'); $inser_data = []; foreach($xr_list as $key => $value){ //组装数据 $time = time(); $pj_data = [ 'name' => $value['name'], 'sex' => $value['sex'], 'age' => $value['age'], 'height' => $value['height'], 'face_img' => $value['face_img'], // 'phone' => $data['phone'], 'member_id' => $value['member_id'], 'member_name' => $value['member_name'], 'member_headr' => $value['member_headr'], 'lost_type' => $value['lost_type'], // 'title' => $data['title'], // 'lost_trait' => $value['describe'],//外貌特征 'lost_time' => $value['lost_time'], 'lost_lon' => $value['lost_lon'],//经度 'lost_lat' => $value['lost_lat'],//纬度 'address_detail' => $value['address_detail'],//详细位置 // 'address' => $adres_res['data']['formatted_address'],//具体地址(经纬度解析 // 'province_id' => $adres_res['data']['province_id'], // 'city_id' => $adres_res['data']['city_id'], // 'district_id' => $adres_res['data']['district_id'], 'describe' => $value['describe'],//走失描述 'insert_time' => $time, // 'update_time' => $time, 'make_type' => $value['make_type'], 'm_phone' => $value['m_phone'], 'laiyuan' => $value['laiyuan'], ]; $inser_data[$key] = $pj_data; // lg('准备写入数据库name='.$value['name']); // $tset_data = Db::table('t_lost_person')->insert($insert_data); // lg('写入完成tset_data='.$tset_data); // die; } lg('组装完成,总计inser_data='.count($inser_data)); lg('准备批量写入数据库'); $tset_data = Db::table('t_lost_person')->insertAll($inser_data); exit('批量写入完成tset_data='.$tset_data); // d($inser_data); } }
说明:
以上请在采集的时候,注意网站版权问题,如上代码,仅做学习使用,请勿用作商业用途。
-----END
影子是一个会撒谎的精灵,它在虚空中流浪和等待被发现之间;在存在与不存在之间....