老狗啃爬虫-图片下载之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类即可,以后有机会我们也尝试下。