Veiking百草园


/ Java编程

老狗啃爬虫-图片下载之Pipeline

老狗啃骨头   @Veiking   2020-12-18

老狗啃爬虫-图片下载之Pipeline

摘要:

WebMagic框架的使用中,Pipeline组件完全是插拔式的,即我们根据功能需要,可以随意增加组合实现。事实上WebMagic框架所有的功能组件都是如此,Spider是一个队列总线,所有的组件都通过接口的实现,参与到整个数据抓取处理流程的工作中来,分工合作,按需定制,这种设计思想也是以后我们在程序设计上非常值得借鉴的

  上一篇内容我们了解到,如何实现一个从页面抓取数据,存入数据库的Pipeline。有些时候我们可能需要将抓取到的图片,直接以文件的形式存储起来,同样,我们亦可以实现一个专门的Pipeline,来实现此功能。

准备

  为了配合数据的存储和图片的下载,我们在之前的测试过的功能和代码基础上稍作修改:
  AigufengCelebrity类:

package cn.veiking.biz.model;

import java.io.Serializable;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
* @author    :Veiking
* @version    :2020年12月5日
* 说明        :爱古风人物信息
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AigufengCelebrity implements Serializable{
    private static final long serialVersionUID = 1L;
    private Integer id;
    private String name;
    private String avatar; //
    private String dynasty;
    private String content;
}

  我们在AigufengCelebrity新增属性avatar,来承接网站页面上人物肖像的图片信息;与此同时,协同存储的DAO接口、数据库亦作同步增改,这里就不赘述。
  AigufengCelebrityProcessor类:

package cn.veiking.spider;

import java.util.List;

import org.springframework.stereotype.Service;

import cn.veiking.base.common.logs.SimLogger;
import cn.veiking.biz.model.AigufengCelebrity;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.processor.PageProcessor;

/**
* @author    :Veiking
* @version    :2020年12月6日
* 说明        :爱古风-人物信息数据获取
*/
@Service
public class AigufengCelebrityProcessor implements PageProcessor {
    SimLogger logger = new SimLogger(this.getClass());

    // 抓取目标也
    private static final String TargetUrl = "http://www\\.aigufeng\\.com/celebrity/article-\\d+/page\\.html";
    // 抓取统计,我们只抓取10条记录
    private static int count = 0;

    @Override
    public Site getSite() {
        Site site = Site.me().setRetryTimes(5).setSleepTime(1000).setTimeOut(10000);
        return site;
    }

    //
    @Override
    public void process(Page page) {
        if(page.getUrl().regex(TargetUrl).match()) {
            // 获取前后翻页页面连接并添加至待抓序列 
            List links= page.getHtml().xpath("//*[@id=\"content\"]/div/div[1]/div[2]/div[2]/div[1]").links().regex(TargetUrl).all();
            page.addTargetRequests(links);
            // 获取页面信息
            String name = page.getHtml().xpath("//*[@id=\"content\"]/div/div[1]/div[2]/div[1]/h1/a/text()").get();
            String avatarUrl = page.getHtml().xpath("//*[@id=\"content\"]/div/div[1]/div[2]/div[1]/a/img/@src").toString(); // 获取头像图片路径
            String dynasty = page.getHtml().xpath("//*[@id=\"content\"]/div/div[1]/div[2]/div[1]/h2/span/a/text()").get().replace("(", "").replace(")", "");
            String content = page.getHtml().xpath("//*[@id=\"icontentContent\"]/text()").get();
            // 页面信息加工备用
            AigufengCelebrity model = new AigufengCelebrity();
            model.setName(name);
            model.setAvatar(avatarUrl);
            model.setDynasty(dynasty);
            model.setContent(content);
            page.putField("aigufengCelebrity", model);
            logger.info("AigufengCelebrityProcessor process [model={}] ", model);
            count ++;
            // 程序自爆,运行终止
            if(count>10) {
                System.exit(0);
            }
        }
    }
}

  我们在AigufengCelebrityProcessor类在原基础上新增抓取页面人物肖像数据,即获取图片的路径。

实现图片下载

  接下来就是程序内容的重点,既然要做图片下载,我们拿之前写的一个可以实现文件下载功能的类,作为下载工具:

package cn.veiking.base.utils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;

/**
 * @Describe:文件工具
 * @Author     :veiking
 * @version :2020年8月20日
 */
public class HttpFileTools {

    private static final int DOWNLOAD_TIMEOUT = 8*1000;

    /**
     * 下载文件(网络图片下载)
     * @param urlString
     * @param filename
     * @param savePath
     * @throws Exception
     */
    public static void download(String fromUrl, String filename, String savePath) throws Exception {
        // 构造URL
        URL from = new URL(fromUrl);
        // 打开连接
        URLConnection con = from.openConnection();
        //设置请求超时为5s
        con.setConnectTimeout(DOWNLOAD_TIMEOUT);
        // 输入流
        InputStream is = con.getInputStream();
        // 1K的数据缓冲
        byte[] bs = new byte[1024];
        // 读取到的数据长度
        int len;
        // 输出的文件流
        File sf = new File(savePath);
        if (!sf.exists()) {
            sf.mkdirs();
        }
        OutputStream os = new FileOutputStream(sf.getPath() + "\\" + filename);
        // 开始读写
        while ((len = is.read(bs)) != -1) {
          os.write(bs, 0, len);
        }
        // 完毕,关闭所有链接
        os.close();
        is.close();
    }

}

  HttpFileTools这个工具类的download方法,可根据fromUrl、 filename、savePath这三个参数,即文件链接文件名文件存储路径,实现文件下载。
  我们这里是在学习WebMagic,于是要考虑在WebMagic的框架内来结合此功能,故而我们要再实现一个Pipeline,来完成图片下载的功能,即新建一个管道类:

