以前也用过爬虫,比如使用nutch爬取指定种子,基于爬到的数据做搜索,还大致看过一些源码。当然,nutch对于爬虫考虑的是十分全面和细致的。每当看到屏幕上唰唰过去的爬取到的网页信息以及处理信息的时候,总感觉这很黑科技。正好这次借助梳理Spring MVC的机会,想自己弄个小爬虫,简单没关系,有些小bug也无所谓,我需要的只是一个能针对某个种子网站能爬取我想要的信息就可以了。有Exception就去解决,可能是一些API使用不当,也可能是遇到了http请求状态异常,又或是数据库读写有问题,就是在这个报exception和解决exception的过程中,JewelCrawler(儿子的小名)已经可以能够独立的爬取数据,并且还有一项基于Word2Vec算法做个情感分析的小技能。

  后面可能还会有未知的Exception等着解决,也有一些性能需要优化,比如和数据库的交互,数据的读写等等。但是目测年内没有太多精力放这上面了,所以今天做一个简单的总结,而且前两篇主要侧重的是功能和结果,这篇来说说JewelCrawler是如何诞生的,并将代码放到Github上(源码地址在文章最后),有兴趣的可以关注下(仅供交流学习,请勿他用,考虑下douban君。多一点真诚,少一点伤害)

 

环境介绍

  开发工具:Intellij idea 14

  数据库: Mysql 5.5 + 数据库管理工具Navicat(可用来连接查询数据库)

seo优化培训,网络推广培训,网络营销培训,SEM培训,网络优化,在线营销培训

  语言:Java

  Jar包管理:Maven

  版本管理:Git

 

目录结构

seo优化培训,网络推广培训,网络营销培训,SEM培训,网络优化,在线营销培训

 

  其中

  com.ansj.vec是Word2Vec算法的Java版本实现

  com.jackie.crawler.doubanmovie是爬虫实现模块,其中又包括

seo优化培训,网络推广培训,网络营销培训,SEM培训,网络优化,在线营销培训

  有些包是空的,因为这些模块还没有用上,其中

    constants包是存放常量类

    crawl包存放爬虫入口程序

    entity包映射数据库表的实体类

    test包存放测试类

    utils包存放工具类

 

  resource模块存放的是配置文件和资源文件,比如

    beans.xml:Spring上下文的配置文件

    seed.properties:种子文件

    stopwords.dic:停用词库

    comment12031715.txt:爬取的短评数据

    tokenizerResult.txt:使用IKAnalyzer分词后的结果文件

    vector.mod:基于Word2Vec算法训练的模型数据

 

  test模块是测试模块,用于编写UT.

 

数据库配置

  1. 添加依赖的包

  JewelCrawler使用的maven管理,所以只需要在pom.xml中添加相应的依赖就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>4.1.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>commons-pool</groupId>
    <artifactId>commons-pool</artifactId>
    <version>1.6</version>
</dependency>
<dependency>
    <groupId>commons-dbcp</groupId>
    <artifactId>commons-dbcp</artifactId>
    <version>1.4</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.38</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.38</version>
</dependency>

  

  2. 声明数据源bean

  我们需要在beans.xml中声明数据源的bean

1
2
3
4
5
6
7
<context:property-placeholder location="classpath*:*.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

注意: 这里是绑定了外部配置文件jdbc.properties,具体数据源的参数从该文件读取。

  如果遇到问题“SQL [insert into user(id) values(?)]; Field 'name' doesn't  have a default value;”解决方法是设置表的相应字段为自增长字段。

 

解析页面遇到的问题

  对于爬到的网页数据需要解析dom结构,拿到自己想要的数据,期间遇到如下错误

  org.htmlparser.Node不识别

  解决方法:添加jar包依赖

1
2
3
4
5
<dependency>
    <groupId>org.htmlparser</groupId>
    <artifactId>htmlparser</artifactId>
    <version>1.6</version>
</dependency>

  

  org.apache.http.HttpEntity不识别

  解决方法:添加jar包依赖

1
2
3
4
5
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.2</version>
</dependency> 

  当然这是期间遇到的问题,最后用的是Jsoup做的页面解析。

 

maven仓库下载速度慢

  之前使用的是默认的maven中央仓库,下载jar包的速度很慢,不知道是我的网络问题还是其他原因,后来在网上找到了阿里云的maven仓库,更新后,相比之前简直是秒下,吐血推荐。

1
2
3
4
5
6
7
8
<mirrors>
    <mirror>
      <id>alimaven</id>
      <name>aliyun maven</name>
      <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
      <mirrorOf>central</mirrorOf>       
    </mirror>
</mirrors>

  找到maven的settings.xml文件,添加这个镜像即可。

 

读取resource模块下文件的一种方法

  比如读取seed.properties文件

1
2
3
4
5
@Test
    public void testFile(){
        File seedFile = new File(this.getClass().getResource("/seed.properties").getPath());
        System.out.print("===========" + seedFile.length() + "===========" );
    }

  

有关正则表达式

  使用regrex正则表达式的时候,如果匹配上了定义的Pattern,则需要先调用matcher的find方法然后才能使用group方法找到子串。直接调用group方法是没有办法找到你想要的结果的。

  我看了下上面Matcher类的源码

+ View Code

  原因是这样的:这里如果不先调用find方法,直接调用group,可以发现group方法调用group(int group),该方法的方法体中有if first<0,显然这里这个条件是成立的,因为first的初始值就是-1,所以这里会抛异常。但是如果调用find方法,可以发现,最终会调用search(nextSearchIndex),注意这里的nextSearchIndex已被last赋值,而last的值为0,再跳转到search方法中

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
boolean search(int from) {
    this.hitEnd = false;
    this.requireEnd = false;
    from        = from < 0 0 : from;
    this.first  = from;
    this.oldLast = oldLast < 0 ? from : oldLast;
    for (int i = 0; i < groups.length; i++)
        groups[i] = -1;
    acceptMode = NOANCHOR;
    boolean result = parentPattern.root.match(this, from, text);
    if (!result)
        this.first = -1;
    this.oldLast = this.last;
    return result;
}

  这个nextSearchIndex传给了from,而from在方法体中被赋值给了first,所以,调用了find方法之后,这个的first就不在是-1,也就不是抛异常了。

 

  源码已经上传至Github:https://github.com/DMinerJackie/JewelCrawler

 

  以上说的问题比较碎,都是在遇到问题和解决问题的时候的一些总结。在具体操作的时候还会遇到其他问题,有问题或者建议的话欢迎提出来^^。

  最后放几张截止目前爬取的数据

  Record表

seo优化培训,网络推广培训,网络营销培训,SEM培训,网络优化,在线营销培训

 

  其中存储的是79032条,爬取过的网页有48471条

 

  movie表

seo优化培训,网络推广培训,网络营销培训,SEM培训,网络优化,在线营销培训

 

  目前爬取了2964部影视作品

 

  comments表

seo优化培训,网络推广培训,网络营销培训,SEM培训,网络优化,在线营销培训

 

  爬取了29711条记录

 

  如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。


延伸阅读

向着Java前进-Java培训,做最负责任的教育,学习改变命运,软件学习,再就业,大学生如何就业,帮大学生找到好工作,lphotoshop培训,电脑培训,电脑维修培训,移动软件开发培训,网站设计培训,网站建设培训向着Java前进