php使用guzzlehttp/guzzle进行数据采集

一、需求目的

1、采集某个平台网站的数据

2、将采集回来的数据与本身平台系统的现有数据汇总

二、实现步骤

1、分析平台网站
  • 分析登录校验方式
    • session 或 token
  • 分析界面数据渲染方式
    • 异步数据渲染 或 直接页面和数据一起渲染
2、确定方案
  • 分析后,发现网站的登录比较简单,直接账号密码登录即可,并且登录校验是通过cookie进行校验的
  • 分析后,发现界面数据的渲染方式是异步数据渲染,代表有接口可以返回数据,直接对接口进行请求即可
3、封装功能类

确认完方案后,为了后续使用方便,特意封装一个功能类,其他地方使用的时候就比较简单
一、设置基础信息
- 设置对应平台域名
- 设置对应的账号密码
- 设置cookie序列化后保存的路径
二、功能方法
- 获取句柄方法(带cookie)
- 认证授权方法
- 带cookie的请求方法
- 简单的http方法
- 外部API返回处理方法
- 某个数据列表页面的获取等

4、使用功能类进行后续处理

三、具体实现

开启数据抓取脚本
使用功能类获取某个数据列表接口,得
```json
{
	'count'=>10000,
	'data_list'=>[{...}]
}
```
获取这批数据在数据库的流水号,并分开保存在数据库表 log 中(为了后续有个计算抓取进度,我把这个数据保存放在了功能类中,每获取完一次接口,都先保存到数据库,同时还保存了要抓取的总数量)
开启数据同步脚本

读取数据库 log 表,按id升序取n条记录
循环记录列表
设置记录状态修改为处理中
具体同步的逻辑(每个人不一样,这里就不展开了)
将记录状态修改为已完成(或者处理失败等)

界面展示

其他数据展示就不说了,这里写一下进度条的展示
在这里插入图片描述
找个进度条的代码样式(网上应该很多,我这里用的是后台管理模版自带的)
在这里插入图片描述
获取进度数据

  • 数据抓取进度
    • 获取log表最后一条数据的流水号,并得到该流水号要获取的总数量
    • (总数量/每次获取的数量)+1 ,得到该流水号共有多少条记录
    • 总记录数量/当前流水号的记录数量,得数据抓取进度
  • 数据同步进度
    • 获取同批流水号记录,通过状态 未处理 等状态判断未处理和已处理的数量
    • 已处理 / 总数量,得数据同步进度
  • 将进度和当前属于哪种进度的相关信息返回(界面简单通过定时器和ajax,所以可以看到进度条和文字在变化)

功能类代码

<?php

namespace App\Common\Features;

use App\Common\Models\SyncLog;
use App\Common\Repositories\LogRepositorie;
use App\Exceptions\ErrorCode;
use GuzzleHttp\Client;
use GuzzleHttp\Cookie\CookieJar;
use GuzzleHttp\Cookie\SetCookie;
use Illuminate\Support\Facades\Redis;

class Feature
{
    //地址
    private $apiHost;

    //账号
    private $username;

    //密码
    private $password;

    //cookie保存路径
    private $cookieJar;

    private $cookie;

    private $client;

    private $LogRepo;

    public function __construct()
    {
        $this->apiHost = '';
        $this->username = '';
        $this->password = '';
        $this->cookieJar = 'cookie/login.cookie';

        $this->LogRepo = new LogRepositorie();
    }


    /**
     * 获取句柄
     * @return Client
     */
    public function getClient()
    {
        if (empty($client)) {
            $this->cookie = new CookieJar;
            $this->client = new Client(array(
                'cookies' => $this->cookie
            ));
        }
        return $this->client;
    }

