概述:WebMagic的结构分为Downloader
、PageProcessor
、Scheduler
、Pipeline
四大组件,并由Spider将它们彼此组织起来。这四大组件对应爬虫生命周期中的下载、处理、管理和持久化等功能。
webmagic 的启动类时Spider,启动时run()方法运行时,会进行初始化各种组件,然后循环运行,从scheduler中取request,包装成runnable,进行请求处理。这个流程中,Scheduler组件负责去重和装待处理的请求,Downloader组件负责下载请求结果包装成page类, pageProcessor组件负责处理page结果,并且可以从page中添加需要采集的另外url,用page.resultItems装处理的结果数据,给Pipeline组件处理结果。
具体使用请看官方文档http://webmagic.io/docs/zh/,或搜索相关案例,本篇主要是对webmagic的源码解析。
一、流程分析
1、主要方法
public void run() {
// 检查运行状态,并且compareAndSet成运行
checkRunningStat();
// 初始化组件
initComponent();
logger.info("Spider {} started!",getUUID());
// 当前线程非中断,运行状态持续运行。
while (!Thread.currentThread().isInterrupted() && stat.get() == STAT_RUNNING) {
final Request request = scheduler.poll(this);
if (request == null) {
if (threadPool.getThreadAlive() == 0 && exitWhenComplete) {
break;
}
// wait until new url added
waitNewUrl();
} else {
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
// 处理请求,下载page,pageProcess处理page,pipeline处理page
processRequest(request);
onSuccess(request);
} catch (Exception e) {
onError(request);
logger.error("process request " + request + " error", e);
} finally {
pageCount.incrementAndGet();
signalNewUrl();
}
}
});
}
}
// 设置状态为停止
stat.set(STAT_STOPPED);
if (destroyWhenExit) {
close();
}
logger.info("Spider {} closed! {} pages downloaded.", getUUID(), pageCount.get());
}
从上面可以看出,Spider就是协调各种组件执行,核心逻辑是从scheduler中取出request包装成任务交由线程池运行,几个主要的组件就是在这个流程中负责各自运行的职责。
2)初始化组件initComponent
protected void initComponent() {
if (downloader == null) { // 默认Downloader为HttpClientDownloader
this.downloader = new HttpClientDownloader();
}
if (pipelines.isEmpty()) { // 默认Pipeline为ConsolePipeline
pipelines.add(new ConsolePipeline());
}
downloader.setThread(threadNum);
if (threadPool == null || threadPool.isShutdown()) {
if (executorService != null && !executorService.isShutdown()) {
threadPool = new CountableThreadPool(threadNum, executorService);
} else {
threadPool = new CountableThreadPool(threadNum); // 线程池为CountableThreadPool包装
}
}
if (startRequests != null) { // 初始添加请求到scheduler, 默认scheduler为QueueScheduler
for (Request request : startRequests) {
addRequest(request);
}
startRequests.clear();
}
startTime = new Date();
}
几个核心组件的的初始实现downloader-HttpClientDownloader、pipelines-ConsolePipeline、scheduler-QueueScheduler pageProcessor-手动制定。
3、处理请求方法processRequest
private void processRequest(Request request) {
Page page = downloader.download(request, this); // 下载-即通过HttpClient下载页面,并且构建Page对象。
if (page.isDownloadSuccess()){ // 下载成功
onDownloadSuccess(request, page);
} else { // 失败循环
onDownloaderFail(request);
}
}
// 下载成功处理
private void onDownloadSuccess(Request request, Page page) {
if (site.getAcceptStatCode().contains(page.getStatusCode())){
pageProcessor.process(page); // 先用pageProcessor处理page
extractAndAddRequests(page, spawnUrl); // 提取添加的request
if (!page.getResultItems().isSkip()) { // 如果没有设置isSkip
for (Pipeline pipeline : pipelines) {
pipeline.process(page.getResultItems(), this); // 多个pipelines处理结果
}
}
}
sleep(site.getSleepTime());
return;
}
下载成功的处理逻辑,就是进过pageProcessor进行处理page,然后提取addTragetRequest添加到scheduler中,然后在判断是否设置了isSkip,如果没有,那么就用多个pipelines进行结果的处理。
///下载失败的处理
private void onDownloaderFail(Request request) {
if (site.getCycleRetryTimes() &#