java并发小说爬虫,多站点搜索下载,并实现Android客户端开发

小说爬虫真的很简单,但要能优雅地使用却很麻烦。下面让我来诉说一下这几天的肝路历程。整个流程很完整,但不会很深入,主要是讲思路,给想要写类似功能的同学踩点坑,有什么细节问题直接评论就好。

[TOC]

先奉上项目Github,里面有实现代码以及jar和apk两种软件,几天课余时间肝出来的,有些想的不周到的地方还请见谅。

明确产品需求

  • 最基本的要求,能够搜索小说,然后点击小说进行阅读或者下载。
  • 同时,我们想要能够搜索到各个站点的小说,并且速度不能太慢。
  • Android客户端实现书籍收藏(追更),并自动检查是否有章节更新
  • 下载格式,我们不仅想要生成txt格式的书籍,还想要epub这种带目录图片的格式,最好还要能够支持mobi,然后直接导入kindle。
  • 下载速度不能像市面上普通的小说软件一章一章的下,否则速度太慢会影响体验,最好要能达到宽带的最大速度。
  • 该程序能有较好的可移植性,因为我们想要同时制作PC端和Android端的软件。

那么,开干!


总流程

流程

关于爬虫框架

在爬网页内容这部分,并没有用什么黑科技,只是普通的正则匹配爬虫。我用了自己的工具类,后来有些网页有些太复杂也引入了Jsoup负责解析html。这里默认大家都明白怎么解析html内容。

关于编码方式的坑

大多数人一想到编码方式,肯定是首选uft-8了。但是在小说网站里,我们需要首选gbk,因为很多小说的某些字符是没有包含在utf-8里的,会变成??常见的小说网站编码方式都是默认gbk。

因此在框架中需要保留一个方法设置编码方式,并且默认应为gbk。

如何实现搜索功能

这里我想到了两种方案。第一种是利用百度这些搜索引擎搜索小说,再对搜索引擎得到的结果进行解析;还有一种就是直接利用小说网站的搜索功能实现搜索。很明显,搜索引擎的优势是能够得到很多小说网站的搜索结果,但是这些网站是随机的,在没有足够多的解析前拿到结果也无能为力。第二种方式是使用小说网站内部的搜索功能,缺点是书籍不够全面,但是只要搜索到就必定能够解析,比起漫无目的的搜索能提高更多效率。而书籍不全的缺点可以通过解析更多网站来弥补。于是最终确定使用小说站内的搜索功能。

基本上每个小说网站都会提供搜索功能的。抓一下包,会发现,无非就是post请求了某个网址,请求内容就是书籍名字。于是可以通过网络请求拿到搜索结果,如下图的某趣阁。

搜索结果有很多附带属性,为了能够提供更好的使用体验,记得把小说名字,小说的目标url,最新章节名,作者,字数,最近更新时间都解析下来,以便后面使用。有的网站有图片链接,也可以解析下来,我个人觉得用处不是很大就没有解析。

以上,就实现了单个站点的搜索解析,按照同样的方式,我解析了十多个站点的搜索功能。在用户点击搜索时,使用并发的方式同时请求,把所有结果保存到集合里并展示给用户。并发的好处是只要网络状况良好,就能用搜索一个网站的时间获得十几个网站的结果,这在网站多了之后是必须的。至于如何使用并发,用最简单的线程池+CountdownLatch就好。

这里有个小技巧,很多小说站内搜索直接使用了百度站内,他们的解析方式是一样的,可以封装起来。只要是这样的网站,搜索代码一行就能解决。

如何解析小说

通过搜索功能我们拿到了小说目录的url,对这个url进行请求并解析出所有章节名和url即可得到这本小说的所有目录。这里单线程就行,多线程并不能提高多少效率。还是放一张某趣阁的截图吧。

拿到url以后,就可以得到章节内容的html了,同样解析出来,就拿到章节内容了。如下图,解析content所在的div即可。

关于解析小说的第二种方式

昨天看了一下owllook的实现方案,使用了匹配class或者id的方法,实现自动解析。说明白点就是传入目录所在的div的class或者id,通过某个算法自动解析章节名字和url,这样做的好处是解析网站特别简单。如上图的某趣阁是没问题的,因为这个网站还是比较规范的,div里就是目录或者小说内容。

但是一旦遇到比较流氓的,就不行了。例如下图,这个网站的目录div分成了三列,每一列是一个单独的div。如果按照自然顺序去解析,解析出来的内容自然就乱序了,而如果要靠第x章的顺序去重新排序显然不太现实。

因此,我并没有采用自动解析的逻辑,而是每个网站全部需要自己实现解析。这样虽然麻烦了点,但是能够保证所有网站都能正常解析,并且还能根据不同的网站删除广告之类的内容。同时,可以将比较常用的解析方法封装起来使用,也不会太繁琐。例如使用Jsoup的textNodes的功能,能够直接提取所有文字到一个集合里。

