相信园子里面的很多人和我一样喜欢阅读小说,下面是这几年用的比较多一点的阅读器,功能相对完整。
Windows本地程序/手机 1.http://www.mybook66.com/ 云帆小说阅读器 2.http://www.rrtxt.com/ 人人阅读 3.http://www.dushubao.com/ 读书宝 还有几个... 网站 1.http://www.sodu.net/ sodu 2.http://www.soshu.cc/ 搜书
在几年前正好写过一个简单的小说下载阅读器,当时基本上没有前期设计,想到什么就写什么,所以代码很乱(有C#和C++两个版本),并且有不少bug。
现在谈谈思路和设计,等找到工作后再放出来晒晒了,如果有时间,会逐渐演进到一个复杂的下载阅读器.
-------------------------------------------------
本文以及后续文章都以起点中文网的《盘龙》这部小说为例解释约定术语(红字部分),实际中,使用另外的链接(不定)来当例子。
网站主页: http://www.qidian.com 小说主页: http://www.qidian.com/Book/1017141.aspx 章节目录索引页: http://www.qidian.com/BookReader/1017141.aspx 章节页: http://www.qidian.com/BookReader/1017141,20361055.aspx
典型场景1(已知某小说网站地址,且知道小说的名称)
1.打开浏览器 ,输入某小说阅读网网站地址,打开网站主页 2.找到小说搜索框,输入小说名称,点击搜索,出现符合搜索条件的小说列表 3.选取某小说,点击链接,转到本部小说主页 一般一本小说的主页上有如下几个链接:点击阅读 下载阅读 加入书架 .... 这里我们只关心 点击阅读 4.打开点击阅读后,页面显示的是本部小说的所有章节目录 5.点击相应的章节链接,就是本章小说的内容 6.阅读本章
如果下次再看同一部小说呢?
将本次看的小说的索引页收藏到浏览器的收藏夹,下次直接点击此链接
简言之流程如下
浏览器-[网站地址]-搜索框-[小说名(关键字)]-小说列表-[小说链接]-小说主页-[点击阅读]-小说目录页-[章节索引]-章节内容
典型场景2(只知道小说名称)
1.打开浏览器 ,打开某搜索引擎,找到搜索框输入某小说名,点击搜索,出现符合搜索条件的小说网站列表 2.点击某小说网站链接,打开其网站 3.余下过程同典型场景1
浏览器-[搜索引擎地址]-搜索框-[小说名(关键字)]-小说网站列表-[小说网站链接]-典型场景1
余下的几个场景用时序图代替,请自己考虑,什么情况对应哪个场景,可能有遗漏,请@我补充。
下面给出多个场景的时序图,为了减少上传图片,我将多个场景的时序图都画在一张图里面,对应都有标记。
普通的网页式阅读方式,需要如下几个要素
1)小说名 2)网站地址或者搜索引擎地址 3)浏览器
缺点很明显
1)操作步骤繁多,并且大都相似 2)读者是主动去寻找打开小说,在打开索引页之前,读者无法知道小说是否更新,也就无法及时看到更新的小说
3)网站的广告很多,阅读章节页面,干扰多
然而分析流程发现,真正看一部小说只需要如下两步:
1)通过小说名找到小说章节目录索引页,并打开 2)找到索引页上要看的章节,点击打开
小说下载阅读器的基本功能:
1)通过输入小说名,可获取对应小说的主页或者章节索引页 2)点击章节索引,可获取小说内容 3)可显示小说章节内容,以供阅读
高级功能:
4)当程序打开状态,收藏夹中的小说更新后,可以及时通知读者
下面仔细分析功能
功能1:从通用搜索引擎或者网站搜索得到小说的章节目录索引页面
根据场景1和2的综合来看,首先需要用到通用搜索引擎,例如Google或者Baidu,其次需要用到小说网站站内部的搜索引擎
为了简化最初的实现过程,我的demo程序里面省略了功能1的实现,这一步放在后面帖子来进行详细讨论
--三种情况 通用搜索引擎-[小说名]-小说网站-[小说名]-小说索引页 通用搜索引擎-[小说名]-小说索引页 小说网站-[小说名]-小说索引页
功能2:通过章节目录索引页面获取各章节内容
这一步主要是根据小说章节索引网址,获取此网页内容,然后取得各个章节对应的网址,再通过章节索引获取章节内容。
功能3:显示章节内容
场景1和2都没有提到显示问题,因为在浏览器中打开最终章节内容的时候,就是html的方式,浏览器本身就是html的阅读器,所以不需要关心此问题,但是如果以程序的方式获取小说章节内容,得到的是html的文本,这个文本不仅包含章节内容,还有html的一些标记和其它垃圾信息,这些垃圾信息必须过滤掉,在demo程序中,并未实现对文本内容的浏览功能。而采用其它方式替代解决。
功能4:对章节目录索引页面的收藏
这个很简单,直接放到数据库或者文本文件中
demo程序中,上述四个功能中,功能2全部实现,功能3采用另外方式折中实现
public class Novel { ///从给定的目录索引URL获取html ///参数indexPageUrl:目录索引页面对应的网址 ///返回值:URL对应网址的html public string GetIndexPageSource(string indexPageUrl); ///从给定页面html的文本中提取章节索引链接 ///参数indexPageSource:页面html的文本 ///返回值:本html包含的章节索引URL集合 public IList<string> GetIndexListFromIndexPage(string indexPageSource); ///从给定的章节索引URL获取html ///参数chapterPageUrl:章节索引页面对应的网址 ///返回值:章节URL对应网址的html public string GetChapterPageSource(string chapterPageUrl); ///给定章节内容,将小说内容从html中提取出来 ///参数chapterPageSource:页面html的文本 ///参数filter:过滤过滤条件,参数不一定是这个 ///返回值:txt文本,且是人直接可阅读的 public string GetChapterPageByFilter(string chapterPageSource,IList<string> filter); //<chapter index="第一章 天龙八部"> // 章节内容 //<chapter/> public string ConvertChapterPageToXML(string chapterPage,string index); //<?xml version="1.0" encoding="UTF-8"?> //<?xml-stylesheet type="text/xsl" href="novel.xsl"?> //<novel> // <chapter index="第一章 降龙有悔">章节内容</chapter> // <chapter index="第二章 飞龙在天">章节内容</chapter> // <chapter index="第三章 天龙八部">章节内容</chapter> //</novel> public string ConvertChapterPageListToXML(IList<string> chapterPageXML,string novelName); }
看接口可以知道大概的实现方式
1)功能2,就是利用http直接获取索引页面,提取页面的章节索引url,然后获取章节页面,再根据过滤条件提取出章节内容 2)功能3,将每章内容和章节名转成一个xml片段,最后将这些片段组合成一个完整的xml文件,然后写一个novel.xsl文件,这样就可以利用浏览器来直接查看此novel.xml小说,不需要自己再实现一个浏览器。当然这么做不仅仅是为了偷懒,更重要的是xml这种格式主要是用来做数据交换,通用性比较好,其次,此程序也可以自己内建一个嵌入式的web服务器,可以以web服务的形式,提供已经整理好的小说,供局域网的人阅读。
在实现过程中,有几个稍微麻烦一点的地方
1.索引页面->章节索引url列表
1)大家可以随便找一个小说的索引页看看,就知道索引页面除了包含多个章节索引链接外,还包括其它一些与小说章节无关的链接,所以进行此步的时候,要加上过滤条件对链接进行过滤。
2)过滤方式有两种,一种是通过html文件中的特定标记,直接过滤出包含所有章节链接所在的最小文本部分,然后从这一部分,提取href链接;第二种是先提取html文件中的所有href,然后观察章节链接的共性作为过滤条件,过滤出章节链接。我采用的是第二种方式。
如下例子就是一个索引页面html的一部分,示例中只包含章节索引,真实环境中还包含其它无关链接,里面的<a>元素是我们需要的
其它部分......
<div class="book_article_texttable"> <div class="book_article_listtext"> <dl id="chapterlist"> <dd><a href="5894704.html">写在新书之前~</a></dd> <dd><a href="5894705.html">第一章 林动【新书开张,郑重的求收藏!】</a></dd> <dd><a href="5894706.html">第二章 通背拳</a></dd> </dl> <div class="clear"></div> </div> <div class="book_article_listtext"> <dl id="chapterlist"> <dd><a href="5894707.html">第三章 古怪的石池</a></dd> <dd><a href="5894708.html">第四章 石池之秘</a></dd> <dd><a href="5894710.html">第五章 神秘石符</a></dd> </dl> <div class="clear"></div> </div> </div>
其它部分......
2)通常小说作者每天都会上传新的小说章节,对应的就是章节索引会逐渐增多,实际在读者追书的时候,基本上都是看新增的章节 也就是说,除了首次阅读此书的时候可能需要下载所有章节,后面的阅读都只需要下载新增的章节,既然有前后索引章节的比较,那么章节索引url列表就需要能够缓存起来,当章节索引url列表缓存后,可以通过一个定时器,定时去抓取章节索引页面,获取新的章节索引url列表,然后对比两次的列表,就可以知道哪些是新增章节,并通知给读者,当然这种做法,需要设计好定时间隔,不然对网站造成压力。
大大减小下载的章节数量 可以实现新章节提醒功能 对网站有一定压力
定时抓取索引页面时,如何减小对服务器的访问压力?
一般我们访问web服务器的页面,都是采用get命令,此命令会将页面的头部(几百个字节)和正文(几十k字节以上)一起取回,如果定时器的时间间隔很小,那么服务器压力将会很大。
索引页面初期都是几k大小,随着章节增多,到后面一般都是上百k的大小
解决方法:
1)查看http协议可知,还有一个head命令可用,它主要是取回页面的头部,我们可以将头部的一些关于页面更改的字段缓存起来,新的头部和缓存的对比,有更改才使用get命令下载索引页面,这样处理,将大大减小通讯的内容长度,减轻服务器压力。不过也有不好的一面,就是有些页面是动态的或者web服务器并未允许此命令。
当然,网站更新小说索引页,也其实有一个固定的时间段的,可以酌情处理。
2)继续查看http协议得知,get命令支持获取某url对应页面的某一部分(这就是断点续传的基础),通过这种方法,除了首次下载索引页面需要get整个页面,后面的get命令只需要获取章节更改的那部分(最小可能只有百十个字节,需要精心设置条件),不幸的是这个也需要web服务器支持。
2.章节索引url->章节内容
1)获取章节内容的时候,是需要过滤来得到干净的内容
2)类似章节索引过滤机制,不过这里采用第一种过滤方式,通过html文件中的特定标记,直接过滤出包含所有章节内容所在的最小文本部分,一般这个特定标记是一对字符串或者正则表达式,记住一定是成对的(起始标记,结束标记)。
3)可惜的是最小文本部分,虽然包含章节正文,但是仍然存在很多html的标记,所以这里必须再次重复进行过滤动作,删除html的标记,一般这里做成一个过滤链的形式,可多次过滤,最终可形成干净的正文。
4)这里有一种特殊情况,就是正文并不是文本文件,而是一张或者多张图片形式的内容,这里的处理大部分和前面一样,不同处在于要保留<img>这个元素,所以过滤的时候要特别小心此种情况
原始的html片段(最小文本部分)
<div id="booktext"><!--go--><STRONG><FONT size=5><a href="/newmessage.php?tosys=1&title=《武动乾坤 第二十八章 奇门印,残篇》章节出错啦!&content=章节错误原因:" target="_blank"" style="color:red">章节错误/点此举报</a></FONT></STRONG><br><br> “奇门印,残篇…”<br /><br /> 林动怔怔的望着那木牌上的介绍,却是没有想到这三品武学,居然是残缺的。<br /><br /> “这套武学,据说是爷爷一次偶然间得到的,不过也正如上面所说,这只是一门武学的残篇,你若真是想要见识见识三品武学的话,还是换一种吧。”林霞凑上前来,道。<br /><br /> “光是残篇就能被爷爷放在这里,想必这奇门印应该很厉害吧?”林动若有所思的道。<br /><br /> “还行吧,不过学不全,那也没用,按照爷爷说,这残缺的奇门印,就算是炼成功,也只是三品武学的威力,甚至,恐怕还略有不及。”林霞笑道。<!--over--></div>
起始标记:<div id="booktext"><!--go-->,结束标记:<!--over--></div>
1)从html完本中通过起始和结束标记得出最小正文,就是上述例子所示。
过滤或替换html元素:
1)<div id="booktext"><!--go--> 和<!--over--></div> --->都替换成空字符串
2)<STRONG>(.*)</STRONG> ---> 替换成空字符串
3)<br /> --->替换成特殊标记,这里选用“;;”,换行标记必须保留,可以直接替换成\r\n(不用此方法是为了更好的扩展)
4)( )+ --->替换空字符串,这里的空格一般不需要保留的(段前空格),因为可以用;;来统一换成 换行和空格
...
n) ;{3,} --->替换成 ;;,暂时我用的这个,其实可以直接替换成:一个换行符\n加上四个空格,因为最终是xml转换成html显示的\n在html无效,所以暂时是用;;代替,也可以直接用<br/>
上述过滤和替换原则归纳为:如果不属于正文,则必须被过滤掉(替换为空),如果对最终的正文的分段和显示有影响,则需保留
一般来说,同一个网站的不同小说,起始结束标记是一样的,过滤或替换的html元素也是一样的,不同网站一般是不同的
所以设计的时候,针对网站,可以设计一套过滤标记,如果不放心,对小说也可以在设计一套过滤标记
经过上述过程,得到相对干净的正文
;;“奇门印,残篇…”;;林动怔怔的望着那木牌上的介绍,却是没有想到这三品武学,居然是残缺的。;;“这套武学,据说是爷爷一次偶然间得到的,不过也正如上面所说,这只是一门武学的残篇,你若真是想要见识见识三品武学的话,还是换一种吧。”林霞凑上前来,道。;;“光是残篇就能被爷爷放在这里,想必这奇门印应该很厉害吧?”林动若有所思的道。;;“还行吧,不过学不全,那也没用,按照爷爷说,这残缺的奇门印,就算是炼成功,也只是三品武学的威力,甚至,恐怕还略有不及。”林霞笑道。
将;;替换成换行符和四个空格之后,得到干净正文
“奇门印,残篇…”
林动怔怔的望着那木牌上的介绍,却是没有想到这三品武学,居然是残缺的。
“这套武学,据说是爷爷一次偶然间得到的,不过也正如上面所说,这只是一门武学的残篇,你若真是想要见识见识三品武学的话,还是换一种吧。”林霞凑上前来,道。
“光是残篇就能被爷爷放在这里,想必这奇门印应该很厉害吧?”林动若有所思的道。
“还行吧,不过学不全,那也没用,按照爷爷说,这残缺的奇门印,就算是炼成功,也只是三品武学的威力,甚至,恐怕还略有不及。”林霞笑道。
demo程序(工具集)功能
1.新增、修改、删除小说(索引页),根据已有索引页,下载章节内容,并缓存 2.针对网站可新增对应过滤器,以实现对章节名和章节内容进行过滤,得到干净的名称和内容,另外还有网站间共用的过滤器 3.新增时小说时,可自动关联已有的过滤器。 4.可设定次数和时间间隔,对某小说的更新进行监控,并自动更新。 5.提供格式化工具,可对某小说的缓存章节或者按目录进行格式化,每部小说格式化为一个xml格式,并提供对应的xslt文件,可通过浏览器浏览 6.提供格式转化工具,将xml格式转化成其他格式,暂时只提过xml转umd格式 7.内嵌一个web服务器,可供局域网阅读
C#有两个版本,主要区别在于数据库,一个是sqlite,一个是直接用二进制文件
C++ 只有二进制文件版