webmagic的爬虫基本架构,其设计可以让人只需要实现PageProcessor中的process方法,完成列表页、详情页相应逻辑即可。而很多网站也许就只有简单的这两种页面的逻辑,webmagic还提供了更简单的表达两个页面解析逻辑的使用方法:一:注解;二: 正则组合。
- 注解
注解是Java里面用得多但写的不多的,我本人也没有概念,趁此机会学习一下Java注解。这篇博文讲得比较好 https://www.cnblogs.com/Qian123/p/5256084.html。 个人理解注解的功用是,作者设计出一套自己的表达逻辑、用以取代一定的语言上的功能,从而达到代码简洁的目的。
本次源码都在webmagic-extension模块中,可以us.codecraft.webmagic.model开始,注解在annotation包中。
OOSpider主要成员是ModelPageProcessor,和 ModelPipeline ;都是处理注解的Model类,前者负责抓取时候解析,后者保存时候解析。ModelPageProcessor通过 addPageModel(Class clazz) ; ModelPipeline 则通过 PageModelPipeline<Class>两种方式关联和确定Model.class 。
public class GithubRepoTest {
@Test
public void test() {
OOSpider.create(Site.me().setSleepTime(0)
, new PageModelPipeline<GithubRepo>() {
@Override
public void process(GithubRepo o, Task task) {
assertThat(o.getStar()).isEqualTo(86);
assertThat(o.getFork()).isEqualTo(70);
}
}, GithubRepo.class).addUrl("https://github.com/code4craft/webmagic").setDownloader(new MockGithubDownloader()).test("https://github.com/code4craft/webmagic");
}
}
1、ModelPageProcessor
ModelPageProcessor存了个List<PageModelExtractor>;每个 注解类正是通过对应的一个 PageModelExtractor 来处理。核心方法void process(Page)时, 对每个PageModelExtractor, 根据它关联class的注解类型,
1.1 extractLinks抽取helpUrl,经page.addTargetRequest的下一次继续抓取,
1.2 由 PageModelExtractor.process(page) 抽取页面元素obj, 元素经page.put(class.getCanonicalName, obj) 存入ResultItems,待pipeline后续保存。
2、ModelPipeline,
OOSpider.modelPipeline 保存了一个 {class : PageModelPipeline<T>}的map ,即代码清单的蓝色内部类。ModelPipeline在process时,对map中的每个PageModelPipeline: 从ResultItems里先根据上面(一.2)存好的obj ,进行 PageModelPipeline.process(obj) 。(此处我好奇,ModelPipeline是实现Pipeline接口的,把ResultItems当一个中转map用。能否反过来实现ModelPipeline<T>,Pipeline只是一个T=ResultItems的特例 呢?)
而处理注解的核心还是PageModelExtractor,看它是怎么实现的。
3、PageModelExtractor
3.1 PageModelExtractor.init(class)中首先取类注解,添加对应规则;然后提取成员注解,存入List<FieldExtractor>。
3.2 Object process(Page) 中,根据初始化过的规则和成员,调用processSingle方法。processSingle里首先class实例化Object o = clazz.newInstance();操作FieldExtractor抽取page上的值value。 通过 setField(o, FieldExtractor, value)添加到o对象;本方法正是上文 (一.2) 的具体实现。
以上两步的实现反映了注解的设计,看起来要设计出让别人用起来方便的注解,还是需要一番功夫。
- 正则组合
webmagic还提供了一种复合pattern抓取方式: CompositePageProcessor,CompositePipeline组合;在us.codecraft.webmagic.handler 中包中7个类。
4.1 RequestMatcher 接口,核心 boolean match(Request page) 是否要处理请求request。
4.2 PatternRequestMatcher implements RequestMatcher, 实现了match方法:正则匹配 request.getUrl
4.3 SubPageProcessor extends RequestMatcher , 核心 processPage(Page page); 留给自定义
4.4 SubPipeline extends RequestMatcher , 核心 processResult(ResultItems resultItems, Task task); 留给自定义.
4.5 PatternProcessor extends PatternRequestMatcher implements SubPipeline, SubPageProcessor; 初始化正则串 ,留出4.3、4.4两个接口。
4.6 CompositePageProcessor implements PageProcessor。 主要结构List<SubPageProcessor>。已经实现了在void process(Page page) 时候,分别调用SubPageProcessor.processPage(page)
4.7 CompositePipeline implements Pipeline。主要结构List<SubPipeline>, 已经实现了在void process(ResultItems) 时候分别调用subPipeline.processResult(resultItems)
4.8 合在一起: us.codecraft.webmagic.example.PatternProcessorExample 。 实现只需要用正则来完成抽取任务的PatternProcessors 放入CompositePageProcessor 、CompositePipeline ,Spider.create方式启动即可。(这里一个PatternProcessor 对应一次完整的解析和保存,不能分开)
读完webmagic源码之后可以说webmagic是麻雀虽小五脏俱全,webmagic-extension里的代码富含的设计理念更多一些,但core下的代码的设计更有美感,毕竟是直接从scrapy模仿过来的。不论如何webmagic不长,对编程进阶来说都是值得一阅的好源码。