上个月看到一个学长在做斗鱼弹幕分析的开源项目 ,正好之前的计划已经完成了大半 ,所以决定借此机会学习一波爬虫和数据分析. 后面学长忙其他事去了 ,我也就开了一个新坑 ,从抓取分析B站的数据开始 :)
准备
爬虫
说到爬虫 ,就会想到Python ,边学边写我就直接上手了 .因为之前有接触过scala ,最近工作中也会写很多js (前端还很菜) ,所以语法掌握起来还不算太难 .不过我也没有一开始就写爬虫 ,先写了一个上传文件到FTP的脚本 ,用来上传我工作中的一些文件.
主要是练手 ,所以使用python成功抓取数据并放入DB后 ,还是用Java重构了那部分代码 ,准备后期做自动化程序
完成程序
- Python
- 上传文件到FTP
- 爬取Linux公社的技术书籍
- 抓取bilibili视频数据 (开始抓的页面 ,后来发现了几个api接口 ,直接返回的JSON)
- Java
- 使用webmagic抓取房天下的房屋数据 (代码已删除)
- 抓取bilibili接口数据
Python的第三方功能非常完善和强大 ,也有脚本语言简洁/直接的特点 ,用起来还是很不错的 .
在最近的工作中 ,我用Python生成带版本号的js文件 ,扫描并替换html中的script和css相对地址 ,实现前端文件的版本系统.
MongoDB
中间对数据分析部分的技术选型犹豫了一阵 ,开始想使用mysql ,使用ORM框架 ,然后就可以操作POJO类进行后面的处理 .
但是关系型数据库对形态不固定的数据支持度很低 (抓取的数据五花八门 ,就算通过接口抓 ,也要建表建字段 ,心累)
所以果断使用了MongoDB ,抓取的数据直接insert进去就行 ,不用管字段的数量和类型.
建议用IDEA的朋友安装Mongo Plugin ,启动mongod.exe后就可以在IDEA中查看数据了.
Hadoop
(大)数据分析 ,自然少不了Hadoop/Spark等 ,Java环境下自然首选Hadoop .学习的话最好搭建一个开发环境 ,不然每次都要把数据传到服务器上也是很麻烦的一件事 .
教你Windows平台安装配置Hadoop选用Hadoop 2.7.4 + hadooponwindows 就可以在Windows上单机运行Hadoop程序了 .
开始
自我提醒:
在搭建这个项目的时候 ,我就想要构造一个灵活的系统/要抽取/要继承/要抽象 ,反而把代码弄得很乱 ,结构很散 .在工作的时候(目前由我独立开发) ,因为有很强的目的性 ,我会优先完成功能 ,然后再考虑功能是否重复/可否抽象 .
TaskManager任务管理器
分发任务
public void router(String module, String taskName, List<Object> params) {
switch (module) {
case "crawler":
routerCrawler(taskName, params);
break;
case "analysis":
routerAnalysis(taskName, params);
break;
}
}
public void routerCrawler(String taskName, List<Object> params) {
...
}
public void routerAnalysis(String taskName, List<Object> params) {
...
}
执行任务并记录任务的状态
public void execute(String module, String taskName, List<Object> params) {
BasicDBObject searchQuery = new BasicDBObject()
.append("module", module)
.append("taskName", taskName)
.append("params", params);
log.debug("开始执行任务:\n{}", searchQuery.toString());
try (MongoCursor<Document> result = MongodbDao.find(TASK_RECORD, searchQuery)) {
if (result.hasNext()) {
Document taskRecord = result.next();
log.debug("查询到任务历史记录:\n{}", taskRecord.toString());
TaskStatusEnum taskStatus = TaskStatusEnum.valueOf(taskRecord.getString("taskStatus"));
if (taskStatus.equals(running)) {
throw new KnownException(taskRecord.toString() + "任务正在执行中 ,请勿重复执行");
} else if (taskStatus.equals(TaskStatusEnum.finished)) {
throw new KnownException(taskRecord.toString() + "任务已执行完毕 ,请到结果页面进行查看");
}
}
}
Instant start = now();
searchQuery.append("taskStatus", running);
searchQuery.append(