    /**
     * 认证授权
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function auth()
    {
        $retryCount = 3;//认证问题不通过3次重试

        $response = null;

        while ($retryCount > 0) {
            $retryCount--;
            try {
                $isAuth = false;
                try {
                    //获取cookie和句柄
                    $cookieContent = file_get_contents($this->cookieJar);
                    $cookieObject = unserialize($cookieContent);//
                    $client = new Client(array(
                        'cookies' => $cookieObject
                    ));

                    $response = $client->request('GET', $this->apiHost . '/xxx');//先尝试请求某个要登录的接口
                    $result = $this->loadResult($response);

                    $this->client = $client;//如果成功则句柄和cookie有效
                    $this->cookie = $cookieObject;

                    return;
                } catch (\Exception $e) {
                    $isAuth = true;
                }

                //判断是否需要登录
                if ($isAuth) {
                    //使用登录接口
                    $this->getClient();
                    $result = $this->client->request('POST', $this->apiHost . '/login', [
                        'timeout' => 30,
                        'form_params' => [
                            'username' => $this->username,
                            'password' => $this->password,
                        ]
                    ]);
                    //登录成功保存cookie
                    $cookie = $this->cookie;
                    $cookieContent = serialize($cookie);
                    file_put_contents($this->cookieJar, $cookieContent);
                }

                $retryCount = 0;

                return;
            } catch (\Exception $e) {

            }
        }

        //抛异常
    }

    public function dataSearch()
    {
        $uri = '/xxx';

        //获取记录批次流水
        $serial_number = $this->LogRepo->getSerialNumber([
            'uri' => $uri
        ]);

        //循环获取数据
        $flag = true;
        $pageSize = 500;
        $page = 1;
        $totalItems = 0;
        $orderArrayList = [];
        while ($flag) {
            $request = [
                'pageSize' => $pageSize,
                'page' => $page,
            ];

            $request_type = 'post';

            $result = $this->request($uri, $request_type, $request, $serial_number);

            if (!empty($result['list'])) {
                $orderArrayList[] = $result['list'];
            } else {
                $totalItems = $result['totalItems'];
                $flag = false;
            }

            echo '共 ' . (ceil(($result['totalItems'] / $pageSize)) + 1) . " 页,第 " . $page . ' 页结束,' . date('Y-m-d H:i:s') . "\n";
            sleep(5);

            $page++;
        }

        $response = [
            'count' => count($orderArrayList),
            'data_list' => $orderArrayList,
        ];

        return $response;
    }

    /**
     * 带cookie的请求方法
     * @param $uri
     * @param string $method
     * @param array $queryData
     * @param string $serial_number
     * @param string $resultType
     * @return false|mixed|string
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function request($uri, $method = 'get', $queryData = [], $serial_number, $resultType = 'array')
    {
        //这里新增记录,为了后续的进度统计
        $syncLog = $this->LogRepo->addSyncLog([
            'uri' => $uri,
            'serial_number' => $serial_number,
            'request_type' => $method,
            'request' => $queryData,
        ]);
        $syncLogId = $syncLog['id'];

        $this->auth();

        $result = $this->http($uri, $method, $queryData, [], $resultType);

        //更新同步记录的结果
        $this->LogRepo->updateSyncLog([
            'id' => $syncLogId
        ], [
            'response' => $result,
            'status' => SyncLog::STATUS_WAIT_SYNC,
            'grab_time' => time(),
        ]);

        return $result;
    }

    /**
     * 简单的http方法
     * @param $uri
     * @param string $method
     * @param array $queryData
     * @param array $headerData
     * @param string $resultType
     * @return false|mixed|string
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function http($uri, $method = 'get', $queryData = [], $headerData = [], $resultType = 'array')
    {
        $url = $this->apiHost . $uri;

        $headers = [
            'Content-Type' => 'application/x-www-form-urlencoded'
            //'Content-Type' => 'application/json;charset=UTF-8'
        ];

        if (is_array($headerData)) {
            $headers = array_merge($headers, $headerData);
        }

        $params = [];

        if (!empty($queryData) && is_array($queryData)) {
            if ($method == 'get') {
                $params['query'] = $queryData;
            } else {
                if ($headers['Content-Type'] == 'application/x-www-form-urlencoded') {
                    $params['form_params'] = $queryData;
                } else {
                    $params['json'] = $queryData;
                }
            }
        }

        if (!empty($headers) && is_array($headers)) {
            $params['headers'] = $headers;
        }

        $response = $this->client->request($method, $url, $params);

        $result = $this->loadResult($response);

        if (strtolower($resultType) == 'array') {
            return $result;
        } else if (strtolower($resultType) == 'json') {
            return json_encode($result, true);
        }
    }

    /**
     * 外部API返回处理
     * @param $response
     * @return mixed
     */
    protected function loadResult($response)
    {
        $response_code = $response->getStatusCode();

        $result = $response->getBody()->getContents();

        $result = json_decode($result, true);

        if (!isset($result['success']) || $result['success'] != true) {
        }

        return $result['data'];
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值