WebMagic之优秀爬虫框架

image

1. 一个框架,一个领域

一个好的框架必然凝聚了领域知识。WebMagic的设计参考了业界最优秀的爬虫Scrapy,而实现则应用了HttpClient、Jsoup等Java世界最成熟的工具,目标就是做一个Java语言Web爬虫的教科书般的实现。

如果你是爬虫开发老手,那么WebMagic会非常容易上手,它几乎使用Java原生的开发方式,只不过提供了一些模块化的约束,封装一些繁琐的操作,并且提供了一些便捷的功能。

如果你是爬虫开发新手,那么使用并了解WebMagic会让你了解爬虫开发的常用模式、工具链、以及一些问题的处理方式。熟练使用之后,相信自己从头开发一个爬虫也不是什么难事。

因为这个目标,WebMagic的核心非常简单——在这里,功能性是要给简单性让步的。

一般来说,一个爬虫包括几个部分:

2. 微内核和高可扩展性

WebMagic由四个组件(Downloader、PageProcessor、Scheduler、Pipeline)构成,核心代码非常简单,主要是将这些组件结合并完成多线程的任务。这意味着,在WebMagic中,你基本上可以对爬虫的功能做任何定制。

WebMagic的核心在webmagic-core包中,其他的包你可以理解为对WebMagic的一个扩展——这和作为用户编写一个扩展是没有什么区别的。

下面是使用webmaigc的步骤:

  • 页面下载

    页面下载是一个爬虫的基础。下载页面之后才能进行其他后续操作。

  • 链接提取

    一般爬虫都会有一些初始的种子URL,但是这些URL对于爬虫是远远不够的。爬虫在爬页面的时候,需要不断发现新的链接。

  • URL管理

    最基础的URL管理,就是对已经爬过的URL和没有爬的URL做区分,防止重复爬取。

  • 内容分析和持久化

    一般来说,我们最终需要的都不是原始的HTML页面。我们需要对爬到的页面进行分析,转化成结构化的数据,并存储下来。

不同的爬虫,对这几部分的要求是不一样的。

对于通用型的爬虫,例如搜索引擎蜘蛛,需要指对互联网大部分网页无差别进行抓取。这时候难点就在于页面下载和链接管理上--如果要高效的抓取更多页面,就必须进行更快的下载;同时随着链接数量的增多,需要考虑如果对大规模的链接进行去重和调度,就成了一个很大的问题。一般这些问题都会在大公司有专门的团队去解决,比如这里有一篇来自淘宝的快速构建实时抓取集群。对Java来说,如果你要研究通用爬虫,那么可以看一下heritrix或者nutch

而垂直类型的爬虫要解决的问题则不一样,比如想要爬取一些网站的新闻、博客信息,一般抓取数量要求不是很大,难点则在于如何高效的定制一个爬虫,可以精确的抽取出网页的内容,并保存成结构化的数据。这方面需求很多,webmagic就是为了解决这个目的而开发的。

使用Java语言开发爬虫是比较复杂的。虽然Java有很强大的页面下载、HTML分析工具,但是每个都有不小的学习成本,而且这些工具本身都不是专门为爬虫而生,使用起来也没有那么顺手

 webmagic的实现还参考了另一个Java爬虫SpiderMan。SpiderMan是一个全栈式的Java爬虫,它的设计思想跟webmagic稍有不同,它希望将Java语言的实现隔离,仅仅让用户通过配置就完成一个垂直爬虫。理论上,SpiderMan功能更强大,很多功能已经内置,而webmagic则比较灵活,适合熟悉Java语法的开发者,可以比较非常方便的进行扩展和二次开发。

webmagic的模块划分

webmagic目前的核心代码都在webmagic-core中,webmagic-samples里有一些定制爬虫的例子,可以作为参考。而webmagic-plugin目前还不完善,后期准备加入一些常用的功能。下面主要介绍webmagic-core的内容。

