转载请注明出处:
个人博客站已经上线了,网址 ~欢迎各位吐槽~
-------------------------------------------------------------------------------------------------在前面的几篇博客中,我们已经介绍了怎样採集纵横小说站点上的信息以及怎样把这些信息持久化到数据库中。如今我们就開始介绍怎样做分布式採集,让各个模块之间能够完美的配合。
採集类改动
在開始介绍分布式採集之前。我们须要对之前介绍的採集类加入一些方法。也就是返回上一篇博客中介绍的小说javabean,详细源代码还请參照个人站点上的。
1.简单介绍页
简单介绍页需呀加入一个方法,让它返回简单介绍页的数据信息,详细例如以下:
/** * @return * @Author:lulei * @Description: 分析简单介绍页。获取简单介绍页数据 */ public NovelIntroModel getNovelIntro() { NovelIntroModel bean = new NovelIntroModel(); bean.setMd5Id(ParseMD5.parseStrToMd5L32(this.pageUrl)); bean.setName(getName()); bean.setAuthor(getAuthor()); bean.setDescription(getDesc()); bean.setType(getType()); bean.setLastChapter(getLatestChapter()); bean.setChapterlisturl(getChapterListUrl()); bean.setWordCount(getWordCount()); bean.setKeyWords(keyWords()); return bean; }2.阅读页
阅读页内相同须要加入一个方法。让它返回阅读页内的数据信息,详细例如以下:
/** * @return * @Author:lulei * @Description: 分析阅读页。获取阅读页数据 */ public NovelReadModel getNovelRead(){ NovelReadModel novel = new NovelReadModel(); novel.setTitle(getTitle()); novel.setWordCount(getWordCount()); novel.setContent(getContent()); return novel; }
这些方法都是对之前类中的方法做一个整合。将之前分析到的数据组装成一个javabean返回,方便后面的操作。
各页採集线程类
在实现分布式採集的时候,就须要编写各个页面的採集线程类。让他来控制各页面的採集业务。以下我们就一一介绍:
1.更新列表页线程
这个线程的主要功能就是监控更新列表页的数据。提取页面上的简单介绍页URL,觉得它们是有更新的页面,将相应的信息持久化到数据库中,详细实现例如以下:
/** *@Description: 更新列表页线程 */ package com.lulei.crawl.novel.zongheng; import java.util.List;import java.util.concurrent.TimeUnit;import com.lulei.db.novel.zongheng.ZonghengDb; public class UpdateListThread extends Thread{ private boolean flag = false; private String url;//抓取的更新列表页URL private int frequency;//採集频率 public UpdateListThread(String name, String url, int frequency){ super(name); this.url = url; this.frequency = frequency; } @Override public void run() { flag = true; ZonghengDb db = new ZonghengDb(); while (flag){ try { UpdateList updateList = new UpdateList(url); List2.简单介绍页&章节列表页线程类urls = updateList.getPageUrls(true); db.saveInfoUrls(urls); TimeUnit.SECONDS.sleep(frequency); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } super.run(); } public static void main(String[] args) { // TODO Auto-generated method stub UpdateListThread thread = new UpdateListThread("llist", "http://book.zongheng.com/store/c0/c0/b9/u0/p1/v0/s9/t0/ALL.html", 60); thread.start(); }}
因为一个简单介绍页就相应一个章节列表页,所以我们就把这两个线程合为一个线程,让事实上现小说简单介绍信息的採集以及小说章节列表信息的採集,详细实现例如以下:
/** *@Description: 小说简单介绍信息线程 */ package com.lulei.crawl.novel.zongheng; import java.util.List;import java.util.concurrent.TimeUnit;import com.lulei.crawl.novel.zongheng.model.NovelIntroModel;import com.lulei.db.novel.zongheng.ZonghengDb; public class IntroPageThread extends Thread { private boolean flag = false; public IntroPageThread(String name) { super(name); } @Override public void run() { flag = true; try { ZonghengDb db = new ZonghengDb(); while (flag) { //随机获取一个待採集的简单介绍页url String url = db.getRandIntroPageUrl(1); if (url != null) { IntroPage intro = new IntroPage(url); NovelIntroModel bean = intro.getNovelIntro(); //採集小说章节列表页信息 ChapterPage chapterPage = new ChapterPage(bean.getChapterlisturl()); Listchapters = chapterPage.getChaptersInfo(); bean.setChapterCount(chapters == null ?
0 : chapters.size()); //更新小说简单介绍信息 db.updateInfo(bean); //插入待採集的章节列表 db.saveChapters(chapters); //假设本次有待採集的资源。睡眠一个时间。没有待採集的资源。睡眠还有一个时间 TimeUnit.MILLISECONDS.sleep(500); }else { TimeUnit.MILLISECONDS.sleep(1000); } } } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { // TODO Auto-generated method stub IntroPageThread thread = new IntroPageThread("novelinfo"); thread.start(); } }
3.阅读页线程这个线程的主要功能就是将小说阅读页的信息採集并持久化到数据库中。详细例如以下:
/** *@Description: 小说阅读页线程 */ package com.lulei.crawl.novel.zongheng; import java.util.concurrent.TimeUnit;import com.lulei.crawl.novel.zongheng.model.NovelChapterModel;import com.lulei.crawl.novel.zongheng.model.NovelReadModel;import com.lulei.db.novel.zongheng.ZonghengDb;import com.lulei.util.ParseMD5; public class ReadPageThread extends Thread { private boolean flag = false; public ReadPageThread(String name) { super(name); } @Override public void run() { flag = true; ZonghengDb db = new ZonghengDb(); while (flag) { try { //随机获取待採集的阅读页 NovelChapterModel chapter = db.getRandReadPageUrl(1); if (chapter != null) { ReadPage read = new ReadPage(chapter.getUrl()); NovelReadModel novel = read.getNovelRead(); if (novel == null) { continue; } novel.setChapterId(chapter.getChapterId()); novel.setTime(chapter.getTime()); novel.setUrl(chapter.getUrl()); //保存阅读页信息 db.saveNovelRead(novel); //将状态改动为不须要採集 db.updateChapterState(ParseMD5.parseStrToMd5L32(novel.getUrl()), 0); //假设本次有待採集的资源,睡眠一个时间,没有待採集的资源,睡眠还有一个时间 TimeUnit.MILLISECONDS.sleep(500); } else { TimeUnit.MILLISECONDS.sleep(1000); } } catch(Exception e){ e.printStackTrace(); } } } public static void main(String[] args) { ReadPageThread thread = new ReadPageThread("novel read page"); thread.start(); }}分布式採集
上面已经介绍完了各个线程完毕的工作,以下就须要一个类来控制管理这些线程。让其执行起来,详细代码例如以下:
/** *@Description: */ package com.lulei.crawl.novel.zongheng; import java.util.List;import com.lulei.crawl.novel.zongheng.model.CrawlListInfo;import com.lulei.db.novel.zongheng.ZonghengDb; public class CrawStart { private static boolean booleanCrawlList = false; private static boolean booleanCrawlIntro = false; //简单介绍页採集线程数目 private static int crawlIntroThreadNum = 2; private static boolean booleanCrawlRead = false; //阅读页採集线程数目 private static int crawlReadThreadNum = 10; /** * @Author:lulei * @Description: 更新列表页採集 */ public void startCrawlList(){ if (booleanCrawlList) { return; } booleanCrawlList = true; ZonghengDb db = new ZonghengDb(); Listinfos = db.getCrawlListInfos(); if (infos == null) { return; } for (CrawlListInfo info : infos) { if (info.getUrl() == null || "".equals(info.getUrl())) { continue; } UpdateListThread thread = new UpdateListThread(info.getInfo(), info.getUrl(), info.getFrequency()); thread.start(); } } /** * @Author:lulei * @Description: 小说简单介绍页和章节列表页 */ public void startCrawlIntro() { if (booleanCrawlIntro) { return; } booleanCrawlIntro = true; for (int i = 0; i < crawlIntroThreadNum; i++) { IntroPageThread thread = new IntroPageThread("novel info thread" + i); thread.start(); } } /** * @Author:lulei * @Description: 小说阅读页 */ public void startCrawlRead() { if (booleanCrawlRead) { return; } booleanCrawlRead = true; for (int i = 0; i < crawlReadThreadNum; i++) { ReadPageThread thread = new ReadPageThread("novel read page" + i); thread.start(); } } public static void main(String[] args) { CrawStart start = new CrawStart(); start.startCrawlList(); start.startCrawlIntro(); start.startCrawlRead(); }}
执行结果
通过上面的这几个步骤,纵横小说的分布式採集程序已经完毕,以下就为大家展示一下採集后的数据库截图
写在最后在上面的线程实现中,有非常多的配置信息,比方说线程中的两个请求之间的间隔时间以及各类线程的数量,像这些信息我们都能够将其写到配置文件里,方便之后的改动(这里写到程序中是方便大家的理解,还请见谅)。
----------------------------------------------------------------------------------------------------
ps:近期发现其它站点可能会对博客转载,上面并没有源链接,如想查看很多其它关于 基于 请。或訪问网址http://blog.csdn.net/xiaojimanman/article/category/2841877 或 http://www.llwjy.com/blogtype/lucene.html-------------------------------------------------------------------------------------------------
小福利 ------------------------------------------------------------------------------------------------- 个人在极客学院上《Lucene案例开发》课程已经上线了(眼下上线到第二课)。欢迎大家吐槽~