ThinkPHP5.1+swoole+QueryList實現異步爬取數據功能

先講下爲什麼要使用swoole異步任務,在項目中我們經常會遇到執行時間比較久的請求(上傳,爬取等),讓用戶一直等待不是好的體驗,所以我們使用swoole異步請求,先返回‘執行中’,等異步任務執行完畢再修改狀態。

接下來我們來實現swoole在thinkphp 裏的集成:

1.安裝swoole
推薦使用官網提供的安裝方式 安裝swoole擴展

2.安裝QueryList

composer require jaeger/querylist

文檔地址 querylist

3.在Tp5裏自定義一個命令行類

在application下建立一個console文件夾用來存放自定義命令文件,然後在該文件夾下新建Crawler.php文件,編輯application目錄下的command.php文件
添加如下代碼

return [
    'app\console\Crawler',
];

深度截圖_選擇區域_20190920133914.png
編寫 Crawler.php

<?php

namespace app\Console;

use QL\QueryList;
use think\console\Command;
use think\console\Input;
use think\console\Output;


class Ceshi extends Command{
    protected $server;
    protected function configure()
    {
        //參考tp5自定義命令行手冊
        $this->setName('crawler:start')->setDescription('Start Crawler Server!');
    }

    protected function execute(Input $input, Output $output)
    {
        $serv = new \swoole_server('0.0.0.0',9502);//9502爲swoole監聽端口,可以自行設置
        $serv->set(array('task_worker_num' => 4));//異步進程數量 根據實際情況進行配置,參照官網提供的方案
        $serv->on('connect', function ($serv, $fd){

        });
        $serv->on('receive', function($serv, $fd, $from_id,$data) {
            //投遞異步任務 $data爲接口傳遞的參數,接收參數做一些處理之後交給task
            echo'收到任務';
            $serv->task($data);
        });
        $serv->on('task', function ($serv, $task_id, $from_id, $data) {
            //接收receive傳遞的任務,進行執行
            //本例中實現的是爬取京東商品任務 ,大家可以根據自身情況修改
            echo'執行任務';
            $this->crawler($data);
        });
        $serv->on('finish', function ($serv, $task_id, $data) {

        });
        $serv->start();
    }

    /**
     * 採集商品數據
     * @param $activity_id
     * @return bool
     */
    private function crawler($activity_id)
    {
        //假設 $number 爲京東活動id
        $number = 123456;
        $data = [];
        $limit = 1;//起始
        $ql = QueryList::getInstance();
        for (; ;) {
            $offset = $limit * 30 + 1;//偏移量
            //這個url是通過分析京東加載商品接口提取出來的,修改這幾個參數就能模擬分頁爬取到京東全部活動的商品
            $url = 'https://search.jd.com/s_new.php?activity_id=' . $number . '&vt=2&scc=1&page=' . $limit . '&s=' . $offset . '&scrolling=y&tpl=1_M';
            //這裏使用了代理,因爲循環爬取的原因被京東檢測到了封了ip,所以使用動態代理爬取,代理的使用根據代理服務商提供的api每個都不同,在本文不詳細介紹
            $crawler = $this->immediate($url);
            if ($crawler) {
                $query = [];
                //phpquery 的核心思想就是使用php像js一樣通過dom操作頁面
                //下面代碼的意思是 獲取頁面的html 找到class爲gl-item的子元素,並遍歷(就是遍歷京東頁面展示的商品div)
                $ql->html($crawler)->find('.gl-item')->children()->map(function ($item) use (&$query, $number, $activity_id) {
                    $tags = '';
                    //找到標籤展示規律並提取
                    $item->find('.p-icons')->children()->map(function ($child_tag) use (&$tags) {
                        $tags .= $child_tag->html() . ',';
                    });
                    //找到商品價格展示規律並提取
                    $price = $item->find('.p-price>strong>i')->html();
                    if (!$price) {
                        $price = $item->find('.p-price>strong')->attrs('data-price')[0] ?? 0;
                    }
                    if ($price && $price > 0) {
                        //這裏通過節點找到需要的數據和數據表對應存到mysql
                        $query[] = [
                            'activity_id' => $activity_id,
                            'activity_number' => $number,
                            'goods_number' => $item->find('.p-operate>a')->attrs('data-sku')[0] ?? '',
                            'title' => $item->find('.p-name-type-2>a>em')->text(),
                            'image' => 'http:' . $item->find('.p-img>a>img')->attrs('source-data-lazy-img')[0] ?? '',
                            'price' => $price,
                            'tags' => rtrim($tags, ','),
                            'shop_name' => $item->find('.p-shop>span>a')->html(),
                            'shop_number' => $item->find('.p-img>div')->attrs('data-venid')[0] ?? '',
                        ];

                    }
                });
                if (count($query)) {
                    foreach ($query as $k => $v) {
                        $data[] = $v;
                    }
                    $limit++;
                } else {
                    break;
                }
            }
            //儘量讓爬蟲等待一秒再執行,避免內存溢出
            sleep(1);
        };
        //爬蟲功能結束之後及時釋放內存
        $ql->destruct();
        //.......
        //接下來把數據存進mysql中就大功告成啦
    }

}

4.寫一個接口去調用異步任務

public function swoole_task(Request $request){
        $type = $request->param('type');
        $client = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
        $ret = $client->connect("服務器ip", 9502);
        if(empty($ret)){
            return 'FAIL';
        } else {
            $client->send($type);//提交任務到crawler.php 處理 我是傳參$type實現不同的異步任務調用
            return 'SUCCESS';
        }
    }

5.進入tp5 項目根目錄執行命令

php think crawler:start//通常用於調試

讓swoole監聽命令以守護進程方式運行(服務器)

php think crawler:start &

6.執行上面寫好的調用接口swoole_task 可以在你想用的任何地方調用
7.效果
深度截圖_選擇區域_20190920142220.png

本文提供的方法僅供參考,實例並不能直接執行,需要大家理解工作原理後結合自身項目進行使用

8.推薦一下本文使用技術在項目中的應用,微信搜索‘東東小賣部’,每天會爬取最新的京東滿減打折活動,
通過算法湊成滿減之後將商品組合返回給用戶,親身體驗過非常不錯,這個項目對於喜歡網購的朋友們來說絕對會帶給你非常棒的體驗。
大家要是覺得本文有幫助的話,幫忙關注下公衆號分享給身邊的人,不許白嫖哦,十分感謝
微信圖片_20190920104620.jpg
微信圖片_20190920104614.jpg
d19aaddda144ad34e5809b91dfa20cf431ad8526.jpg
e37546d98d1001e9f3030819b70e7bec54e79726.jpg

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章