使用easyswoole开发多进程多协程队列式爬虫

爬虫步骤

我们先说一下一个完整的爬虫步骤。

爬虫步骤分为2个阶段:

1:根据初始页面,获取更多的待爬取页面

2:根据页面,获取到自己想要的东西


更多待爬取页面获取

当我们填入一个初始页面时,我们需要通过某一个特定的逻辑,进行获取更多的页面

例如:

爬取百度页面,我们可以通过页码按钮,获取更多页面的html源码:

仙士可博客


爬取知乎用户,我们可以通过该用户的关注者,和粉丝爬取更多用户界面:


仙士可博客


结果获取

获取到了页面html代码之后,我们就可以根据相应的规则,获取到自己想要的东西了。

这里我推荐http://www.querylist.cc/  可使用jq的语法选择html页面的元素,非常好用



使用消息队列

我们现在已经知道了整个爬虫的步骤了,下一步是如何运行这个代码。

在上一步我们可以看出,一个页面,可能有多个待爬取页面,如果我们每次都是取一个页面,然后分析结果,然后再取一个页面,分析结果

这样会造成代码逻辑的难以掌控。

例如:

1:分析页面1,假设页面1有5个待爬取页面链接

2:分析页面1结果,存储

3:分析页面1的第一个结果待爬取页面2,页面2有5个待爬取页面链接

4:分析页面2的结果,存储

5:分析页面1的第二个待爬取页面3,页面3有5个待爬取页面链接。。。。。。

。。。。

这样会造成代码的难以控制,不好维护待爬取页面,我们可以使用队列的形式进行处理

1:初始页面存入分析队列

2:分析初始页面1,获取5个待爬取页面链接,存入分析队列

3:分析页面1的结果,存入结果队列

4:分析队列出列页面2,获取5个待爬取页面链接,存入分析队列

5:分析页面2的结果,存入结果队列


使用队列的情况下,逻辑将会非常的清晰,只需要每次将分析的页面出入队列,然后取出继续分析即可

同样,结果队列只需要新增一个结果消费进程,进行处理结果数据即可


实战

本人已经写好了基础的爬虫框架,基于easyswoole,使用redis队列进行消费。

github地址:https://github.com/tioncico/ESSpider

首先进行composer安装

composer up

复制produce.php命名为dev.php,增加redis配置:

<?php
/**
 * Created by PhpStorm.
 * User: yf
 * Date: 2019-01-01
 * Time: 20:06
 */

return [
    'SERVER_NAME' => "EasySwoole",
    'MAIN_SERVER' => [
        'LISTEN_ADDRESS' => '0.0.0.0',
        'PORT'           => 9501,
        'SERVER_TYPE'    => EASYSWOOLE_WEB_SERVER, //可选为 EASYSWOOLE_SERVER  EASYSWOOLE_WEB_SERVER EASYSWOOLE_WEB_SOCKET_SERVER,EASYSWOOLE_REDIS_SERVER
        'SOCK_TYPE'      => SWOOLE_TCP,
        'RUN_MODEL'      => SWOOLE_PROCESS,
        'SETTING'        => [
            'worker_num'            => 8,
            'task_worker_num'       => 8,
            'reload_async'          => true,
            'task_enable_coroutine' => true,
            'max_wait_time'         => 3
        ],
    ],
    'TEMP_DIR'    => null,
    'LOG_DIR'     => null,
    /*################ REDIS CONFIG ##################*/
    'REDIS'       => [
        'host' => '127.0.0.1',
        'port' => '6379',
        'auth' => '',
        'serialize'=>true
    ],
];


本身自带了某网站的爬取逻辑,直接运行即可看到:

php easyswoole start

现在我们说下如何爬取其他网站的步骤


爬取http://moe.005.tv/moeimg/动漫图片网站

清空/App/Spider/Event.php的方法逻辑

<?php

namespace App\Spider;

use EasySwoole\Utility\File;
use QL\QueryList;

/**
 * Created by PhpStorm.
 * User: tioncico
 * Date: 19-6-16
 * Time: 上午9:53
 */
class Event
{

    static function consume($data, $html)
    {
    }

    static function produce($data, $html)
    {
    }
}


编写生产代码(获取更多待爬取页面逻辑):

首先,我们要确定,我们的初始页面是http://moe.005.tv/moeimg/,然后我们可以根据下一页按钮获取更多的页面

