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);
    }
}

说明:

  以上请在采集的时候,注意网站版权问题,如上代码,仅做学习使用,请勿用作商业用途。