上节课我们实现了一个单线程的网络爬虫,但是,很明显,单线程的爬取效率是远远不能满足我们的需求的,因此,我们需要将单线程的爬虫改编为多线程的爬虫。
在java中,实现多多线程只需要实现Runnable接口即可:
public class MultiCrawlerThread implements Runnable {
private final MultiCrawler multiCrawler;
private final HtmlParser htmlParser = new HtmlParser();
public MultiCrawlerThread(MultiCrawler multiCrawler) {
super();
this.multiCrawler = multiCrawler;
}
@Override
public void run() {
WebPage webPage;
int getUnCrawlPageTimes = 0;
while (true) {
webPage = multiCrawler.getUnCrawlPage();
if(webPage == null) {
if(getUnCrawlPageTimes > 10) {
break;
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
getUnCrawlPageTimes++;
continue;
}
}
getUnCrawlPageTimes = 0;
// your code here
}
}
}
怎么样,是不是和上节课实现的doRun
方法很相似?实际上多线程的爬虫就是将单线程爬虫的doRun
方法放到线程中,由多个线程同步执行,这样就大大加快了我们爬取页面的速度。
另外,while (true)
包含的代码是为了防止开始时爬虫队列中未爬取页面过少,线程拿不到足够的未爬页面而过早退出。
多线程的爬虫主程序与单线程的爬虫主程序一样,都实现了Crawler
,与单线程的不同在于,doRun
方法需要调用多个线程,同步爬取页面:
public static final Integer MAX_THREADS = 20;
public void runThreads() throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(MAX_THREADS);
for(int i = 0; i < MAX_THREADS; i++) {
executorService.execute(new MultiCrawlerThread(this));
}
executorService.shutdown();
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
}
实现完所有方法后,执行main函数,可以看到,多线程爬虫的爬取时间远远小于单线程的爬取时间。在需要爬取大规模数据的情况下,多线程是一项必不可少的技术。
登录发表评论 登录 注册