多线程方式采集搜狗高清壁纸的实现

上一篇,完成了Windows下PHP多线程扩展pthreads的安装,下面就利用多线程进行图片的采集

一、实现前准备工作

1、打开搜狗图片网站

打开控制台,分析异步请求数据规律

2、搜狗图片存储数据表结构创建

打开搜狗异步请求链接,查看响应结果中的json数据


根据上图中图片详情创建搜狗图片数据表结构sql语句如下:

CREATE TABLE `sougou_pic` (
  `sougou_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '表主键id',
  `id` int(11) DEFAULT NULL COMMENT '搜过网站图片id',
  `did` int(10) DEFAULT NULL,
  `thumbUrl` varchar(255) DEFAULT NULL,
  `thumb_width` int(10) DEFAULT NULL,
  `thumb_height` int(10) DEFAULT NULL,
  `sthumbUrl` varchar(255) DEFAULT NULL,
  `sthumb_width` int(10) DEFAULT NULL,
  `sthumb_height` int(10) DEFAULT NULL,
  `bthumbUrl` varchar(255) DEFAULT NULL,
  `bthumb_width` int(10) DEFAULT NULL,
  `bthumb_height` int(10) DEFAULT NULL,
  `pic_url` varchar(255) DEFAULT NULL,
  `width` int(10) DEFAULT NULL,
  `height` int(10) DEFAULT NULL,
  `size` int(10) DEFAULT NULL,
  `ori_pic_url` varchar(255) DEFAULT NULL,
  `ext_url` varchar(255) DEFAULT NULL,
  `page_title` varchar(64) DEFAULT NULL,
  `page_url` varchar(255) DEFAULT NULL,
  `title` varchar(64) DEFAULT NULL,
  `tags` varchar(64) DEFAULT NULL,
  `group_mark` varchar(8) DEFAULT NULL,
  `group_index` int(10) DEFAULT NULL,
  `publish_time` int(10) DEFAULT NULL,
  `surr1` varchar(16) DEFAULT NULL,
  `surr2` varchar(16) DEFAULT NULL,
  `category` varchar(16) DEFAULT NULL,
  `weight` int(10) DEFAULT NULL,
  `deleted` int(10) DEFAULT NULL,
  `waplink` varchar(255) DEFAULT NULL,
  `weblink` varchar(255) DEFAULT NULL,
  `intime` int(10) DEFAULT NULL COMMENT '添加时间',
  `pfrom` varchar(255) DEFAULT '',
  PRIMARY KEY (`sougou_id`)
) ENGINE=InnoDB AUTO_INCREMENT=11088 DEFAULT CHARSET=utf8;

3、录入搜狗图片到数据库

采集代码这里省略,需要注意的是,采集过程中防止超时的存在(主要可能还是搜狗有做简单反爬虫处理),超时采集不到就延时5秒继续采集。

下面是录入完成后数据表一览

二、多线程方式下载数据库中已录入的搜狗图片

1、采用Yii2框架实现

  • Yii2框架控制台(console)下创建命令行可执行脚本文件SougouController.php,代码如下:

<?php

namespace console\controllers;

use yii;
use yii\console\Controller;
use common\models\SougouPic as M;
use common\component\SougouPthreads;
use yii\console\Exception;

class SougouController extends Controller
{
    /**
     * 下载搜狗图片
     */
    public function actionDownload()
    {
        try{
            $stime=microtime(true);

            $arrsSougouPic = M::myTables();//3500,6030,9636
            echo '图片总数量共: '.count($arrsSougouPic).PHP_EOL;

            //线程池
            $pool = array();

            foreach ($arrsSougouPic as $member){
                $sougou_id = $member['sougou_id'];
                while (true){
                    if(count($pool) < 5){
                        //要下载的原图
                        $ori_pic_url = $member['ori_pic_url'];
                        $pool[$sougou_id] = new SougouPthreads($member['sougou_id'], $member['size'], $member['title'], $ori_pic_url, $member['width'], $member['height']);
                        $pool[$sougou_id]->start();
                        break;
                    }else{
                        foreach ( $pool as $name => $worker){
                            if(! $worker->isRunning()){
                                unset($pool[$name]);
                            }
                        }
                    }
                }
            }

            $arrsSougouPic = null;

            echo "Download completed!".PHP_EOL;

            $etime=microtime(true);//获取程序执行结束的时间
            $total=$etime-$stime;   //计算差值
            echo '共运行时间:'.$total.'秒'.PHP_EOL;
        } catch (Exception $e) {
            echo '【' , date('H:i:s') , '】', '【系统错误】', $e->getMessage(), "".PHP_EOL;
        }
    }
    
}

  • 搜狗多线程类继承线程类,SougouPthreads.php代码如下

<?php
namespace common\component;

use Yii;
use Thread;

class SougouPthreads extends Thread
{
    public $running = false;
    public $sougou_id = '';
    public $title = '';
    public $size;
    public $ori_pic_url = '';
    public $width = '';
    public $height = '';
    protected $saveDir = '';
    protected $downloadFailedTxt;

    public function __construct($sougou_id, $size, $title, $ori_pic_url, $width, $height)
    {
        $this->sougou_id = $sougou_id;
        $this->size = $size;
        $this->title = $title;
        $this->ori_pic_url = $ori_pic_url;
        $this->width = $width;
        $this->height = $height;

        $this->saveDir.='./static/sougouImages/'.date('Y-m-d').'/'.$this->width.'_'.$this->height.'/';
        $this->downloadFailedTxt = $this->saveDir.'1_downloadfailedlist.txt';
        if(!is_dir($this->saveDir)){
            mkdir($this->saveDir, 0777, true);
            chmod($this->saveDir, 0777);
        }
    }

    public function run()
    {
        //下载
        $return = $this->downloadImage();
        if (!$return) {
            echo $this->ori_pic_url.' Download failed'.PHP_EOL;
        }else{
            echo $this->ori_pic_url.' Download completed'.PHP_EOL;
        }
    }


    /**
     * 下载图片
     * @return bool
     */
    public function downloadImage()
    {
        //图片后缀
        $basename = basename($this->ori_pic_url);
        $dot = strrpos($basename, '.');
        $imageExt = substr($basename, $dot);
        $imageTitle = substr($basename, 0, $dot);
        if(strlen($this->title)<2){
            //图片名称
            $imageName = $imageTitle.'_'.$this->sougou_id;
        }else{
            //图片名称
            $imageName = $this->title.'_'.$this->sougou_id;
        }
        $imageName = iconv('UTF-8', 'GBK', $imageName);
        //保存图片路径及名称
        $filename = $this->saveDir.$imageName.$imageExt;
        //判断文件是否已下载
        if(file_exists($filename)){
            file_put_contents($this->downloadFailedTxt, $this->ori_pic_url.' --文件已下载'.PHP_EOL, FILE_APPEND);
            return true;
        }
        //获取远程文件
        $imgContent = $this->myCurl($this->ori_pic_url);
        if(!$imgContent){
            return false;
        }
        //保存图片到本地
        $return=file_put_contents($filename, $imgContent);

        if(!file_exists($filename)){
            //记录下载失败的图片信息
            file_put_contents($this->downloadFailedTxt, $this->ori_pic_url.' --下载失败'.PHP_EOL, FILE_APPEND);
            return false;
        }

        //图片大小与录入时记录的图片大小不一致,说明图片下载失败
        if(filesize($filename) != $this->size){
            //下载图片不完整,删除
            unlink($filename);
            file_put_contents($this->downloadFailedTxt, $this->ori_pic_url.' --下载图片不完整'.PHP_EOL, FILE_APPEND);
            return false;
        }
        return true;
    }


    /**
     * @author RenZhicai <renzhicai.sz@mopon.cn>
     * 自定义curl请求
     * @param string $url
     * @return mixed
     */
    public function myCurl($url='')
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_TIMEOUT,60);   //只需要设置一个秒的数量就可以
        if(preg_match('/https:\/\//', $url)){
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //这个是重点。
        }
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1');
        $output = curl_exec($ch);
        if($output === false){
            //记录curl请求失败的详情
            $errorInfo = "cURL Error: ".curl_error($ch);
            file_put_contents('curl_error_'.date('Ymd').'.txt', $errorInfo, FILE_APPEND);
        }
        curl_close($ch);

        return $output;
    }
}

2、在Yii2项目根目录执行控制台脚本(console)

//将执行结果记录到文本文件中
php yii sougou/download > sougoudownloadinfo.txt

3、下载完后效果一览

自动按尺寸归类图片

【参考资料】

posted @ 2017-11-20 01:31  才鱼  阅读(640)  评论(0编辑  收藏  举报