Java之网络爬虫WebCollector+selenium+phantomjs(一)

最近研究了一下爬虫技术,与你们分享一下。javascript

因为目前有不少成熟的框架(奉劝不要本身花时间再写爬虫框架了,真心不必),俺也就从中选一个适合我目前需求或者说相对简单的框架来学习吧。css

先把各类网络爬虫框架地址曝光一下:基于Java的网络爬虫框架集合html

此次学习的框架WebCollector2:WebCollector2前端

WebCollector中集成的Jsoup:Jsoup中文文档java

后面抓取js动态生成的html还须要selenium-api-2.12.chm(selenium中文api手册)与phantomJS(windows运行程序),固然若是你想在linux或者mac上配置环境能够访问各类操做系统phantomJS运行环境node

至于项目中须要的各类jar请看下面的pom清单(本次是用maven构建的工程)。mysql

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>WebCollectorDemo</groupId>
  <artifactId>WebCollectorDemo</artifactId>
  <version>1.0</version>
  <packaging>war</packaging>
  <name/>
  <description/>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies>
    	<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.11</version>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.5</version>
		</dependency>
		<!-- WebCollector dependency -->
		<dependency>
            <groupId>cn.edu.hfut.dmic.webcollector</groupId>
            <artifactId>WebCollector</artifactId>
            <version>2.09</version>
        </dependency>
    	<!-- selenium -->
    	<dependency>
			<groupId>org.seleniumhq.selenium</groupId>
			<artifactId>selenium-java</artifactId>
			<version>2.44.0</version>
		</dependency>
		<!-- phantomjsdriver(selenium webdriver 第三方支持) -->
		<dependency>
    		<groupId>com.github.detro</groupId>
    		<artifactId>phantomjsdriver</artifactId>
    		<version>1.2.0</version>
		</dependency>
  </dependencies>
  <build>
    <sourceDirectory>${basedir}/src</sourceDirectory>
    <outputDirectory>${basedir}/WebRoot/WEB-INF/classes</outputDirectory>
    <resources>
      <resource>
        <directory>${basedir}/src</directory>
        <excludes>
          <exclude>**/*.java</exclude>
        </excludes>
      </resource>
    </resources>
    <plugins>
      <plugin>
        <artifactId>maven-war-plugin</artifactId>
        <configuration>
          <webappDirectory>${basedir}/WebRoot</webappDirectory>
          <warSourceDirectory>${basedir}/WebRoot</warSourceDirectory>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
目录结构:

好,准备工做已经作完,下面按照官网教程作一个到两个小例子,后面会有一个爬取京东商品的例子展示给你们。linux

 在进行小例子以前,先简单说下爬虫的逻辑,方便你们对框架有更好的理解。基本思路是:git

1.有个种子连接(你准备爬取的网页地址)github

2.设置爬取规则(根据此规则在种子连接网页上进行爬取新的连接,以方便进行更深刻挖掘,固然你也能够不进行爬取,能够添加更多种子连接,值爬取页面上你感兴趣的东西,而不进行更深刻爬取,看你本身的需求)

3.爬取到感兴趣的网页后,对网页上感兴趣的内容进行爬取,主要经过css属性或者html标签来抓取你想要的内容,因此你须要具备相关前端网页知识

ok,上面就是基本思路。下面给出一张webcollector爬虫类的继承体系结构图:

结合上图,抽象类DeepCrawler是最主要的爬虫类,能够经过此抽象类自定义本身须要的爬取策略,其中BreadthCrawler为DeepCrawler的子抽象类,MultiExtractor为BreadthCrawler的子类。BreadthCrawler实现了一个深度爬取功能的封装,用户能够添加本身的种子与过滤规则而且能够决定是否进行深度爬取。MultiExtractor在BreadthCrawler基础上作了一个多页面爬取的封装。下面结合官网的第一个小例子WebCollector 2.x tutorial 2 (BreadthCrawler中文教程)进行咱们的学习。

我稍稍去掉了一些内容,去掉了jdbc 模版的部分,有兴趣的同窗稍微看下官网例子便可。下面是个人代码

/*
 * Copyright (C) 2015 zhao
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package com.zhao.crawler.demo;

import org.jsoup.nodes.Document;

import cn.edu.hfut.dmic.webcollector.crawler.BreadthCrawler;
import cn.edu.hfut.dmic.webcollector.model.Links;
import cn.edu.hfut.dmic.webcollector.model.Page;

/**
 *
 * WebCollector 2.x版本的tutorial
 * 2.x版本特性:
 *   1)自定义遍历策略,可完成更为复杂的遍历业务,例如分页、AJAX
 *   2)内置Berkeley DB管理URL,能够处理更大量级的网页
 *   3)集成selenium,能够对javascript生成信息进行抽取
 *   4)直接支持多代理随机切换
 *   5)集成spring jdbc和mysql connection,方便数据持久化
 *   6)集成json解析器
 *   7)使用slf4j做为日志门面
 *   8)修改http请求接口,用户自定义http请求更加方便
 * 
 * 可在cn.edu.hfut.dmic.webcollector.example包中找到例子(Demo)
 * 
 * @author hu
 *
 * @author <a href="ls.zhaoxiangyu@gmail.com">zhao</>
 * @date 2015-10-20
 */
