webmagic 二次开发爬虫 爬取网站图片
webmagic的是一个无须配置、便于二次开发的爬虫框架,它提供简单灵活的API,只需少量代码即可实现一个爬虫。
webmagic介绍 编写一个简单的爬虫
webmagic的使用文档:http://webmagic.io/docs/
webmagic的设计文档:webmagic的设计机制及原理-如何开发一个Java爬虫
1.编写一个核心的url过滤类
1 package com.xwer.spider.main; 2 3 import java.util.List; 4 5 import org.apache.log4j.Logger; 6 7 import us.codecraft.webmagic.Page; 8 import us.codecraft.webmagic.Site; 9 import us.codecraft.webmagic.processor.PageProcessor; 10 import us.codecraft.webmagic.utils.UrlUtils; 11 12 /** 13 * 定制爬虫逻辑的核心类 14 * @author xwer 15 * 16 */ 17 public class MM_Processor implements PageProcessor { 18 private Logger logger = Logger.getLogger(this.getClass()); 19 // 部分一:抓取网站的相关配置,包括编码、抓取间隔、重试次数等 20 private Site site = Site.me().setRetryTimes(5).setSleepTime(1000); 21 // 网页匹配规则 22 private String urlPattern; 23 public MM_Processor(String startUrl, String urlPattern) { 24 // 设置所属域 25 this.site = Site.me().setDomain(UrlUtils.getDomain(startUrl)); 26 this.urlPattern = urlPattern; 27 } 28 29 @Override 30 // process是定制爬虫逻辑的核心接口,在这里编写抽取逻辑 31 public void process(Page page) { 32 site.setUserAgent("Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)"); 33 //图片抓取规则 34 String imgRegex3 = "http://mm.howkuai.com/wp-content/uploads/20[0-9]{2}[a-z]/[0-9]{2}/[0-9]{2}/[0-9]{1,4}.jpg"; 35 // 获取目标链接 例如 http://www.meizitu.com/a/5535.html 36 List<String> requests = page.getHtml().links().regex(urlPattern).all(); 37 logger.info("获取到的目标链接是: "+requests); 38 logger.info("添加链接( "+requests.size()+" )条到集合"); 39 40 //将获取的链接存入到targetRequests中(list集合) 41 page.addTargetRequests(requests); 42 logger.info("队列中存储的链接数是: "+page.getResultItems().getAll().size()); 43 44 // 图片的title,标题名称,用于设定文件夹的名称 45 String imgHostFileName = page.getHtml().xpath("//title/text()").replace("\\p{Punct}", "").toString(); 46 logger.info("获取的标题是"+imgHostFileName); 47 48 List<String> listProcess = page.getHtml().regex(imgRegex3).all(); 49 logger.info("存入的图片地址: "+listProcess); 50 // 此处将标题一并抓取,之后提取出来作为文件名 51 listProcess.add(0, imgHostFileName); 52 logger.info("存入的图片链接数量是: "+listProcess.size()); 53 //将获取到的页面的数据放到resultItems集合中(map) 54 page.putField("img", listProcess); 55 } 56 @Override 57 public Site getSite() { 58 return site; 59 } 60 }
2.对获取的结果进行持久化处理
1 package com.xwer.spider.main; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 import java.util.Map; 6 import org.apache.log4j.Logger; 7 import com.xwer.spider.utils.DownLoadUtils; 8 import us.codecraft.webmagic.ResultItems; 9 import us.codecraft.webmagic.Task; 10 import us.codecraft.webmagic.pipeline.Pipeline; 11 12 /** 13 * 处理 14 * @author xwer 15 * 16 */ 17 public class MM_Pipeline implements Pipeline { 18 private Logger logger = Logger.getLogger(this.getClass()); 19 private String path; 20 21 public MM_Pipeline() { 22 setPath("/MM/"); 23 } 24 25 public MM_Pipeline(String path) { 26 setPath(path); 27 } 28 29 public void setPath(String path) { 30 this.path = path; 31 } 32 33 // 处理下载的方法 34 @Override 35 public void process(ResultItems resultItems, Task task) { 36 logger.info("到了process" + resultItems); 37 String fileStorePath = this.path; 38 for (Map.Entry<String, Object> entry : resultItems.getAll().entrySet()) { 39 if (entry.getValue() instanceof List) { 40 List<String> list = new ArrayList<String>((List) entry.getValue()); 41 //取出之前存的网页的标题,拼接成一个新的文件夹名称 42 fileStorePath = new StringBuffer(fileStorePath) 43 .append(list.get(0)) 44 .append("\\").toString(); 45 //遍历图片链接list 46 for (int i = 1; i < list.size(); i++) { 47 // 获取文件唯一名字 48 String realname = DownLoadUtils.subFileName(list.get(i)); 49 String uuidname = DownLoadUtils.generateRandonFileName(realname); 50 // 这里通过自己写的下载工具前抓取到的图片网址,并放在对应的文件中 51 try { 52 DownLoadUtils.download(list.get(i), uuidname, fileStorePath); 53 logger.info("文件" + uuidname +"已经下载完毕"); 54 } catch (Exception e) { 55 logger.warn("文件下载异常" + list.get(i)); 56 e.printStackTrace(); 57 } 58 } 59 } 60 else { 61 System.out.println(entry.getKey() + ":\t" + entry.getValue()); 62 } 63 } 64 } 65 }
3.编写一个下载图片的工具类
1 package com.xwer.spider.utils; 2 3 import java.io.File; 4 import java.io.FileOutputStream; 5 import java.io.InputStream; 6 import java.io.OutputStream; 7 import java.net.URL; 8 import java.net.URLConnection; 9 import java.util.UUID; 10 11 /** 12 * 下载相关的工具类 13 * 14 * @author xwer 15 * 16 */ 17 public class DownLoadUtils { 18 19 /** 20 * 下载图片工具 21 * 22 * @param urlString 23 * 图片链接地址 24 * @param filename 25 * 图片的文件名字 26 * @param savePath 27 * 图片保存的路径 28 * @throws Exception 29 */ 30 public static void download(String urlString, String filename, String savePath) throws Exception { 31 // 构造URL 32 URL url = new URL(urlString); 33 // 打开连接 34 URLConnection con = url.openConnection(); 35 // 设置请求头 36 con.addRequestProperty("User-Agent", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)"); 37 // 设置请求超时为5s 38 con.setConnectTimeout(5 * 1000); 39 // 输入流 40 InputStream is = con.getInputStream(); 41 42 // 1K的数据缓冲 43 byte[] bs = new byte[1024]; 44 // 读取到的数据长度 45 int len; 46 // 输出的文件流 47 File sf = new File(savePath); 48 if (!sf.exists()) { 49 sf.mkdirs(); 50 } 51 OutputStream os = new FileOutputStream(sf.getPath() + "\\" + filename); 52 // 开始读取 53 while ((len = is.read(bs)) != -1) { 54 os.write(bs, 0, len); 55 } 56 // 完毕,关闭所有链接 57 os.close(); 58 is.close(); 59 } 60 61 /** 62 * 截取真实文件名 63 * 64 * @param fileName 65 * @return 66 */ 67 public static String subFileName(String fileName) { 68 // 查找最后一个 \出现位置 69 int index = fileName.lastIndexOf("\\"); 70 if (index == -1) { 71 return fileName; 72 } 73 return fileName.substring(index + 1); 74 } 75 76 /** 77 * 获得随机UUID文件名 78 * 79 * @param fileName 80 * @return 81 */ 82 public static String generateRandonFileName(String fileName) { 83 // 获得扩展名 84 String ext = fileName.substring(fileName.lastIndexOf(".")); 85 return UUID.randomUUID().toString().replace("-", "") + ext; 86 } 87 }
4.配置一个日志的输出文件(用户打印日志)
1 ### direct log messages to stdout ### 2 log4j.appender.stdout=org.apache.log4j.ConsoleAppender 3 log4j.appender.stdout.Target=System.out 4 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 5 log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n 6 7 ### direct messages to file mylog.log ### 8 log4j.appender.file=org.apache.log4j.FileAppender 9 log4j.appender.file.File=c:/mylog3.log 10 log4j.appender.file.layout=org.apache.log4j.PatternLayout 11 log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n 12 13 ### set log levels - for more verbose logging change 'info' to 'debug' ### 14 log4j.rootLogger=info, stdout ,file
5.编写程序的入口类
1 package com.xwer.spider.main; 2 3 import java.util.regex.Pattern; 4 5 import org.junit.Test; 6 7 8 9 import us.codecraft.webmagic.Spider; 10 import us.codecraft.webmagic.scheduler.FileCacheQueueScheduler; 11 12 public class MM_test { 13 public static void main(String[] args) { 14 //图片的存放路径,PiPline需要用到 15 String fileStorePath = "D:\\test\\"; 16 17 //过滤网页的正则 http://www.meizitu.com/a/more_1.html 18 String urlPattern = "http://www.meizitu.com/[a-z]/[0-9]{1,4}.html"; 19 //自定义的解析器核心 20 MM_Processor mmSprider = new MM_Processor("http://www.meizitu.com/", urlPattern); 21 22 //设置一些种子链接 23 String[] urls ={"http://www.meizitu.com/", 24 "http://www.meizitu.com/a/4221.html", 25 "http://www.meizitu.com/a/4467.html", 26 "http://www.meizitu.com/a/5467.html", 27 "http://www.meizitu.com/a/5065.html", 28 "http://www.meizitu.com/a/4278.html", 29 "http://www.meizitu.com/a/699.html", 30 }; 31 //启动爬虫 32 Spider.create(mmSprider).addUrl(urls) 33 .setScheduler(new FileCacheQueueScheduler("D:\\webmagic\\cach")) 34 .addPipeline(new MM_Pipeline(fileStorePath)) 35 .thread(10) 36 .run(); 37 } 38 39 40
6. 爬取结果