• 欢迎访问web前端中文站,JavaScript,CSS3,HTML5,web前端demo
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏web前端中文站吧

WebMagic 爬虫框架 注解用法

JAVA web前端中文站 3年前 (2017-04-24) 1797次浏览 已收录 0个评论

自从 java 注解出来之后,就无框架不注解。同样的 WebMagic 爬虫框架也支持注解的方式实现网页的抓取,今天为大家分享一下?WebMagic 爬虫的注解教程。

@TargetUrl 和@HelpUrl

HelpUrl/TargetUrl 是一个非常有效的爬虫开发模式,TargetUrl 是我们最终要抓取的 URL,最终想要的数据都来自这里;而 HelpUrl 则是为了发现这个最终 URL,我们需要访问的页面。几乎所有垂直爬虫的需求,都可以归结为对这两类 URL 的处理:

  • 对于博客页,HelpUrl 是列表页,TargetUrl 是文章页。
  • 对于论坛,HelpUrl 是帖子列表,TargetUrl 是帖子详情。
  • 对于电商网站,HelpUrl 是分类列表,TargetUrl 是商品详情。

下面的例子中,TargetUrl 是最终的项目页,而 HelpUrl 则是项目搜索页,它会展示所有项目的链接。

 @TargetUrl("https://github.com///w+///w+") 
 @HelpUrl("https://github.com///w+") 
 public class GithubRepo {     …… }

TargetUrl 中的自定义正则表达式

这里我们使用的是正则表达式来规定 URL 范围。可能细心的朋友,会知道.是正则表达式的保留字符,那么这里是不是写错了呢?其实是这里为了方便,WebMagic 自己定制的适合 URL 的正则表达式,主要由两点改动:

  • 将 URL 中常用的字符.默认做了转义,变成了/.
  • 将”*”替换成了”.*”,直接使用可表示通配符。

例如,https://github.com/*在这里是一个合法的表达式,它表示 https://github.com/下的所有 URL。

在 WebMagic 中,从 TargetUrl 页面得到的 URL,只要符合 TargetUrl 的格式,也是会被下载的。所以即使不指定 HelpUrl 也是可以的——例如某些博客页总会有“下一篇”链接,这种情况下无需指定 HelpUrl。

sourceRegion

TargetUrl 还支持定义 sourceRegion,这个参数是一个 XPath 表达式,指定了这个 URL 从哪里得到——不在 sourceRegion 的 URL 不会被抽取。

@ExtractBy

@ExtractBy 是一个用于抽取元素的注解,它描述了一种抽取规则。@ExtractBy 注解主要作用于字段,它表示“使用这个抽取规则,将抽取到的结果保存到这个字段中”。例如:

 @ExtractBy("//div[@id='readme']/text()") 
 private String readme;

这里”//div[@id=’readme’]/text()”是一个 XPath 表示的抽取规则,而抽取到的结果则会保存到 readme 字段中。

除了 XPath,还可以使用其他抽取方式来进行抽取,包括 CSS 选择器、正则表达式和 JsonPath,在注解中指明 type 之后即可。

 @ExtractBy(value = "div.BlogContent", type = ExtractBy.Type.Css) 
 private String content;

notnull

@ExtractBy 包含一个 notNull 属性,如果熟悉 mysql 的同学一定能明白它的意思:此字段不允许为空。如果为空,这条抽取到的结果会被丢弃。对于一些页面的关键性属性(例如文章的标题等),设置 notnull 为 true,可以有效的过滤掉无用的页面。notNull 默认为 false。

@ExtractByUrl

@ExtractByUrl 是一个单独的注解,它的意思是“从 URL 中进行抽取”。它只支持正则表达式作为抽取规则。

在类上使用 ExtractBy

在之前的注解模式中,我们一个页面只对应一条结果。如果一个页面有多个抽取的记录呢?例如在“QQ 美食”的列表页面,我想要抽取所有商户名和优惠信息,该怎么办呢?
在类上使用@ExtractBy 注解可以解决这个问题。
在类上使用这个注解的意思很简单:使用这个结果抽取一个区域,让这块区域对应一个结果。

 @TargetUrl("http://meishi.qq.com/beijing/c/all[//-p2]*") 
 @ExtractBy(value = "//ul[@id=/"promos_list2/"]/li",multi = true) 
 public class QQMeishi {     
 @ExtractBy("//div[@class=info]/a[@class=title]/h4/text()")     
 private String shopName;     
 @ExtractBy("//div[@class=info]/a[@class=title]/text()")     
 private String promo;     
 public static void main(String[] args) {         
 OOSpider.create(Site.me(), new ConsolePageModelPipeline(), 
 QQMeishi.class).addUrl("http://meishi.qq.com/beijing/c/all").thread(4).run();     } }