public class TutorialCrawler2 extends BreadthCrawler {

	/**
	 * 若是autoParse设置为true,遍历器会自动解析页面中符合正则的连接,加入后续爬取任务,不然不自动解析连接。
	 *
	 * @param crawlPath
	 * @param autoParse
	 */
	public TutorialCrawler2(String crawlPath, boolean autoParse) {
		super(crawlPath, autoParse);
		
		/*BreadthCrawler能够直接添加URL正则规则*/
		this.addRegex("http://item.jd.com/.*.html");
//        this.addRegex("http://.*zhihu.com/.*");
//        this.addRegex("-.*jpg.*");
	}

	/**
	 *用户自定义对每一个页面的操做,通常将抽取、持久化等操做写在visit方法中。
	 *
	 * @param page
	 * @param nextLinks 须要后续爬取的URL。若是autoParse为true,爬虫会自动抽取符合正则的连接并加入nextLinks。
	 */
	@Override
	public void visit(Page page, Links nextLinks) {
		Document doc=page.getDoc();
		String title = doc.title();
        System.out.println("URL:" + page.getUrl() + "  标题:" + title);
//        System.out.println(doc.html());
        
        /*
        //添加到nextLinks的连接会在下一层或下x层被爬取,爬虫会自动对URL进行去重,因此用户在编写爬虫时彻底没必要考虑生成重复URL的问题。
        //若是这里添加的连接已经被爬取过,则连接不会在后续任务中被爬取
        //若是须要强制添加已爬取过的连接,只能在爬虫启动(包括断点启动)时,经过Crawler.addForcedSeed强制加入URL。
         nextLinks.add("http://www.csdn.net");
        */
	}
	
	public static void main(String[] args) throws Exception {
		 /*
        第一个参数是爬虫的crawlPath,crawlPath是维护URL信息的文件夹的路径,若是爬虫须要断点爬取,每次请选择相同的crawlPath
        第二个参数表示是否自动抽取符合正则的连接并加入后续任务
     */
     TutorialCrawler2 crawler = new TutorialCrawler2("D:/test/crawler/demo",true);
     crawler.setThreads(50);
     crawler.addSeed("http://list.jd.com/list.html?cat=1319,1523,7052&page=1&go=0&JL=6_0_0");
//     crawler.addSeed("http://www.zhihu.com/");
     crawler.setResumable(false);

     /*
     //requester是负责发送http请求的插件,能够经过requester中的方法来指定http/socks代理
     HttpRequesterImpl requester=(HttpRequesterImpl) crawler.getHttpRequester();    
    
     //单代理
     requester.setProxy("127.0.0.1", 1080,Proxy.Type.SOCKS);
     
     //多代理随机
     RandomProxyGenerator proxyGenerator=new RandomProxyGenerator();
     proxyGenerator.addProxy("127.0.0.1",8080,Proxy.Type.SOCKS);
     requester.setProxyGenerator(proxyGenerator);
     */


     /*设置是否断点爬取*/
     crawler.setResumable(false);
     /*设置每层爬取爬取的最大URL数量*/
     crawler.setTopN(1000);

     /*若是但愿尽量地爬取,这里能够设置一个很大的数,爬虫会在没有待爬取URL时自动中止*/
     crawler.start(2);
	}
}

