Hook 方法
这些方法的工作原理类似于钩子(hooks)。实现这些方法可以让你在从初始化到对象销毁的不同阶段控制爬虫工具的运行。
除了 parse 之外,所有方法的实现都是可选的。
async parse(set, results)
parse 方法实现了处理查询和获取数据抓取结果的核心逻辑,传入的参数包括:
set- 包含查询信息的对象:set.query- 查询的文本字符串set.lvl- 查询层级,默认为0
results- 包含结果的对象,需要在parse()方法中填充并返回:- 爬虫工具应检查 results 对象中是否存在每个键,并仅在存在时才填充它,从而优化速度并仅抓取用于生成结果的数据
results包含所需的 flat 变量键,其值为none(默认表示未获取结果),以及数组变量(arrays)键,其值为空数组,已准备好进行填充- 处理查询成功时,
results.success应设置为1;默认值为0,表示查询处理出错
让我们看一个例子:
class JS_HTML_Tags extends BaseParser {
static defaultConf = {
results: {
flat: [
['title', 'Title'],
],
arrays: {
h2: ['H2 Headers List', [
['header', 'Header'],
]],
}
},
...
};
async parse(set, results) {
// 获取在查询中传递的地址的 HTML 页面内容
const {success, data, headers} = await this.request('GET', set.query);
// 检查成功与否及 data 的类型,在正确处理 HTML 页面时我们应该得到 'string' 类型,否则 A-Parser 会返回 Buffer 类型的对象
if (success && typeof data == 'string') {
let matches;
// 检查是否需要采集 title 并保存值
if (results.title && matches = data.match(/<title[^>]*>(.*?)<\/title>/))
results.title = matches[1];
// 检查是否需要采集 h2
if (results.h2) {
let count = 0;
const re = /<h2[^>]*>(.*?)<\/h2>/g;
while(matches = re.exec(data)) {
// 在循环中保存所有找到的 h2 标签
results.h2.push(matches[1]);
}
}
// 通知数据抓取成功
results.success = 1;
}
// 返回处理后的结果
return results;
}
};
请注意,你可以创建自己的函数和方法来更好地组织代码:
function Answer() {
return 42;
}
class JS_HTML_Tags extends BaseParser {
...
async parse(set, results) {
results = await this.doWork(set, results);
return results;
}
async doWork(set, results) {
results.answer = Answer();
return results;
}
};
async processConf?(conf)
此方法用于根据某些规则转换配置,例如在使用验证码时,我们始终需要使用会话:
async processConf(conf) {
if (conf.useCaptcha)
conf.useSessions = 1
}
async parse(set, results) {
if (conf.useSessions)
await this.login();
}
该方法的存在是因为 A-Parser 支持动态配置字段,并且在同一个任务框架内,配置可以具有不同的值,这种情况可能发生在以下两种场景中:
- 在配置字段中使用模板,例如在 User-Agent 字段中使用
[% tools.ua.random() %] - 在从一个爬虫工具调用另一个爬虫工具时使用
overrides,用于this.parser.request
processConf 方法在 init() 之前调用一次。对于上述情况,processConf 会在处理每个查询之前额外调用一次。
应用 processConf 的基本规则:
- 仅在配置转换对性能有影响时使用
- 请记住
init仅执行一次,而processConf可能会为每个查询执行,如果init依赖于变化的配置字段,则在这种情况下逻辑可能会被破坏(见下文)
async init?()
init 方法在初始化基础爬虫工具对象时调用一次,用于执行一次性操作:
- 启动浏览器
- 使用
this.sessionManager.init()方法初始化会话管理器 - 连接到数据库并在数据库中创建表
- 读取静态数据
- 等等
由于该方法仅调用一次,所有 init() 所依赖的配置字段都不能与配置字段模板或 this.parsers.request 调用中的 overrides 配合使用。
async destroy?()
destroy 方法在任务结束时调用一次,用于正确销毁打开的资源:
- 关闭浏览器
- 关闭数据库连接
- 等等
async threadInit?()
此方法在每个线程初始化时运行,每个线程都是基础爬虫工具对象的副本,具有其唯一的 this.threadId,该 ID 从 0 开始到 threads_count - 1 结束。
主要应用场景:
- 为每个线程创建浏览器页面(标签页)
async threadDestroy?()
在任务结束过程中线程终止时执行,用于释放为该线程分配的资源。
async afterResultsProcessor?(results)
该方法在结果处理(包括结果构造器、过滤和去重)之后执行。其主要应用场景是在应用用户自定义过滤器后,通过 this.query.add 方法将查询添加到队列中,爬虫工具
HTML::LinkExtractor 的跳转链接过滤 (followlinks) 功能就是以此方式实现的。