这个例子演示了如何同时运行两个不同的爬虫。
首先写一个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任务结束