概况:帮周同学做小说采集做了有一段时间了。一开始是从其它网站的页面上直接写正则去采集,然后慢慢的转为采集别人提供的API。
环境:CENTOS+NGINX+PHP5.2.17。基于JIEQI小说管理系统。
直接采集其它网站页面的时候,主要改的是JIEQI自带的采集系统,印象比较深的,是加上了判断章节顺序的功能,还修改了其它的“BUG”(呀,其它具体什么来着,我现在都快忘了,做了好久了)。这回感触比较深的是,是采集API。
背景:采集数十个小说站的API(目前有五个,预计有40,50个)
设计:共用一个显示页面,逻辑分开处理。可批量采集,可单篇采集。
上个图:其中,两个实体,是根据我需要的信息,自己定义的。为什么要规定这个实体(或者是接口),主要是因为,每个API给的信息都是不一样的,要统一后,才能操作。
单篇采集 VS 多篇采集
单篇采集相对比较简单,想怎么写都行,问题不大。
而多篇批量采集,这次写了四个版本。
V1:将所有的操作,写在了同一PHP进程里面。
优势:逻辑简单,容易实现。
缺点:PHP进程容易庞大,容易挂悼。
问题:最大只能设置5篇,而且,不能看到采集的过程。
V2:将所有的操作分开,用file_get_contents遍历访问。
优势:类似“异步”采集,将所有操作分开到每个进程,单进程不容易挂。效率很高。
缺点:采集过程将产生N多个PHP进程,NGINX会出现504等错误。
问题:如上述缺点,如果一作品的章节较多,在短时间内(0.1S或更短)产生上百个HTTP请求,NGINX出现问题,服务器吃不消。
改进:在PHP中加上sleep,导致NGINX不稳定,集中耗资源。偶尔还出现file_get_contents的错误。
V3:结合V1和V2,用JS做定时。
思路:使用iframe,定时刷新采集的每个页面(V2),根据页面返回信息,做下一步操作。即:循环设置iframe的SRC。
优势:将采集时集中对服务器的压力分散掉,章节按顺序来入库。
缺点:采集的间隔时间,不太好设置。哪怕是根据iframe的返回值,再判断,也要多加定时(采用父页面定时刷新,定时抓取iframe的数据来判断)。
问题:setTimeout出各种问题,会出现无法控制的情况。因为JS也是单线程的。setInertval也一样。
V4:结合前面三,主要改进是在V3的基础上,再次分开。
思路:不再循环设置iframe的SRC,而是,新建N多个iframe。
优势:可以很轻松的控制时间(即:间隔多少S,打开新的iframe)。
缺点:若前面的章节操作比较慢(即:比如说,第一章卡壳了,2S都还没有连接上采集的PHP的URL。而第二章,在第0.5S后,已经开始,且连接上了,那么第二章就会在第一章之前入库),这里就涉及到一个章节的顺序问题。还有,同上,第一章已经连接上了,但,操作特慢,2S才搞定;而第二章,字数少(或其它原因),1S就搞定了,问题同上。
问题:同上缺点所述,还有一个问题要注意。因为有些字段,必须要在采集完之后,更新表的。SO,采用了一个方法:就是子页JS,调用父页的JS的一个方法,在父页中设置一个iframe(ajax或script一样),访问修正作品的URL。
实用:果然,实用的时候,缺点所产生的问题已经出现了。
修正:将章节排序的字段,做到和章节信息一样,放到数组里面,同步更新。这样,哪怕第二章先入库,但它的order,还是2。第一章后入库,其order为1。在显示时,还是第一章在前面。问题解决。
每个采集站的API和模版都分开了,这样的好处就是统一了接口,其它的自由发挥。做这个玩意,也被周同学说了几次,不过想想,确实,一开始做的时候,没有考虑这么细,做得不够好,看来,还是经验不够啊。
当然,采集的话,建议使用.net做成一个EXE。有向周同学提,但他觉得更麻烦,也懒得重新来弄过。现在这个版本,够用了,符合要求了。还有优化的地方继续优化。
此抛砖引玉,期待大牛们的指点。