package cn.veiking.spider;

import org.springframework.stereotype.Service;

import cn.veiking.base.common.logs.SimLogger;
import cn.veiking.base.utils.HttpFileTools;
import cn.veiking.biz.model.AigufengCelebrity;
import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.pipeline.Pipeline;

/**
* @author    :Veiking
* @version    :2020年12月12日
* 说明        :爱古风-人物信息-头像图片数据下载存储管道
*/
@Service
public class AigufengCelebrityImgPipeline implements Pipeline{
    SimLogger logger = new SimLogger(this.getClass());
    // 图片存储目录
    private static final String BASE_IMG_FILE_PATH = "E:\\data\\img\\";

    @Override
    public void process(ResultItems resultItems, Task task) {
        AigufengCelebrity model = resultItems.get("aigufengCelebrity");
        if(model != null) {
            String avatarUrl = model.getAvatar();
                // 获取图片的文件名
                String fileName = avatarUrl.substring(avatarUrl.lastIndexOf("/") + 1);
            try {
                HttpFileTools.download(avatarUrl, fileName, BASE_IMG_FILE_PATH);
            } catch (Exception e) {
                e.printStackTrace();
                logger.info("AigufengCelebrityImgPipeline process exception ... ");
            }
             logger.info("AigufengCelebrityImgPipeline process download img [avatar={}]", fileName);
        }
    }
}

  我们可以看到,AigufengCelebrityImgPipeline类,也是在获取到AigufengCelebrity信息后,开始执行人物肖像的图片下载。

程序测试

  完成上面的代码之后,就可以测试了,在原来的测试代码基础上,我们增加AigufengCelebrityImgPipeline,代码如下:

package cn.veiking.aigufeng;

import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import cn.veiking.StartTest;
import cn.veiking.base.common.logs.SimLogger;
import cn.veiking.spider.AigufengCelebrityImgPipeline;
import cn.veiking.spider.AigufengCelebrityPipeline;
import cn.veiking.spider.AigufengCelebrityProcessor;
import us.codecraft.webmagic.Spider;

/**
* @author    :Veiking
* @version    :2020年12月8日
* 说明        :aigufengCelebrityPipeline、aigufengCelebrityImgPipeline、aigufengCelebrityProcessor 测试
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = StartTest.class)
public class AigufengCelebrityTest {
    SimLogger logger = new SimLogger(this.getClass());

    @Autowired
    private AigufengCelebrityPipeline aigufengCelebrityPipeline;
    @Autowired
    private AigufengCelebrityImgPipeline aigufengCelebrityImgPipeline;

    @Autowired
    private AigufengCelebrityProcessor aigufengCelebrityProcessor;

    private static final String StartUrl = "http://www.aigufeng.com/celebrity/article-1000056/page.html";

    @Test
    public void testSpider() {
        long startTime, endTime;
        logger.info("AigufengCelebrityTest testSpider [start={}] ", "开始爬取数据");
        startTime = System.currentTimeMillis();
        Spider.create(aigufengCelebrityProcessor)
        .addUrl(StartUrl)
        .addPipeline(aigufengCelebrityPipeline)
        .addPipeline(aigufengCelebrityImgPipeline) // 图片下载管道
        .thread(3)
        .run();

        endTime = System.currentTimeMillis();
        logger.info("AigufengCelebrityTest testSpider [end={}] ", "爬取结束,耗时约" + ((endTime - startTime) / 1000) + "秒");
    }
}

  好了,代码准备完了即可进行测试。程序运行结束之后我们可以看到,日志信息已经打印出来了,图片文件也已经成功下载:


  (由于这里我们只是在测试,当抓取10个页面之后程序是被暴力终止了,所以,图片下载实际上还没有完全结束,故少了一个文件)

结语

  本次学习我们可以发现,WebMagic框架的使用中,Pipeline组件完全是插拔式的,即我们根据功能需要,可以随意增加组合实现。事实上WebMagic框架所有的功能组件都是如此,Spider像一个队列总线,所有的组件都通过接口的实现,参与到整个数据抓取处理流程的工作中来,分工合作,按需定制,这种设计思想也是以后我们在程序设计上非常值得借鉴的。
  在WebMagic框架的爬虫实现中,图片的下载如此,其他的文件,类如音频视频等,基本也是同样的思路,可以通过实现一个对应功能的Pipeline类即可,以后有机会我们也尝试下。


老狗啃骨头



慷慨发言

(您提供的信息将用于后续必要的反馈联系,本站会恪守隐私)

潜影拾光

荔枝公园

层峦叠嶂的楼宇,深圳缩影

扫码转发

二维码
二维码
二维码
二维码
二维码
二维码

博文标签