TutorialCrawler2.java我将原例子中爬取知乎改为了爬取京东的一个商品列表页面。重写父类中visit方法,在此方法中对页面进行抽取工做。构造函数中添加了一个过滤规则,这块须要你们对正则表达式有些基本了解,第一个过滤规则是抽取连接符合该规则的全部连接(即全部商品连接),咱们这里visit方法里只作了简单的打印工做,打印了爬取的url与页面标题。

再说下main函数里面,咱们首先建立一个TutorialCrawler2对象实例,两个参数分别为保存爬取数据的保存路径,与是否进行深度爬取。setThreads()是设置爬取启动的最大线程数(理论上同时爬取多页面时启动线程越多,爬取速度越快,追踪源码发现默认为50)。addSeed()是添加爬取的种子页面,能够添加一个或者多个。setResumable()设置是否支持断点爬取,setTop()为设置每层爬取的最大url数量,start(int depth)为启动爬取程序,其中depth为爬取层数。在这里解释下层数的概念,种子url为第一层,若是设置start(1),那么只会爬取种子页面,不会再往下进行爬取,若是设置start(2),那么从种子页面爬取出来的全部连接即为第二层准备爬取的连接,爬取完第二层连接就不会继续往下继续爬取。好接下来运行该爬取程序,会把种子页面全部符合爬取规则的连接都抽取出来,而后会继续爬取从第一层抽取出来的连接,继续按照过滤规则抽取符合过滤规则的连接,可是不会继续往下继续怕去了额,下面为运行结果。

[cn.edu.hfut.dmic.webcollector.crawler.Crawler]starting depth 1
[cn.edu.hfut.dmic.webcollector.fetcher.Fetcher]-activeThreads=1, spinWaiting=0, fetchQueue.size=0
[cn.edu.hfut.dmic.webcollector.fetcher.Fetcher]fetch http://list.jd.com/list.html?cat=1319,1523,7052&page=1&go=0&JL=6_0_0
URL:http://list.jd.com/list.html?cat=1319,1523,7052&page=1&go=0&JL=6_0_0  标题:婴幼奶粉 奶粉 母婴 【行情 价格 评价 正品行货】-京东
[cn.edu.hfut.dmic.webcollector.fetcher.Fetcher]-activeThreads=0, spinWaiting=0, fetchQueue.size=0
[cn.edu.hfut.dmic.webcollector.fetcher.Fetcher]clear all activeThread
[cn.edu.hfut.dmic.webcollector.fetcher.DbUpdater]start merge
[cn.edu.hfut.dmic.webcollector.fetcher.DbUpdater]merge fetch database
[cn.edu.hfut.dmic.webcollector.fetcher.DbUpdater]merge link database
[cn.edu.hfut.dmic.webcollector.fetcher.DbUpdater]end merge
[cn.edu.hfut.dmic.webcollector.fetcher.DbUpdater]remove fetch database
[cn.edu.hfut.dmic.webcollector.fetcher.DbUpdater]remove link database
[cn.edu.hfut.dmic.webcollector.crawler.Crawler]depth 1 finish: 
	TOTAL urls:	1
	TOTAL time:	2 seconds
