转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/46812645

http://www.llwjy.com/blogdetail/9df464b20cca5405c7ce07e2fb2d768f.html

个人博客站已经上线了,网址 www.llwjy.com
~欢迎各位吐槽~

-------------------------------------------------------------------------------------------------

在前面的几篇博客中,我们已经介绍了怎样採集纵横小说站点上的信息以及怎样把这些信息持久化到数据库中。如今我们就開始介绍怎样做分布式採集,让各个模块之间能够完美的配合。

採集类改动

在開始介绍分布式採集之前。我们须要对之前介绍的採集类加入一些方法。也就是返回上一篇博客中介绍的小说javabean,详细源代码还请參照个人站点上的博客源代码

1.简单介绍页

简单介绍页需呀加入一个方法,让它返回简单介绍页的数据信息,详细例如以下:

  1. /**
  2. * @return
  3. * @Author:lulei
  4. * @Description: 分析简单介绍页。获取简单介绍页数据
  5. */
  6. public NovelIntroModel getNovelIntro() {
  7. NovelIntroModel bean = new NovelIntroModel();
  8. bean.setMd5Id(ParseMD5.parseStrToMd5L32(this.pageUrl));
  9. bean.setName(getName());
  10. bean.setAuthor(getAuthor());
  11. bean.setDescription(getDesc());
  12. bean.setType(getType());
  13. bean.setLastChapter(getLatestChapter());
  14. bean.setChapterlisturl(getChapterListUrl());
  15. bean.setWordCount(getWordCount());
  16. bean.setKeyWords(keyWords());
  17. return bean;
  18. }

2.阅读页

阅读页内相同须要加入一个方法。让它返回阅读页内的数据信息,详细例如以下:

  1. /**
  2. * @return
  3. * @Author:lulei
  4. * @Description: 分析阅读页。获取阅读页数据
  5. */
  6. public NovelReadModel getNovelRead(){
  7. NovelReadModel novel = new NovelReadModel();
  8. novel.setTitle(getTitle());
  9. novel.setWordCount(getWordCount());
  10. novel.setContent(getContent());
  11. return novel;
  12.  
  13. }

这些方法都是对之前类中的方法做一个整合。将之前分析到的数据组装成一个javabean返回,方便后面的操作。

各页採集线程类

在实现分布式採集的时候,就须要编写各个页面的採集线程类。让他来控制各页面的採集业务。以下我们就一一介绍:

1.更新列表页线程

这个线程的主要功能就是监控更新列表页的数据。提取页面上的简单介绍页URL,觉得它们是有更新的页面,将相应的信息持久化到数据库中,详细实现例如以下:

  1. /**
  2. *@Description: 更新列表页线程
  3. */
  4. package com.lulei.crawl.novel.zongheng;
  5.  
  6. import java.util.List;
  7. import java.util.concurrent.TimeUnit;
  8.  
  9. import com.lulei.db.novel.zongheng.ZonghengDb;
  10.  
  11. public class UpdateListThread extends Thread{
  12. private boolean flag = false;
  13. private String url;//抓取的更新列表页URL
  14. private int frequency;//採集频率
  15.  
  16. public UpdateListThread(String name, String url, int frequency){
  17. super(name);
  18. this.url = url;
  19. this.frequency = frequency;
  20. }
  21.  
  22. @Override
  23. public void run() {
  24. flag = true;
  25. ZonghengDb db = new ZonghengDb();
  26. while (flag){
  27. try {
  28. UpdateList updateList = new UpdateList(url);
  29. List<String> urls = updateList.getPageUrls(true);
  30. db.saveInfoUrls(urls);
  31. TimeUnit.SECONDS.sleep(frequency);
  32. } catch (Exception e) {
  33. // TODO Auto-generated catch block
  34. e.printStackTrace();
  35. }
  36. }
  37. super.run();
  38. }
  39.  
  40. public static void main(String[] args) {
  41. // TODO Auto-generated method stub
  42. UpdateListThread thread = new UpdateListThread("llist", "http://book.zongheng.com/store/c0/c0/b9/u0/p1/v0/s9/t0/ALL.html", 60);
  43. thread.start();
  44.  
  45. }
  46.  
  47. }

2.简单介绍页&章节列表页线程类

