这个例子演示了如何同时运行两个不同的爬虫。
首先写一个BasicCrawler类:
package com.java1234.multipleCrawlers; import java.util.Set; import java.util.regex.Pattern; import edu.uci.ics.crawler4j.crawler.Page; import edu.uci.ics.crawler4j.crawler.WebCrawler; import edu.uci.ics.crawler4j.parser.HtmlParseData; import edu.uci.ics.crawler4j.url.WebURL; /** * 自定义爬虫类需要继承WebCrawler类,决定哪些url可以被爬以及处理爬取的页面信息 * @author user * */ public class BasicCrawler extends WebCrawler { // 正则匹配指定的后缀文件 private static final Pattern FILTERS = Pattern.compile( ".*(\\.(css|js|bmp|gif|jpe?g" + "|png|tiff?|mid|mp2|mp3|mp4" + "|wav|avi|mov|mpeg|ram|m4v|pdf" + "|rm|smil|wmv|swf|wma|zip|rar|gz))$"); private String[] myCrawlDomains; // 目标爬虫域名 /** * 爬虫实例启动前调用,一般是作用是初始化一些数据 */ @Override public void onStart() { myCrawlDomains = (String[]) myController.getCustomData(); // 获取自定义数据 目标爬虫域名 控制器类对象里会设置的 } /** * 这个方法主要是决定哪些url我们需要抓取,返回true表示是我们需要的,返回false表示不是我们需要的Url * 第一个参数referringPage封装了当前爬取的页面信息 * 第二个参数url封装了当前爬取的页面url信息 */ @Override public boolean shouldVisit(Page referringPage, WebURL url) { String href = url.getURL().toLowerCase(); // 得到小写的url // 正则匹配,过掉一些指定后缀的文件 if (FILTERS.matcher(href).matches()) { return false; } // 遍历目标域名 指定目标爬虫域名 for (String crawlDomain : myCrawlDomains) { if (href.startsWith(crawlDomain)) { return true; } } return false; } /** * 当我们爬到我们需要的页面,这个方法会被调用,我们可以尽情的处理这个页面 * page参数封装了所有页面信息 */ @Override public void visit(Page page) { int docid = page.getWebURL().getDocid(); // 获取docid url的唯一识别 类似主键 String url = page.getWebURL().getURL(); // 获取url int parentDocid = page.getWebURL().getParentDocid(); // 获取上级页面的docId System.out.println("docId:"+docid); System.out.println("url:"+url); System.out.println("上级页面docId:"+parentDocid); if (page.getParseData() instanceof HtmlParseData) { // 判断是否是html数据 HtmlParseData htmlParseData = (HtmlParseData) page.getParseData(); // 强制类型转换,获取html数据对象 String text = htmlParseData.getText(); // 获取页面纯文本(无html标签) String html = htmlParseData.getHtml(); // 获取页面Html Set<WebURL> links = htmlParseData.getOutgoingUrls(); // 获取页面输出链接 System.out.println("纯文本长度: " + text.length()); System.out.println("html长度: " + html.length()); System.out.println("输出链接个数: " + links.size()); } System.out.println("======================"); } }
再定义一个爬虫控制器MultipleCrawlerController类:
package com.java1234.multipleCrawlers; import edu.uci.ics.crawler4j.crawler.CrawlConfig; import edu.uci.ics.crawler4j.crawler.CrawlController; import edu.uci.ics.crawler4j.fetcher.PageFetcher; import edu.uci.ics.crawler4j.robotstxt.RobotstxtConfig; import edu.uci.ics.crawler4j.robotstxt.RobotstxtServer; /** * 多线程爬虫控制器 * @author user * */ public class MultipleCrawlerController { public static void main(String[] args) throws Exception { String crawlStorageFolder = "c:/crawl"; // 定义爬虫数据存储位置 CrawlConfig config1 = new CrawlConfig(); // 爬虫配置实例1 CrawlConfig config2 = new CrawlConfig(); // 爬虫配置实例2 // 两个爬虫实例文件分别存储各种文件夹 config1.setCrawlStorageFolder(crawlStorageFolder + "/crawler1"); config2.setCrawlStorageFolder(crawlStorageFolder + "/crawler2"); config1.setPolitenessDelay(1000); // 设置1秒爬取一次 config2.setPolitenessDelay(2000); // 设置2秒爬取一次 config1.setMaxPagesToFetch(5); // 设置最大爬取页数5 config2.setMaxPagesToFetch(6); // 设置最大爬取页数6 // 使用两个PageFetcher实例 PageFetcher pageFetcher1 = new PageFetcher(config1); PageFetcher pageFetcher2 = new PageFetcher(config2); // 使用同一个RobotstxtServer实例 RobotstxtConfig robotstxtConfig = new RobotstxtConfig(); RobotstxtServer robotstxtServer = new RobotstxtServer(robotstxtConfig, pageFetcher1); CrawlController controller1 = new CrawlController(config1, pageFetcher1, robotstxtServer); CrawlController controller2 = new CrawlController(config2, pageFetcher2, robotstxtServer); // 分别指定目标爬虫域名 String[] crawler1Domains = {"http://www.zuidaima.com/"}; String[] crawler2Domains = {"http://www.java1234.com/"}; // 设置自定义数据 controller1.setCustomData(crawler1Domains); controller2.setCustomData(crawler2Domains); // 配置爬虫种子页面,就是规定的从哪里开始爬,可以配置多个种子页面 controller1.addSeed("http://www.zuidaima.com/"); controller1.addSeed("http://www.zuidaima.com/share/p1-s1.htm"); controller2.addSeed("http://www.java1234.com/"); controller2.addSeed("http://www.java1234.com/a/bysj/javaweb/"); // 启动爬虫,爬虫从此刻开始执行爬虫任务,根据以上配置 根据源码 这种启动是无阻塞的 controller1.startNonBlocking(BasicCrawler.class, 5); controller2.startNonBlocking(BasicCrawler.class, 7); controller1.waitUntilFinish(); // 直到完成 System.out.println("爬虫1任务结束"); controller2.waitUntilFinish(); // 直到完成 System.out.println("爬虫2任务结束"); } }
运行结果:
docId:1 url:http://www.java1234.com/ 上级页面docId:0 纯文本长度: 3852 html长度: 45322 输出链接个数: 148 ====================== docId:1 url:http://www.zuidaima.com/ 上级页面docId:0 纯文本长度: 5068 html长度: 57570 输出链接个数: 325 ====================== docId:2 url:http://www.zuidaima.com/share/p1-s1.htm 上级页面docId:0 纯文本长度: 5272 html长度: 66409 输出链接个数: 261 ====================== docId:3 url:http://www.zuidaima.com/share/2814499085634560.htm 上级页面docId:1 纯文本长度: 3576 html长度: 34466 输出链接个数: 218 ====================== docId:3 url:http://www.java1234.com/a/bysj/javaweb/2014/0401/1896.html 上级页面docId:1 纯文本长度: 2133 html长度: 29038 输出链接个数: 55 ====================== docId:4 url:http://www.zuidaima.com/share/2519215530527744.htm 上级页面docId:1 纯文本长度: 4816 html长度: 38285 输出链接个数: 228 ====================== docId:5 url:http://www.zuidaima.com/share/2783070349888512.htm 上级页面docId:1 纯文本长度: 3985 html长度: 34784 输出链接个数: 225 ====================== docId:4 url:http://www.java1234.com/a/bysj/javaweb/2016/0316/5826.html 上级页面docId:1 纯文本长度: 2119 html长度: 30022 输出链接个数: 53 ====================== docId:5 url:http://www.java1234.com/a/javaziliao/shuji/2016/0603/6204.html 上级页面docId:1 纯文本长度: 2040 html长度: 26507 输出链接个数: 56 ====================== 爬虫1任务结束 爬虫2任务结束