如何编写爬虫
# ChopperBot与爬虫
简介
如果说插件构成了ChopperBot这颗独特的星球,那么爬虫得到数据则是这颗星球的生命。没有生命的星球将是死气沉沉的,而没有爬虫的ChopperBot也将失去它的光泽 再此向各位开发者介绍如何在ChopperBot中编写一个规范的爬虫并在融合进入ChopperBot中。
如果你不知道什么是爬虫,或者如何编写,请参考下列文章:
# 编写ChopperBot爬虫
# 创建文件夹
+-- ChopperBot
| +-- 模块名
| | +-- core
| | | +-- creeper
| | | | +-- builder //爬虫文件构造器
| | | | +-- group //爬虫组
| | | | +-- loadconfig //爬虫参数文件(必要)
| | | | +-- loadtask //爬虫主体任务(必要)
| | | | +-- processor //后续任务处理(必要)
| +-- 模块名
2
3
4
5
6
7
8
9
10
在开始编写爬虫脚本前,请先确保你的模块下的core文件夹中有如上图的几个必要文件夹,如果已经拥有则可以开始进行爬虫脚本的编写了
# 创建LoadConfig
说明
LoadConfig是当前你要进行爬虫时使用的爬虫参数文件,它可以包含url,header,cookie等等信息,也是爬虫脚本启动的必备文件
- 创建一个LoadConfig文件,包含你爬虫需要的必备信息
- 添加@Creeper注解,将脚本文件交给CreeperCenter管理
@Creeper(creeperName = "豆瓣书籍top250",loadTask = DouBanLoadTask.class,creeperDescription = "爬豆瓣爬的",group="douban")
public class DouBanLoadConfig extends LoadConfig {
public DouBanLoadConfig() {
this.url = "https://book.douban.com/top250"; //爬虫url
//this.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36";
//this.Origin = "https://live.bilibili.com";
//this.Referer = "https://live.bilibili.com/";
}
}
2
3
4
5
6
7
8
9
LoadConfig的其他参数
String url;
String startTime;
String UserAgent;
String Origin;
String Referer;
Map<String,String> header; //头信息
Map<String,String> cookie; //Cookie
2
3
4
5
6
7
如果你需要更多参数可以继承LoadConfig,自行添加,例如下面代码:
public abstract class LoadVideoConfig extends LoadConfig {
protected String videoPath;
// 视频保存名称
protected String videoName;
protected int clarity;
public LoadVideoConfig(String videoPath, String videoName) {
this.videoPath = videoPath;
this.videoName = videoName;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 创建LoadTask
说明
LoadTask就是整个爬虫文件运行的核心,他负责读取LoadConfig中的数据,并进行爬虫!启动!是爬虫脚本启动的必备文件, 在ChopperBot也提供了多种LoadTask类型的编写,目前结合了WebMagic框架,之后会结合更多爬虫框架。
编写一个最简单的脚本
/**
* 网上随便找的个爬豆瓣的demo,需要导入jsoup,不清楚能不能运行
* 链接:https://www.w3cschool.cn/article/69979497.html
*/
public class DouBanLoadTask extends CommonLoadTask<ArrayList<Book>> {
public DouBanLoadTask(DouyuBanLoadConfig loadConfig) {
super(loadConfig);
}
@Override
public ArrayList<Book> start() {
try {
// 连接到URL,并获取网页的文档对象
Document doc = Jsoup.connect(loadConfig.getUrl()).get();
// 选择所有包含书籍信息的元素
Elements elements = doc.select("div.article > div.indent > table");
// 遍历每个元素
for (Element element : elements) {
// 提取书籍的标题
String title = element.select("div.pl2 > a").attr("title");
// 提取书籍的作者
String author = element.select("p.pl").text().split("/")[0];
// 提取书籍的评分
String rating = element.select("span.rating_nums").text();
// 提取书籍的简介
String summary = element.select("span.inq").text();
// 创建一个Book对象
Book book = new Book(title, author, rating, summary);
// 将Book对象添加到列表中
books.add(book);
}
} catch (IOException e) {
e.printStackTrace();
}
return books;
}
@Override
public void end() {
}
class Book{
private String title; // 标题
private String author; // 作者
private String rating; // 评分
private String summary; // 简介
// 书籍的构造方法
public Book(String title, String author, String rating, String summary) {
this.title = title;
this.author = author;
this.rating = rating;
this.summary = summary;
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# 其他的LoadTask
- CommonLoadTask: 无特殊需求无框架的LoadTask,适用于编写所有类型的爬虫脚本
- WebMagicLoadTask: 基于WebMagic框架开发的LoadTask,适用于快速开发多线程,深度广度等多种爬虫策略的LoadTask
- 创建Spider
SpiderFactory.buildSpider(平台名称,Processor,url)
- 获取最终结果
Object obj = getData(spider,url)
@Override public List<DouyuLive> start(){ List<DouyuLive> lives; Spider spider = SpiderFactory.buildSpider(ConstPool.PLATFORM.DOUYU.getName(), //平台名称 douyuHotLiveProcessor, //processor loadConfig.getUrl()); //url try { lives = getData(spider,loadConfig.getUrl()); }catch (Exception e){ fail(e); return null; } success(); return lives; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- WebSocketLoadTask: 基于Websocket开发的爬虫,适用于websocket回传的数据,例如:抖音直播弹幕,斗鱼直播弹幕等等..
- 重写
onOpen,onMessage,onClose,onError
- 如果需要在返回值前进行一些操作处理,可以重写
preHandler
但是要注意阻塞等待你的数据全部获取完毕。- 重写
handler
, 该方法负责处理数据并返回值
public class HelloLoadTask extends WebSocketLoadTask<String> {
public DouyuLiveBarrageLoadTask(HelloLoadConfig loadConfig) throws FileCacheException {
super(loadConfig);
}
@Override
public void preHandler(){
// try {
// client.closeBlocking();
// }catch (InterruptedException e){
//
// }
}
@Override
public String handler() {
return "hello world";
}
@Override
public void onOpen(ServerHandshake serverHandshake) {
//client.send("hello");
}
@Override
public void onMessage(String s) {}
@Override
public void onMessage(ByteBuffer buffer) {}
@Override
public void onClose(int i, String s, boolean b) {
//client.close()
}
@Override
public void onError(Exception e) {
//client.close();
//this.logger.error("Hello Error:{}",e.getMessage());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
AsyncLoadTask(不完善,不推荐使用): 异步的LoadTask,用于异步运行的爬虫脚本,得到的结果需要提供给result 至此你的爬虫脚本就编写完毕了,无需额外的学习,你可以当作写一个算法OR一个面向过程的程序,总之想要编写一个爬虫脚本很简单。除了创建几个必要的文件外和一行注释外,你只需要写下你的脚本代码即可
# 爬虫融入ChopperBot
说明
创建一个爬虫的脚本后,虽然它具备了爬虫的功能,但是它还是无法融入ChopperBot的世界中,如果想让更多插件能够方便自动的使用你的爬虫你需要进行一些操作
# 爬虫功能组
在ChopperBot中,一个功能可能会有着许多爬虫脚本,也有可能有许多版本的爬虫脚本,这些相同功能的爬虫脚本会被分到一个功能组中,系统将根据爬虫脚本是否启用以及优先级 来选择爬虫组中的脚本进行调用,方便开发者开发爬虫,版本更替。也方便使用者自行更改。
- 爬虫加入爬虫组很简单,只需要在注解上加上爬虫组的名字即可
@Creeper(creeperName = "斗鱼热门模块",
loadTask = DouBanLoadTask.class,
creeperDescription = "爬斗鱼热门模块",
priority = 10, //不必要,默认5
group="douyu_hot_module")
2
3
4
5
# 已有爬虫功能组
功能组名称 : {平台}_{功能组名称}
例:douyu_hot_module
功能组名称 | 已有平台 | 方法介绍 |
---|---|---|
hot_module | douyu,bilibili | 热门模块 |
hot_live | douyu,bilibili | 热门直播 |
live | douyu,bilibili | 直播爬虫 |
live_barrage | douyu,bilibili | 直播弹幕爬虫 |
live_check | douyu,bilibili | 开播检测吗,详细直播信息 |
# 创建功能组
如果需要自定义爬虫功能组,则按以下代码来即可,以下代码是定义了一个tiktok_live
爬虫功能组
@Component
public class TikTokLiveGroup extends AbstractCreeperGroup {
@Override
public String getPlatform() {
return "tiktok";
}
@Override
public String getFunctionName() {
return "live";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 爬虫LoadConfig构造器
为了其他插件能更加好的自动化使用您编写的爬虫,我们提供了爬虫构造器,如果你的爬虫LoadConfig需要提供一些构造参数,可以编写一个关于该爬虫的构造器,来让插件帮你自动构建。
@Component
public class BiliBiliLiveLoadConfigBuilder extends CommonLoadConfigBuilder<BilibiliLiveOnlineConfig> {
@Override
public BilibiliLiveOnlineConfig build(Object obj) {
if(obj instanceof BiliBiliLive){
String liveId = ((BiliBiliLive) obj).getLiveId();
String liver = ((BiliBiliLive) obj).getLiver();
String showTime = ((BiliBiliLive) obj).getShowTime();
String path = LiveModuleConstPool.getPlatformLiveSavePath(ConstPool.PLATFORM.BILIBILI);
BilibiliLiveOnlineConfig bilibiliLiveOnlineConfig = new BilibiliLiveOnlineConfig(liveId, path, LoadLiveConfig.fileName(liveId, liver,showTime), true);
bilibiliLiveOnlineConfig.setShowDownloadTable(true);
bilibiliLiveOnlineConfig.setLiverName(liver);
bilibiliLiveOnlineConfig.setRoomName(((BiliBiliLive) obj).getLiveName());
bilibiliLiveOnlineConfig.setStartTime(showTime);
return bilibiliLiveOnlineConfig;
}
return null;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 爬虫任务实时监控器
说明
为了更好的了解爬虫数据的爬取情况,我们提供了爬虫监控器,开启监控器后将启用ws实时在前端监控爬虫的下载情况,目前已有的监控器(视频下载监控器,弹幕监控器),如果需要编写新的监控器,需要自行实现前端监控组件。
# 使用监控器
请在自己编写的LoadTask类上添加 @Monitor(clazz = VideoTaskMonitor.class)
注解来进行监控
# VideoTaskMonitor
负责监控直播,录播,视频下载情况。
//从监控中心插件获取对应任务id的监控
PluginCheckAndDo.CheckAndDo((plugin)->{
VideoTaskMonitor monitor = ((MonitorCenter) plugin).getInitMonitor(taskId, VideoTaskMonitor.class);
if(monitor!=null){
monitor.addDownloadedBytes(bytes); //写入下载字节数
}
}, PluginName.TASK_MONITOR_PLUGIN);
2
3
4
5
6
7
# BarrageTaskMonitor
负责监控弹幕下载情况。
//从监控中心插件获取对应任务id的监控
PluginCheckAndDo.CheckAndDo(plugin -> {
BarrageTaskMonitor monitor = ((MonitorCenter) plugin).getInitMonitor(this.loadBarrageConfig.getTaskId(), BarrageTaskMonitor.class);
if(monitor!=null){
monitor.addBarrage(barrageNume); //写入下载弹幕数量
}
}, PluginName.TASK_MONITOR_PLUGIN);
2
3
4
5
6
7
# 自定义监控器
- 继承
CommonTaskMonitor
- 定义监控类型 `monitorType = "barrage";
- 重写run方法,使用
handler(websocket类)
返回下载数据。
public class BarrageTaskMonitor extends CommonTaskMonitor {
private int totalBarrage = 0;
private int preDownloadBarrage = 0;
public BarrageTaskMonitor() {
monitorType = "barrage";
}
public void addBarrage(int num){
preDownloadBarrage = num;
totalBarrage += num;
}
@Override
public void run() {
if(handler!=null){
handler.wrapperAndSend(
Map.of(
"taskId",getTaskId(),
"monitor",monitorType,
"pre",preDownloadBarrage,
"total",totalBarrage,
"type","barrage",
"useTime",timeConsuming(),
"time", LocalDateTime.now()
),getTaskId()
);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 爬虫运行中心(该版本不适应,待更新)
说明
如果你已经完成一个可以单独运行的爬虫脚本。你可以将你写好的爬虫脚本放入TaskCenter中来进行爬虫脚本的运行管理,监控,调度,以及本地存储,失败恢复等功能。
CommonPlugin plugin = InitPluginRegister.getPlugin(PluginName.TASK_CENTER_PLUGIN); //获取TaskCenter插件
ReptileRequest request = new ReptileRequest(new DouyuHotModuleConfig(), //需要调度爬虫方法的参数文件
(t)->{System.out.println("return val:"+t)} //爬虫任务完成后的callback方法
)
((TaskCenter)plugin).request(request);
2
3
4
5
6
7
以上代码分为以下几点:
TaskCenter
插件已经启动,并获取- 构建
ReptileRequest
- 放入需要爬虫方法的文件,文件要确保有对应的
LoadTask
- 编写
CallBack
方法,这个是在LoadTask
调用结束并返回值时执行的,t为返回的值,你可以自行处理返回的值 - 发送爬虫请求给
TaskCenter
运行
# 爬虫脚本库(该版本不适应,待更新)
当你需要跨模块调用或者快捷使用爬虫脚本时,你可以在CreeperManger
插件中查找你已经注解@Creeper
的爬虫脚本
CreeperManager manager = InitPluginRegister.getPlugin(PluginName.CREEPER_MANAGER_PLUGIN); //获取CreeperManager插件
//根据名字获取爬虫脚本
LoadTask task1 = manager.getLoadTask("豆瓣脚本")
//根据爬虫请求中的LoadConfig获得爬虫脚本
LoadTask task2 = manager.getLoadTask(ReptileRequest)
2
3
4
5
6