webmagic中文文档(7)

2019-01-27 21:20

}

page.putField(\}

现在我们想将结果保存到控制台,要怎么做呢?ConsolePipeline可以完成这个工作: public class ConsolePipeline implements Pipeline { @Override

public void process(ResultItems resultItems, Task task) {

System.out.println(\

//遍历所有结果,输出到控制台,上面例子中的\、\、\都是一个key,其结果则是对应的value

for (Map.Entry entry : resultItems.getAll().entrySet()) { System.out.println(entry.getKey() + \ } } }

参考这个例子,你就可以定制自己的Pipeline了——从ResultItems中取出数据,再按照你希望的方式处理即可。

6.1.3 将结果保存到MySQL

这里先介绍一个demo项目:jobhunter。它是一个集成了Spring,使用WebMagic抓取招聘信息,并且使用Mybatis持久化到Mysql的例子。我们会用这个项目来介绍如果持久化到Mysql。

在Java里,我们有很多方式将数据保存到MySQL,例如jdbc、dbutils、spring-jdbc、MyBatis等工具。这些工具都可以完成同样的事情,只不过功能和使用复杂程度不一样。如果使用jdbc,那么我们只需要从ResultItems取出数据,进行保存即可。

如果我们会使用ORM框架来完成持久化到MySQL的工作,就会面临一个问题:这些框架一般都要求保存的内容是一个定义好结构的对象,而不是一个key-value形式的ResultItems。以MyBatis为例,我们使用MyBatis-Spring可以定义这样一个DAO: public interface JobInfoDAO {

@Insert(\into JobInfo (`title`,`salary`,`company`,`description`,`requirement`,`source`,`url`,`urlMd5`) values (#{title},#{salary},#{company},#{description},#{requirement},#{source},#{url},#{urlMd5})\ public int add(LieTouJobInfo jobInfo); }

我们要做的,就是实现一个Pipeline,将ResultItems和LieTouJobInfo对象结合起来。 注解模式

注解模式下,WebMagic内置了一个PageModelPipeline: public interface PageModelPipeline { //这里传入的是处理好的对象 public void process(T t, Task task); }

这时,我们可以很优雅的定义一个JobInfoDaoPipeline,来实现这个功能: @Component(\

public class JobInfoDaoPipeline implements PageModelPipeline {

@Resource

private JobInfoDAO jobInfoDAO; @Override

public void process(LieTouJobInfo lieTouJobInfo, Task task) { //调用MyBatis DAO保存结果 jobInfoDAO.add(lieTouJobInfo); } }

基本Pipeline模式

至此,结果保存就已经完成了!那么如果我们使用原始的Pipeline接口,要怎么完成呢?其实答案也很简单,如果你要保存一个对象,那么就需要在抽取的时候,将它保存为一个对象: public void process(Page page) {

page.addTargetRequests(page.getHtml().links().regex(\ page.addTargetRequests(page.getHtml().links().regex(\ GithubRepo githubRepo = new GithubRepo();

githubRepo.setAuthor(page.getUrl().regex(\ githubRepo.setName(page.getHtml().xpath(\public']/strong/a/text()\

githubRepo.setReadme(page.getHtml().xpath(\ if (githubRepo.getName() == null) { //skip this page page.setSkip(true); } else {

page.putField(\ } }

在Pipeline中,只要使用

GithubRepo githubRepo = (GithubRepo)resultItems.get(\ 就可以获取这个对象了。

PageModelPipeline实际上也是通过原始的Pipeline来实现的,它将与PageProcessor进行了整合,在保存时,使用类名作为key,而对象则是value,具体实现见:ModelPipeline。 6.1.4 WebMagic已经提供的几个Pipeline

WebMagic中已经提供了将结果输出到控制台、保存到文件和JSON格式保存的几个Pipeline: 类

ConsolePipeline FilePipeline JsonFilePipeline FilePageModelPipeline

说明

输出结果到控制台 保存结果到文件

JSON格式保存结果到文件 (注解模式)保存结果到文件

备注

抽取结果需要实现toString方法 抽取结果需要实现toString方法

ConsolePageModelPipeline (注解模式)输出结果到控制台

(注解模式)JSON格式保存结果到想要持久化的字段需要有getterJsonFilePageModelPipeline

文件 方法 6.2 定制Scheduler

Scheduler是WebMagic中进行URL管理的组件。一般来说,Scheduler包括两个作用:

1. 对待抓取的URL队列进行管理。 2. 对已抓取的URL进行去重。

WebMagic内置了几个常用的Scheduler。如果你只是在本地执行规模比较小的爬虫,那么基本无需定制Scheduler,但是了解一下已经提供的几个Scheduler还是有意义的。 类

说明

备注

抽象基类,提供一些模板

DuplicateRemovedScheduler 继承它可以实现自己的功能

方法 QueueScheduler

使用内存队列保存待抓

取URL

耗费内存较QueueScheduler更大,但是

使用带有优先级的内存

当设置了request.priority之后,只能使

队列保存待抓取URL

用PriorityScheduler才可使优先级生效

PriorityScheduler

使用文件保存抓取URL,

可以在关闭程序并下次需指定路径,会建立.urls.txt和.cursor.txt

FileCacheQueueScheduler

启动时,从之前抓取到的两个文件 URL继续抓取 RedisScheduler

使用Redis保存抓取队列,可进行多台机器同时需要安装并启动redis 合作抓取

在0.5.1版本里,我对Scheduler的内部实现进行了重构,去重部分被单独抽象成了一个接口:DuplicateRemover,从而可以为同一个Scheduler选择不同的去重方式,以适应不同的需要,目前提供了两种去重方式。 类

HashSetDuplicateRemover

说明

使用HashSet来进行去重,占用内存较大

使用BloomFilter来进行去重,占用内存较小,但是可能漏抓页

BloomFilterDuplicateRemover

面 所有默认的Scheduler都使用HashSetDuplicateRemover来进行去重,(除开RedisScheduler是使用Redis的set进行去重)。如果你的URL较多,使用HashSetDuplicateRemover会比较占用内存,所以也可以尝试以下BloomFilterDuplicateRemover,使用方式: spider.setScheduler(new QueueScheduler() .setDuplicateRemover(new BloomFilterDuplicateRemover(10000000)) //10000000是估计的页面数量 )

6.3 使用Downloader

WebMagic的默认Downloader基于HttpClient。一般来说,你无须自己实现Downloader,不过HttpClientDownloader也预留了几个扩展点,以满足不同场景的需求。

另外,你可能希望通过其他方式来实现页面下载,例如使用SeleniumDownloader来渲染动态页面。

一些常见爬虫的编写方式

即使你对WebMagic的框架已经很熟练了,也会对有些爬虫的编写有些迷茫。比如如何定期抓取并更新、如何抓取动态渲染的页面等。

这一节我会整理一些常见案例,希望对读者有帮助。 列表+详情的基本页面组合

我们先从一个最简单的例子入手。这个例子里,我们有一个列表页,这个列表页以分页的形式展现,我们可以遍历这些分页找到所有目标页面。 1 示例介绍

这里我们以作者的新浪博客http://blog.sina.com.cn/flashsword20作为例子。在这个例子里,我们要从最终的博客文章页面,抓取博客的标题、内容、日期等信息,也要从列表页抓取博客的链接等信息,从而获取这个博客的所有文章。

? 列表页

列表页的格式是“http://blog.sina.com.cn/s/articlelist_1487828712_0_1.html“, 其中“0_1”中的“1”是可变的页数。

文章页

文章页的格式是“http://blog.sina.com.cn/s/blog_58ae76e80100g8au.html”, 其中“58ae76e80100g8au”是可变的字符。

?

2 发现文章URL

在这个爬虫需求中,文章URL是我们最终关心的,所以如何发现这个博客中所有的文章地址,是爬虫的第一步。

我们可以使用正则表达式http://blog\\\\.sina\\\\.com\\\\.cn/s/blog_\\\\w+\\\\.html对URL进行一次粗略过滤。这里比较复杂的是,这个URL过于宽泛,可能会抓取到其他博客的信息,所以我们必须从列表页中指定的区域获取URL。

在这里,我们使用xpath//div[@class=\\\\\选中所有区域,再使用links()或者

xpath//a/@href获取所有链接,最后再使用正则表达式http://blog\\\\.sina\\\\.com\\\\.cn/s/blog_\\\\w+\\\\.html,对URL进行过滤,去掉一些“编辑”或者“更多”之类的链接。于是,我们可以这样写:

page.addTargetRequests(page.getHtml().xpath(\//blog\\\\.sina\\\\.com\\\\.cn/s/blog_\\\\w+\\\\.html\

同时,我们需要把所有找到的列表页也加到待下载的URL中去:

page.addTargetRequests(page.getHtml().links().regex(\1487828712_0_\\\\d+\\\\.html\

3 抽取内容

文章页面信息的抽取是比较简单的,写好对应的xpath抽取表达式就可以了。 page.putField(\ page.putField(\

page.getHtml().xpath(\ page.putField(\page.getHtml().xpath(\SG_txtc']\ 4 区分列表和目标页 现在,我们已经定义了对列表和目标页进行处理的方式,现在我们需要在处理时对他们进行区分。在这个例子中,区分方式很简单,因为列表页和目标页在URL格式上是不同的,所以直接用URL区分就可以了! //列表页

if (page.getUrl().regex(URL_LIST).match()) {

page.addTargetRequests(page.getHtml().xpath(\POST).all());

page.addTargetRequests(page.getHtml().links().regex(URL_LIST).all());

//文章页 } else {

page.putField(\ page.putField(\

page.getHtml().xpath(\ page.putField(\

page.getHtml().xpath(\SG_txtc']\}

这个例子完整的代码请看SinaBlogProcessor.java。 5 总结

在这个例子中,我们的主要使用几个方法:

? 从页面指定位置发现链接,使用正则表达式来过滤链接.

? 在PageProcessor中处理两种页面,根据页面URL来区分需要如何处理。 有些朋友反应,用if-else来区分不同处理有些不方便#issue83。WebMagic计划在将来的0.5.0版本中,加入SubPageProcessor来解决这个问题。 抓取前端渲染的页面

随着AJAX技术不断的普及,以及现在AngularJS这种Single-page application框架的出现,现在js渲染出的页面越来越多。对于爬虫来说,这种页面是比较讨厌的:仅仅提取HTML内


webmagic中文文档(7).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:徐州市中高说课高创新要求

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: