今天有个网友在博客回帖,能不能用注解来写一个爬虫?想了想,因为Javaer总习惯结果有个对象Model(我在自己用的时候也是这样),ResultItems的key-value形式难免会有点麻烦,何不将抽取和Model合为一体呢?好了!现在爬osc博客只有这点代码了!而且这个对象本身是可以继续使用的!
@TargetUrl("http://my.oschina.net/flashsword/blog/*")
public class OschinaBlog {
@ExtractBy("//title")
private String title;
@ExtractBy(value = "div.BlogContent",type = Fetcher.Type.Css)
private String content;
@ExtractBy(value = "//div[@class=‘BlogTags‘]/a/text()", multi = true)
private List<String> tags;
}
这里这个TargetUrl有两个意思:一个是符合这个url的交由这个Page处理,另一个是符合这样的url都会被抓取。怎么样?是不是很巧妙?另外为了方便,这里使用了类似正则的通配符形式(其实最终会编译成正则,只不过把”.“转义成了”\.“)。
考虑到一些更复杂的情况,例如:一个页面可能对应多个实体类(列表页视图),后面又做了一些更新,比如下面是一段抽取oschina问答的所有回答的代码:
@TargetUrl("http://www.oschina.net/question/\\d+_\\d+*")
@HelpUrl("http://www.oschina.net/question/*")
@ExtractBy(value = "//ul[@class=‘list‘]/li[@class=‘Answer‘]", multi = true)
public class OschinaAnswer{
@ExtractBy("//img/@title")
private String user;
@ExtractBy("//div[@class=‘detail‘]")
private String content;
public static void main(String[] args) {
OOSpider.create(Site.me().addStartUrl("http://www.oschina.net/question/567527_120597"), OschinaAnswer.class).run();
}
}
咦?当自己写完这堆处理代码的时候,才发现webmagic完全变成了Spiderman的一个注解版本。再看了一下Spiderman的sample,人家还内置了一套表达式引擎!
没关系,回到设计的初衷上来。Spiderman的设计是一个All-in-one的框架,提倡不写代码;而webmagic则是一个easy to build-in的框架,目标就是用代码实现爬虫,只不过少写点代码。不支持表达式引擎?没关系!复杂的逻辑,咱们可以写代码嘛!于是很简单的定义了一个AfterExtractor接口,在抽取完后,会调用这个接口:
@TargetUrl("http://my.oschina.net/flashsword/blog/*")
public class OschinaBlog implements AfterExtractor {
@ExtractBy("//title")
private String title;
@ExtractBy(value = "div.BlogContent", type = ExtractBy.Type.Css)
private String content;
@ExtractBy(value = "//div[@class=‘BlogTags‘]/a/text()", multi = true)
private List<String> tags;
@Override
public void afterProcess(Page page) {
System.out.println("title:\t"+title);
System.out.println("content:\t"+content);
System.out.println("tags:\t" + tags);
}
public static void main(String[] args) {
OOSpider.create(Site.me().addStartUrl("http://my.oschina.net/flashsword/blog/145796"), OschinaBlog.class).run();
}
}
public void afterProcess(Page page) 这个函数能做PageProcessor所有能做的事情。我想这段代码也不用咱说明了吧。个人还是比较满意的。
使用这个方式,结合JFinal,轻易实现了结果持久化到数据库的任务,代码:http://www.oschina.net/code/snippet_190591_23456。
值得一提的是,以上代码都没有修改底层的核心模块划分,以前手写PageProcessor的方式依然是有效的。看来一个良好的前期规划是很有必要的!
代码目前在https://github.com/code4craft/webmagic annotation分支上,后续测试稳定后回合并到主干。