因为一个简单介绍页就相应一个章节列表页,所以我们就把这两个线程合为一个线程,让事实上现小说简单介绍信息的採集以及小说章节列表信息的採集,详细实现例如以下:

  1. /**
  2. *@Description: 小说简单介绍信息线程
  3. */
  4. package com.lulei.crawl.novel.zongheng;
  5.  
  6. import java.util.List;
  7. import java.util.concurrent.TimeUnit;
  8.  
  9. import com.lulei.crawl.novel.zongheng.model.NovelIntroModel;
  10. import com.lulei.db.novel.zongheng.ZonghengDb;
  11.  
  12. public class IntroPageThread extends Thread {
  13. private boolean flag = false;
  14.  
  15. public IntroPageThread(String name) {
  16. super(name);
  17. }
  18.  
  19. @Override
  20. public void run() {
  21. flag = true;
  22. try {
  23. ZonghengDb db = new ZonghengDb();
  24. while (flag) {
  25. //随机获取一个待採集的简单介绍页url
  26. String url = db.getRandIntroPageUrl(1);
  27. if (url != null) {
  28. IntroPage intro = new IntroPage(url);
  29. NovelIntroModel bean = intro.getNovelIntro();
  30. //採集小说章节列表页信息
  31. ChapterPage chapterPage = new ChapterPage(bean.getChapterlisturl());
  32. List<String[]> chapters = chapterPage.getChaptersInfo();
  33. bean.setChapterCount(chapters == null ?
  34.  
  35. 0 : chapters.size());
  36. //更新小说简单介绍信息
  37. db.updateInfo(bean);
  38. //插入待採集的章节列表
  39. db.saveChapters(chapters);
  40. //假设本次有待採集的资源。睡眠一个时间。没有待採集的资源。睡眠还有一个时间
  41. TimeUnit.MILLISECONDS.sleep(500);
  42. }else {
  43. TimeUnit.MILLISECONDS.sleep(1000);
  44. }
  45. }
  46. } catch (Exception e) {
  47. e.printStackTrace();
  48. }
  49. }
  50.  
  51. public static void main(String[] args) {
  52. // TODO Auto-generated method stub
  53. IntroPageThread thread = new IntroPageThread("novelinfo");
  54. thread.start();
  55. }
  56.  
  57. }

3.阅读页线程

这个线程的主要功能就是将小说阅读页的信息採集并持久化到数据库中。详细例如以下:

  1. /**
  2. *@Description: 小说阅读页线程
  3. */
  4. package com.lulei.crawl.novel.zongheng;
  5.  
  6. import java.util.concurrent.TimeUnit;
  7.  
  8. import com.lulei.crawl.novel.zongheng.model.NovelChapterModel;
  9. import com.lulei.crawl.novel.zongheng.model.NovelReadModel;
  10. import com.lulei.db.novel.zongheng.ZonghengDb;
  11. import com.lulei.util.ParseMD5;
  12.  
  13. public class ReadPageThread extends Thread {
  14. private boolean flag = false;
  15. public ReadPageThread(String name) {
  16. super(name);
  17. }
  18.  
  19. @Override
  20. public void run() {
  21. flag = true;
  22. ZonghengDb db = new ZonghengDb();
  23. while (flag) {
  24. try {
  25. //随机获取待採集的阅读页
  26. NovelChapterModel chapter = db.getRandReadPageUrl(1);
  27. if (chapter != null) {
  28. ReadPage read = new ReadPage(chapter.getUrl());
  29. NovelReadModel novel = read.getNovelRead();
  30. if (novel == null) {
  31. continue;
  32. }
  33. novel.setChapterId(chapter.getChapterId());
  34. novel.setTime(chapter.getTime());
  35. novel.setUrl(chapter.getUrl());
  36. //保存阅读页信息
  37. db.saveNovelRead(novel);
  38. //将状态改动为不须要採集
  39. db.updateChapterState(ParseMD5.parseStrToMd5L32(novel.getUrl()), 0);
  40. //假设本次有待採集的资源,睡眠一个时间,没有待採集的资源,睡眠还有一个时间
  41. TimeUnit.MILLISECONDS.sleep(500);
  42. } else {
  43. TimeUnit.MILLISECONDS.sleep(1000);
  44. }
  45. } catch(Exception e){
  46. e.printStackTrace();
  47. }
  48. }
  49. }
  50.  
  51. public static void main(String[] args) {
  52. ReadPageThread thread = new ReadPageThread("novel read page");
  53. thread.start();
  54. }
  55.  
  56. }