前面说到,webmagic参考了scrapy的模块划分,分为Spider(整个爬虫的调度框架)、Downloader(页面下载)、PageProcessor(链接提取和页面分析)、Scheduler(URL管理)、Pipeline(离线分析和持久化)几部分。只不过scrapy通过middleware实现扩展,而webmagic则通过定义这几个接口,并将其不同的实现注入主框架类Spider来实现扩展。

image

Spider类-核心调度

Spider是爬虫的入口类,Spider的接口调用采用了链式的API设计,其他功能全部通过接口注入Spider实现,下面是启动一个比较复杂的Spider的例子。


    long startTime, endTime;
    System.out.println("========MM爬虫【启动】!=========");
    startTime = new Date().getTime();
   Spider spider =  Spider.create(new MMPaqu()).addUrl(initUrl);
   //添加spider监控
   try {
	SpiderMonitor.instance().register(spider);
} catch (JMException e) {
	e.printStackTrace();
}
   	spider.thread(5).run();
    endTime = new Date().getTime();
    System.out.println("========MM爬虫【结束】!=========");
    System.out.println("一共爬到" + num + "个MM!用时为:" + (endTime - startTime) / 1000 + "s");

Spider的核心处理流程非常简单,代码如下:

<!-- lang: java -->
private void processRequest(Request request) {
    Page page = downloader.download(request, this);
    if (page == null) {
        sleep(site.getSleepTime());
        return;
    }
    pageProcessor.process(page);
    addRequest(page);
    for (Pipeline pipeline : pipelines) {
        pipeline.process(page, this);
    }
    sleep(site.getSleepTime());
}

Downloader-页面下载

页面下载是webmagic的入口,一切工作的开始.

大部分爬虫都是通过模拟http请求,接收并分析响应来完成。

而JDK自带的HttpURLConnection可以满足最简单的需要,并且Apache HttpClient(4.0后整合到HttpCompenent项目中)则是开发复杂爬虫的不二之选。它支持自定义HTTP头(对于爬虫比较有用的就是User-agent、cookie等)、自动redirect、连接复用、cookie保留、设置代理等诸多强大的功能。

webmagic使用了HttpClient 4.2,并封装到了HttpClientDownloader。学习HttpClient的使用对于构建高性能爬虫是非常有帮助的。

下面是一个使用HttpClient最简单的例子:

StringBuffer result = new StringBuffer();
org.apache.http.client.HttpClient httpClient = (org.apache.http.client.HttpClient) new HttpClient();
		HttpResponse httpReponse=null ;
		 BufferedReader br=null;
		try {
			  httpReponse = httpClient.execute(httpUriRequest);
			  InputStream is = httpReponse.getEntity().getContent();
			  //读取数据流内容
			    br = new BufferedReader(new InputStreamReader(is));
			  String  line ="";
			  while((line = br.readLine() )!=null){
				  result.append(line);
			  }
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			try {
				br.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		return result;
		
	

对于一些Javascript动态加载的网页,仅仅使用http模拟下载工具,并不能取到页面的内容。

目前我想到了两种解决方案:

1 抽丝剥茧:

分析一下页面js逻辑,一般动态请求构造生成的html页面都是通过ajax请求,我们拿到具体的ajax请求之后,可以通过爬虫来重现取数过程,然后提取关键数据,获取想要的数据。

2 模拟浏览器:

可以使用目前业内主流的多任务自动化测试工具Selenium,这是一个非常强大的浏览器模拟工具,我们可以通过java来调用selenium实现浏览器模拟,从而重现整个请求过程。

目前,我使用的是第一种方案,参见我的源码:xx

一般没有必要去扩展Downloader。

PageProcessor-页面分析及链接抽取

这里说的页面分析主要指HTML页面的分析。页面分析可以说是垂直爬虫最复杂的一部分,在webmagic里,PageProcessor是定制爬虫的核心。通过编写一个实现PageProcessor接口的类,就可以定制一个自己的爬虫。

页面抽取最基本的方式是使用正则表达式。正则表达式好处是非常通用,解析文本的功能也很强大。但是正则表达式最大的问题是,不能真正对HTML进行语法级别的解析,没有办法处理关系到HTML结构的情况(例如处理标签嵌套)。例如,我想要抽取一个<div>里的内容,可以这样写:"<div>(.*?)</div>"。但是如果这个div内部还包含几个子div,这个时候使用正则表达式就会将子div的"</div>"作为终止符截取。为了解决这个问题,我们就需要进行HTML的分析。

HTML分析是一个比较复杂的工作,Java世界主要有几款比较方便的分析工具:

####Jsoup

Jsoup是一个集强大和便利于一体的HTML解析工具。它方便的地方是,可以用于支持用jquery中css selector的方式选取元素,这对于熟悉js的开发者来说基本没有学习成本。


		String html =null; 
		try {
			  html = HttpClient.doGet("https://www.tianyancha.com/login", "", "UTF-8");
			  Document doc = Jsoup.parse(html);
			  Element tel = doc.select(".pb30 .position-rel").get(0);
			  Element pass = doc.select(".pb40 .position-rel").get(0);
			  
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		System.out.println(html);
	

Jsoup还支持白名单过滤机制,对于网站防止XSS攻击也是很好的。

几个工具的对比

在这里评价这些工具的主要标准是“方便”。就拿抽取页面所有链接这一基本任务来说,几种代码分别如下:

XPath:

<!-- lang: java -->
tagNode.evaluateXPath("//a/@href")

CSS Selector:

<!-- lang: java -->
//使用类似js的实现
$("a[href]").attr("href")

HtmlParser:

<!-- lang: java -->
Parser p = new Parser(value);
NodeFilter aFilter = new TagNameFilter("a");
NodeList nodes = p.extractAllNodesThatMatch(aFilter);
for (int i = 0; i < nodes.size(); i++) {
    Node eachNode = nodes.elementAt(i);
    if (eachNode instanceof LinkTag) {
        LinkTag linkTag = (LinkTag) eachNode;
        System.out.println(linkTag.extractLink());
    }
}

XPath是最简单的,可以精确选取到href属性值;而CSS Selector则次之,可以选取到HTML标签,属性值需要调用函数去获取;而HtmlParser和SAX则需要手动写程序去处理标签了,比较麻烦。

Selector

Selector是webmagic为了简化页面抽取开发的独立模块,是整个项目中我最得意的部分。这里整合了CSS Selector、XPath和正则表达式,并可以进行链式的抽取,很容易就实现强大的功能。即使你使用自己开发的爬虫工具,webmagic的Selector仍然值得一试。

Scheduler-URL管理

URL管理的问题可大可小。对于小规模的抓取,URL管理是很简单的。我们只需要将待抓取URL和已抓取URL分开保存,并进行去重即可。使用JDK内置的集合类型Set、List或者Queue都可以满足需要。如果我们要进行多线程抓取,则可以选择线程安全的容器,例如LinkedBlockingQueue以及ConcurrentHashMap。

webmagic目前有两个Scheduler的实现,QueueScheduler是一个简单的内存队列,速度较快,并且是线程安全的,FileCacheQueueScheduler则是一个文件队列,它可以用于耗时较长的下载任务,在任务中途停止后,下次执行仍然从中止的URL开始继续爬取。

Pipeline-离线处理和持久化

Pipeline其实也是容易被忽略的一部分。大家都知道持久化的重要性,但是很多框架都选择直接在页面抽取的时候将持久化一起完成,例如crawer4j。但是Pipeline真正的好处是,将页面的在线分析和离线处理拆分开来,可以在一些线程里进行下载,另一些线程里进行处理和持久化。

结语

附上webMagic介绍:http://webmagic.io/docs/zh/

转载于:https://my.oschina.net/yaukie/blog/1635244

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值