恩,萌新刚来,听学长说写博客可以总结梳理自己的知识,所以来试试,自娱自乐,不喜莫喷。目前还是大二狗,学Java半年多,错误很多,望大神指正。
本文涉及:Java多线程,单例模式,爬虫相关技术,MySQL,JDBC,SQL优化
刚学Java还没学sql时写过一个B站爬虫,但是由于自己临时学的sql速度太慢,爬取45万条数据用了四五个小时,速度太慢,最近sql与Java有些进步,优化了一下爬虫,没有再次爬太多视频数据(爬太多B站会封IP),恩,结果是6分钟8万条数据,还算满意,我把我的设计思路分享一下,供萌新们参考,大神轻点打脸
因为限制爬虫速度的第一是网速,第二是数据库,但是他们对硬件占用不冲突,所以我就打算把他们分到不同的线程中:
sql插入优化在另一篇文章中:
核心流程:
step1:爬取数据:因为从网上获取数据有很大的延迟,线程很长时间处于等待状态,对计算机压力不大,所以尽量多开几个线程,我一般开5个
step2:解析并暂存:不管多少个线程,爬取后把json把需要的数据解析成本地的数据类,全部存入一个队列中
step3:数据库写入:数据库只开一个线程,因为写入占用硬盘,限制条件是硬盘速度,多线程意义不大。扫描step2中的队列,如果不为空,从头部取出一个并从队列中移除,然后编入数据库批量写入命令中,达到预定的阈值后一次写入多条。
优点:爬去和写入分开,不会在写入时爬去处于等待状态,系统利用率高
缺点:系统资源占用高
核心算法确定完毕,具体操作如下:
首先用Fiddler4找到接口:
在不断地访问B站后找到了加载视频列表的API
然后访问不同的分区,观察规律,最后总结出获取不同分区视频列表的API函数
public static final int[] categories={22,26,126,127,157,158,164,159,71,137,131,24,25,47,27,33,32,153,51,152,28,31,30,59,29,54,130,
20,154,156,17,65,136,19,121,37,124,122,39,96,95,98,138,21,76,75,161,162,163,22,26,126,127,157,158,164,159,71,137,131};
public static String GetUrl(int categories,int pagenum){
return String.format("http://api.bilibili.com/archive_rank/getarchiverankbypartion?callback=?&type=jsonp&tid=%d&pn=%d&_=?",categories,pagenum);
}
categories对应不同的小区(例如鬼畜大区下的鬼畜调教小区对应22,我没发现大区的API,发现的同学请在留言中提到,万分感谢)
懒得找的同学可以用我找到的信息:
switch (category){ case 24:avType.setTypeName("动画--MAD·AMV");avType.setArea("动画");avType.setAreaNum(1);return avType; case 25:avType.setTypeName("动画--MD·3D");avType.setArea("动画");avType.setAreaNum(1);return avType; case 47:avType.setTypeName("动画--短片·手书·配音");avType.setArea("动画");avType.setAreaNum(1);return avType; case 27:avType.setTypeName("动画--综合");avType.setArea("动画");avType.setAreaNum(1);return avType; case 33:avType.setTypeName("番剧--连载动画");avType.setArea("番剧");avType.setAreaNum(2);return avType; case 32:avType.setTypeName("番剧--完结动画");avType.setArea("番剧");avType.setAreaNum(2);return avType; case 153:avType.setTypeName("番剧--国产动画");avType.setArea("番剧");avType.setAreaNum(2);return avType; case 51:avType.setTypeName("番剧--资讯");avType.setArea("番剧");avType.setAreaNum(2);return avType; case 152:avType.setTypeName("番剧--官方延伸");avType.setArea("番剧");avType.setAreaNum(2);return avType; case 28:avType.setTypeName("音乐--原创音乐");avType.setArea("音乐");avType.setAreaNum(3);return avType; case 31:avType.setTypeName("音乐--翻唱");avType.setArea("音乐");avType.setAreaNum(3);return avType;