分布式採集

上面已经介绍完了各个线程完毕的工作,以下就须要一个类来控制管理这些线程。让其执行起来,详细代码例如以下:

  1. /**
  2. *@Description:
  3. */
  4. package com.lulei.crawl.novel.zongheng;
  5.  
  6. import java.util.List;
  7.  
  8. import com.lulei.crawl.novel.zongheng.model.CrawlListInfo;
  9. import com.lulei.db.novel.zongheng.ZonghengDb;
  10.  
  11. public class CrawStart {
  12. private static boolean booleanCrawlList = false;
  13. private static boolean booleanCrawlIntro = false;
  14. //简单介绍页採集线程数目
  15. private static int crawlIntroThreadNum = 2;
  16. private static boolean booleanCrawlRead = false;
  17. //阅读页採集线程数目
  18. private static int crawlReadThreadNum = 10;
  19.  
  20. /**
  21. * @Author:lulei
  22. * @Description: 更新列表页採集
  23. */
  24. public void startCrawlList(){
  25. if (booleanCrawlList) {
  26. return;
  27. }
  28. booleanCrawlList = true;
  29. ZonghengDb db = new ZonghengDb();
  30. List<CrawlListInfo> infos = db.getCrawlListInfos();
  31. if (infos == null) {
  32. return;
  33. }
  34. for (CrawlListInfo info : infos) {
  35. if (info.getUrl() == null || "".equals(info.getUrl())) {
  36. continue;
  37. }
  38. UpdateListThread thread = new UpdateListThread(info.getInfo(), info.getUrl(), info.getFrequency());
  39. thread.start();
  40. }
  41. }
  42.  
  43. /**
  44. * @Author:lulei
  45. * @Description: 小说简单介绍页和章节列表页
  46. */
  47. public void startCrawlIntro() {
  48. if (booleanCrawlIntro) {
  49. return;
  50. }
  51. booleanCrawlIntro = true;
  52. for (int i = 0; i < crawlIntroThreadNum; i++) {
  53. IntroPageThread thread = new IntroPageThread("novel info thread" + i);
  54. thread.start();
  55. }
  56. }
  57.  
  58. /**
  59. * @Author:lulei
  60. * @Description: 小说阅读页
  61. */
  62. public void startCrawlRead() {
  63. if (booleanCrawlRead) {
  64. return;
  65. }
  66. booleanCrawlRead = true;
  67. for (int i = 0; i < crawlReadThreadNum; i++) {
  68. ReadPageThread thread = new ReadPageThread("novel read page" + i);
  69. thread.start();
  70. }
  71. }
  72.  
  73. public static void main(String[] args) {
  74. CrawStart start = new CrawStart();
  75. start.startCrawlList();
  76. start.startCrawlIntro();
  77. start.startCrawlRead();
  78. }
  79.  
  80. }

执行结果

通过上面的这几个步骤,纵横小说的分布式採集程序已经完毕,以下就为大家展示一下採集后的数据库截图

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

写在最后

在上面的线程实现中,有非常多的配置信息,比方说线程中的两个请求之间的间隔时间以及各类线程的数量,像这些信息我们都能够将其写到配置文件里,方便之后的改动(这里写到程序中是方便大家的理解,还请见谅)。

----------------------------------------------------------------------------------------------------

ps:近期发现其它站点可能会对博客转载,上面并没有源链接,如想查看很多其它关于 基于lucene的案例开发点击这里。或訪问网址http://blog.csdn.net/xiaojimanman/article/category/2841877
或 http://www.llwjy.com/blogtype/lucene.html

-------------------------------------------------------------------------------------------------

小福利

-------------------------------------------------------------------------------------------------

      个人在极客学院上《Lucene案例开发》课程已经上线了(眼下上线到第二课)。欢迎大家吐槽~

第一课:Lucene概述

第二课:Lucene 经常使用功能介绍