[cn.edu.hfut.dmic.webcollector.crawler.Crawler]starting depth 2
[cn.edu.hfut.dmic.webcollector.net.HttpRequest]redirect from http://item.jd.com/1950232304.html to http://item.jd.hk/1950232304.html
[cn.edu.hfut.dmic.webcollector.net.HttpRequest]redirect from http://item.jd.com/1950401845.html to http://item.jd.hk/1950401845.html
[cn.edu.hfut.dmic.webcollector.net.HttpRequest]redirect from http://item.jd.com/1950290275.html to http://item.jd.hk/1950290275.html
[cn.edu.hfut.dmic.webcollector.net.HttpRequest]redirect from http://item.jd.com/1950106400.html to http://item.jd.hk/1950106400.html
[cn.edu.hfut.dmic.webcollector.net.HttpRequest]redirect from http://item.jd.com/1950301488.html to http://item.jd.hk/1950301488.html
[cn.edu.hfut.dmic.webcollector.net.HttpRequest]redirect from http://item.jd.com/1950252374.html to http://item.jd.hk/1950252374.html
[cn.edu.hfut.dmic.webcollector.net.HttpRequest]redirect from http://item.jd.com/1950220622.html to http://item.jd.hk/1950220622.html
[cn.edu.hfut.dmic.webcollector.net.HttpRequest]redirect from http://item.jd.com/1950334150.html to http://item.jd.hk/1950334150.html
[cn.edu.hfut.dmic.webcollector.net.HttpRequest]redirect from http://item.jd.com/1950204490.html to http://item.jd.hk/1950204490.html
[cn.edu.hfut.dmic.webcollector.net.HttpRequest]redirect from http://item.jd.com/1950351781.html to http://item.jd.hk/1950351781.html
[cn.edu.hfut.dmic.webcollector.fetcher.Fetcher]-activeThreads=50, spinWaiting=0, fetchQueue.size=12
[cn.edu.hfut.dmic.webcollector.net.HttpRequest]redirect from http://item.jd.com/1950095740.html to http://item.jd.hk/1950095740.html
[cn.edu.hfut.dmic.webcollector.fetcher.Fetcher]-activeThreads=50, spinWaiting=0, fetchQueue.size=12
[cn.edu.hfut.dmic.webcollector.fetcher.Fetcher]-activeThreads=50, spinWaiting=0, fetchQueue.size=12
[cn.edu.hfut.dmic.webcollector.fetcher.Fetcher]-activeThreads=50, spinWaiting=0, fetchQueue.size=12
[cn.edu.hfut.dmic.webcollector.fetcher.Fetcher]fetch http://item.jd.com/1313006459.html
[cn.edu.hfut.dmic.webcollector.fetcher.Fetcher]-activeThreads=50, spinWaiting=0, fetchQueue.size=12
URL:http://item.jd.com/1313006459.html  标题:惠氏启赋较大婴儿和幼儿配方2段奶粉900g新老包装随机发货 1桶装【图片 价格 品牌 报价】-京东闪购
.
.
.
cn.edu.hfut.dmic.webcollector.crawler.Crawler]depth 2 finish: 
	TOTAL urls:	62
	TOTAL time:	19 seconds

控制台打印日志分析:

启动时start(2),咱们输入的参数为2,因此只会爬取2层。

第一层:因为咱们只添加了一个种子连接,故第一次爬取结束后,打印爬取url数量为1,用时2s(符合验证)

第二层:抽取第一层符合过滤规则的全部连接进行爬取,爬取62个符合规则的连接页面,用时19秒,

没有第三层爬取日志(符合验证)

注意:实际中第一层爬取符合过滤规则的连接数会大于62,由于有重复的连接,可是再进行爬取的时候,会自动过滤掉重复连接的爬取(即相同连接只会爬取一次)。

ok,第一个小例子到这就结束了,经过这个例子能够对这个框架的用法有个基本了解了,可是到了这里还不够,由于在爬取的时候发现,只用该框架不能js生成动态网页(例如京东页面上的价格),下一篇会介绍webcollector与两外两个框架(selenium与phantomjs)结合使用爬取js生成的动态网页.

最后一篇会放出源码。