static function produce($data, $html)
{
    //获得一个queryList对象,并且防止报错
    libxml_use_internal_errors(true);
    @$ql = QueryList::html($html);

    //查询下一页链接,用于继续爬取数据
    $nextLink = $ql->find('.pagelist .n')->attr('href');
    RedisLogic::addProduce($nextLink);

    //查询所有需要爬取的数据,用于爬取里面的图片
    $imgConsumeList = $ql->find('.zhuti_w_list li a')->attrs('href')->all();
    foreach ($imgConsumeList as $value) {
        RedisLogic::addConsume([
            'type' => 1,//消费标识
            'url'  => $value,
        ]);
    }
    return true;
}


编写消费代码

现在我们需要点击一个图片进入,可以发现很多图片,例如:http://moe.005.tv/78243.html

现在,我们编写消费代码:

 static function consume($data, $html)
    {
        //获得一个queryList对象,并且防止报错
        libxml_use_internal_errors(true);
        if ($data['type'] == 1) {
            //消费类型为1,则代表还不是下载图片,需要进行二次消费
            //查询下一页链接,用于继续爬取数据
            @$ql = QueryList::html($html);
            $nextLink = $ql->find('.pagelist .n')->attr('href');
            $nextLink = HttpClientLogic::getTrueUrl($data['url'],$nextLink);
            if ($nextLink) {
                RedisLogic::addConsume([
                    'type' => 1,//消费标识
                    'url'  => $nextLink,
                ]);
            }
            $imgList = $ql->find('.content_nr img')->attrs('src')->all();
//            var_dump($img);
            foreach ($imgList as $img) {
                RedisLogic::addConsume([
                    'type' => 2,//消费标识
                    'url'  => $img,
                ]);
            }
        } elseif ($data['type'] == 2) {
            $urlInfo = parse_url($data['url']);
            $filePath = EASYSWOOLE_ROOT . '/Temp/' . $urlInfo['host'] . $urlInfo['path'];
//            var_dump($filePath);
            File::createFile($filePath, $html);
//            var_dump($data['url']);
        }

    }

初始页面链接可通过协程调用进行增加:

<?php
/**
 * Created by PhpStorm.
 * User: tioncico
 * Date: 19-6-16
 * Time: 下午5:35
 */
include "./vendor/autoload.php";
\EasySwoole\EasySwoole\Core::getInstance()->initialize();
go(function (){
    \App\Spider\RedisLogic::clearConsumeMap();//清除消费map
    \App\Spider\RedisLogic::clearProduceMap();//清除生产map
    \App\Spider\RedisLogic::clearConsumeList();//清除消费队列
    \App\Spider\RedisLogic::clearProduceList();//清除生产队列
    //新增默认队列
    \App\Spider\RedisLogic::addProduce('http://moe.005.tv/moeimg/');
});


直接运行

php easyswoole start

即可实现爬虫爬取页面

仙士可博客



爬虫框架补充

github地址:https://github.com/tioncico/ESSpider

1:可通过EasySwooleEvent.php  文件,进行配置爬取的协程数,不建议太大

2:本爬虫没有做ip池,需要的可自行实现

3:本爬虫框架只适用于学习,请不要用于违法用途





仙士可博客
请先登录后发表评论
  • 最新评论
  • 总共4条评论
仙士可博客

每天与bug做斗争仙士可博客太深奥了 ,我刚接触swoole看到这里只能说一句老师牛逼!

2019-09-27 10:12:52 回复

仙士可博客

:HttpClientLogic类:$response = $httpClient->getRequest(['referer'=>$url]);这个getRequest方法是在哪里的,为什么要这样写?

2019-09-24 11:56:28 回复

仙士可博客
  • 仙士可 回复 :估计是旧版本的HTTPClient,现在改成了直接get,就是发起一次get请求,并且referer为当前url
  • 2019-09-24 14:51:05 回复
仙士可博客
  • 回复 仙士可:谢谢
  • 2019-09-24 17:37:08 回复
仙士可博客

:1

2019-09-24 10:48:32 回复

仙士可博客

云龙:请问easyswoole框架这个如何改成消费者进程池的方式?就是消费进程数不在easyswooleEvent.php里面写死,而是动态创建或销毁?

2019-06-25 15:33:31 回复

仙士可博客
  • 仙士可 回复 云龙:swoole的进程是必须先启动的,不能动态创建
  • 2019-06-26 08:39:56 回复
  • 本站由白俊遥博客程序搭建
    © 2017-1-17 php20.cn 版权所有 ICP证:闽ICP备17001387号
  • 联系邮箱:1067197739@qq.com