如何实现章节并发解析,失败无限重试

这个工具类还真是构思了很久,也写了很多个版本。

粗略一想,直接用线程池,再配合CountdownLatch不就行了?这样确实可以实现并发解析,但是如果某一章小说网络请求出错怎么办?错误的章节直接就跳过了,导致小说不完整。所以需要一个机制来实现失败重试。

我最开始的方案比较粗略,直接把错误的章节添加到一个并发集合里,等待所有章节都解析完毕后再重新解析所有错误的章节。这样在网络情况稳定下,基本没什么问题,但万一重试的章节又失败了呢?所以我想要一个能够失败无限重试直到成功的工具。

于是实现了一个下载队列,这个队列一边把任务添加到线程池,一边重新入队失败的任务,这样就能无限重试了。使用了锁实现这个队列,并且实现了进度监听。wait真香O(∩_∩)O。具体可以参考github上的engine->BookFu*ker类。

关于并发

  • 网络请求的并发线程一定不要设置高了,不然会返回504错误。使用okhttp默认配置即可。
  • 网页解析的线程池可以设置任意数量,因为okhttp维护了一个队列,只要使用同一个client,单个ip最多并发数量为5。增加解析的线程数量可以提高解析速度,是因为能够让html下载下来后更快被解析。玄学调参:电脑300,手机150。
  • 小说服务器是有限制的,比如某趣阁测试量大约2w章节,就被封ip了,只有等到第二天才能重新测试,所以千万别用校花的贴身高手什么的去测试=_=数量和速度应该都有影响吧,大家自行把握。我测试时2m/s的下载速度,断断续续大概30分钟不到。
  • 网络请求的超时设置。由于已经实现了失败重试机制,所以可以适当减少超时,以免某一章节一直卡着浪费资源。如果没有失败重试,必须设置长一点。

小说内容怎么保存

最开始我直接将每一章小说的内容储存为一个String类型,然后返回统一处理。这样做如果只是针对txt格式是没问题的,因为拿到所有小说章节后只需要简单按顺序合并起来就ok。

但是类似EPUB格式的小说,每一章其实是一个html文本,需要在每一行放在</p/>标签里,标题放在</h1/>里,还要统一配置css等。

于是,我们解析的小说内容最好按照每一行保存在一个集合里,这样能方便拓展小说保存格式。

关于小说保存格式

epub格式保存需要将所有章节转换成html,并生成章节目录的html,和epub格式的配置文件,最后压缩打包,生成.epub后缀的文件即可。

虽然知道原理,但是真的不想自己写,于是直接抄袭别人的代码。

在我github代码里的EpubWriter忘记从哪儿抄的了,这个工具类有点坑,不能在安卓平台上使用,Android版本我依赖了epublib,亲测可以使用。

至于mobi格式,目前我还没有找到直接生成的方法,最佳方法应该是下载kindlegen(外网)软件,用epub转mobi,效果很好。但是这个软件在不同操作系统需要下载不同版本,所以没法集成到软件中。

Android开发心得

java代码写的飞起,各种接口各种抽象,复制到安卓工程里才发现,有些接口使用还是不太方便,只能一遍一遍地重构了,现在只是把最基本的功能做出来了,以后慢慢说吧。

庞大集合的传递

很多小说一个网页就几百k的大小,解析后的目录集合能有几千的数量,千万别把这个集合传进intent里跳转activity。如果你的手机性能不错可能不会有bug,其他人手机可能会在跳转时闪退,而且没有报错!!!因此,对于目录这样的超大集合,可以用单例保存。

解析真的太慢了

电脑上解析一个8k+章节的小说只要1s不到,但即便是骁龙835处理器手机也要3~4秒。因此最好将解析结果保存起来,防止不小心退出后需要重新解析。

实现追更

其实实现非常的简单,只需要在每次用户打开App的时候,将追更列表里的书籍并发解析一遍目录,对比数据库里的最新目录即可。

自动更新以及bugly

这种才开始写的app真的很需要bugly来查看异常状况,并且实现自动更新功能,便于修复bug后的推送。后续可能使用热修复方案,毕竟小说网站解析可能会频繁出错。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,295评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,928评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,682评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,209评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,237评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,965评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,586评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,487评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,016评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,136评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,271评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,948评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,619评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,139评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,252评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,598评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,267评论 2 358

推荐阅读更多精彩内容

  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,739评论 2 59
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,224评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,678评论 18 139
  • 一向冷静沉着的她,竟然也会因这点小事再次哭泣 小时候是这样,现在依旧...
    贝纳多阅读 942评论 4 8
  • 懂孩子,是接受他的天赋、理解他行为背后的动因,并且给予积极正向引导。 生命数字是一个非常高效的工具,帮助我们读懂孩...
    雪飞老师阅读 1,144评论 0 3