@Formatter

@Formatter 可以将抽取到的内容,自动转换成一些基本类型,而无需手动使用代码进行转换。

 @Formatter("yyyy-MM-dd HH:mm") 
 @ExtractBy("//div[@class='BlogStat']/regex('//d+-//d+-//d+//s+//d+://d+')") 
 private Date date;

自定义 Formatter

实际上,除了自动类型转换之外,Formatter 还可以做一些结果的后处理的事情。例如,我们有一种需求场景,需要将抽取的结果作为结果的一部分,拼接上一部分字符串来使用。在这里,我们定义了一个 StringTemplateFormatter。

 public class StringTemplateFormatter implements ObjectFormatter<String> {     
 private String template;     
 @Override  public String format(String raw) throws Exception {         
 return String.format(template, raw);     }     
 @Override  public Class<String> clazz() {         
 return String.class;     }     
 @Override  public void initParam(String[] extra) {         
 template = extra[0];     } }

那么,我们就能在抽取之后,做一些简单的操作了!

 @Formatter(value = "author is %s",formatter = StringTemplateFormatter.class) 
 @ExtractByUrl("https://github//.com/(//w+)/.*") private String author;

AfterExtractor

有的时候,注解模式无法满足所有需求,我们可能还需要写代码完成一些事情,这个时候就要用到 AfterExtractor 接口了。

 public interface AfterExtractor {     public void afterProcess(Page page); }

afterProcess 方法会在抽取结束,字段都初始化完毕之后被调用,可以处理一些特殊的逻辑。

 //TargetUrl 的意思是只有以下格式的 URL 才会被抽取出生成 model 对象 
 //这里对正则做了一点改动,'.'默认是不需要转义的,而'*'则会自动被替换成'.*',
 //因为这样描述 URL 看着舒服一点... 
 //继承 jfinal 中的 Model 
 //实现 AfterExtractor 接口可以在填充属性后进行其他操作 
 @TargetUrl("http://my.oschina.net/flashsword/blog/*") 
 public class OschinaBlog extends Model<OschinaBlog> implements 
 AfterExtractor {     
 //用 ExtractBy 注解的字段会被自动抽取并填充     
 //默认是 xpath 语法     
 @ExtractBy("//title")     
 private String title;     
 //可以定义抽取语法为 Css、Regex 等     
 @ExtractBy(value = "div.BlogContent", type = ExtractBy.Type.Css)     
 private String content;     
 //multi 标注的抽取结果可以是一个 List     
 @ExtractBy(value = "//div[@class='BlogTags']/a/text()", multi = true)     
 private List<String> tags;     
 @Override     public void afterProcess(Page page) {         
 //jfinal 的属性其实是一个 Map 而不是字段,没关系,填充进去就是了         
 this.set("title", title);         
 this.set("content", content);         
 this.set("tags", StringUtils.join(tags, ","));         
 //保存         save();     }     
 public static void main(String[] args) {         
 C3p0Plugin c3p0Plugin = new C3p0Plugin(
 "jdbc:mysql://127.0.0.1/blog?characterEncoding=utf-8", "blog", "password");         
 c3p0Plugin.start();         
 ActiveRecordPlugin activeRecordPlugin = new ActiveRecordPlugin(c3p0Plugin);         
 activeRecordPlugin.addMapping("blog", OschinaBlog.class);         
 activeRecordPlugin.start();         
 //启动 webmagic         
 OOSpider.create(Site.me().addStartUrl(
 "http://my.oschina.net/flashsword/blog/145796"), OschinaBlog.class).run();} }

在 WebMagic 里,注解模式其实是完全基于 webmagic-core 中的 PageProcessor 和 Pipeline 扩展实现的,有兴趣的朋友可以去看看代码。


web 前端中文站 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:WebMagic 爬虫框架 注解用法
喜欢 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址