基于lucene的案例开发:纵横小说分布式採集的更多相关文章

  1. 基于lucene的案例开发:查询语句创建PackQuery

    转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/44656141 http://www.llwjy.com/blogdetail/1 ...

  2. 基于zookeeper和强一致性复制实现MySQL分布式数据库集群

    http://qikan.cqvip.com/article/detail.aspx?id=667750898&from=zk_search

  3. 基于AgileEAS.NET企业应用开发平台的分布式解决方案

    开篇 分布式应用 AgileEAS.NET基于Microsoft .Net构件技术而构建,Microsoft .Net最吸引人的莫过于分布式应用技术,基已经提供了XML WebService. .Ne ...

  4. Apache Solr采用Java开发、基于Lucene的全文搜索服务器

    http://docs.spring.io/spring-data/solr/ 首先介绍一下solr: Apache Solr (读音: SOLer) 是一个开源.高性能.采用Java开发.基于Luc ...

  5. 基于JWT的Token开发案例

    代码地址如下:http://www.demodashi.com/demo/12531.html 0.准备工作 0-1运行环境 jdk1.8 maven 一个能支持以上两者的代码编辑器,作者使用的是ID ...

  6. [.NET领域驱动设计实战系列]专题八:DDD案例:网上书店分布式消息队列和分布式缓存的实现

    一.引言 在上一专题中,商家发货和用户确认收货功能引入了消息队列来实现的,引入消息队列的好处可以保证消息的顺序处理,并且具有良好的可扩展性.但是上一专题消息队列是基于内存中队列对象来实现,这样实现有一 ...

  7. 8 个基于 Lucene 的开源搜索引擎推荐

    Lucene是一种功能强大且被广泛使用的搜索引擎,以下列出了8种基于Lucene的搜索引擎,你可以想象它们有多么强大. 1. Apache Solr Solr 是一个高性能,采用Java5开发,基于L ...

  8. WebGIS中兴趣点简单查询、基于Lucene分词查询的设计和实现

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.前言 兴趣点查询是指:输入框中输入地名.人名等查询信息后,地图上可 ...

  9. Lucene/Solr搜索引擎开发笔记 - 第1章 Solr安装与部署(Jetty篇)

    一.为何开博客写<Lucene/Solr搜索引擎开发笔记> 本人毕业于2011年,2011-2014的三年时间里,在深圳前50强企业工作,从事工业控制领域的机器视觉方向,主要使用语言为C/ ...

随机推荐

  1. Node.js:NPM 使用介绍

    ylbtech-Node.js:NPM 使用介绍 1.返回顶部 1. NPM 使用介绍 NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题,常见的使用场景有以下几种: ...

  2. day63-webservice 11.cxf整合spring

    如果我们有Spring配置文件,怎么把WebService整合到Spring里面,用Ioc容器来管理这个Bean. 做项目的时候一般都是分层:Dao层.Service层.Service层要调Dao层, ...

  3. position记录元素原始位置

    position记录元素原始位置 css样式: li { width: 100px; height: 100px; margin: 10px 0 0 10px; background-color: # ...

  4. 数据科学的完整学习路径(Python版)

    转载自:http://python.jobbole.com/80981/ 英文(原文)连接:https://www.analyticsvidhya.com/learning-paths-data-sc ...

  5. CMD-echo

    echo 打印 <> echo ^< echo ^> echo 换行 echo 你好@echo.世界. echo 多行打印 > log.log 此时 > 无效.(我 ...

  6. Dapper中数据表的字段(列)与实体属性不一致时,如何手动配置它们之间的映射?

    NET[C#]Dapper中数据表的字段(列)与实体属性不一致时,如何手动配置它们之间的映射? 问题描述 比如有如下的数据表结构:Person: person_id int first_name va ...

  7. Educational Codeforces Round 33

    # Who = Penalty * A B C D E F 479 arkethos 4 247   + 00:08 + 00:19 +1 00:59 +2 01:41     479  ne-leo ...

  8. python课程设计笔记(五) ----Resuests+BeautifulSoup (爬虫入门)

    官方参考文档(中文版): requests:http://docs.python-requests.org/zh_CN/latest/user/quickstart.html beautifulsou ...

  9. MindManager 2019新版上市 ,了解一下!

    所有的等待都是值得的!MindManager在蓄力一年后,给各位思维导图爱好者带来了全新的MindManager 2019 for Windows.全新的版本包含英语.德语.法语.俄语.中文.日语,新 ...

  10. Project Euler 18 Maximum path sum I( DP and 记忆化搜索 )

    题意:求从三角形顶端出发到达底部,所能够得到的最大路径和 方法一:记忆化搜索 /************************************************************ ...