Seimi基础系列2-SeimiCrawler整合Mybatis存储数据

本文介绍如何将SeimiCrawler与Mybatis进行整合,实现从网页抓取数据并存储到数据库的过程。SeimiCrawler是一款Java爬虫框架,能够简化爬虫系统的开发流程。文中详细展示了依赖配置、数据表结构定义、Model对象创建及Mybatis配置等步骤。

最近关注SeimiCrawler整合Mybatis的朋友比较多,故仅以此文抛砖引玉。如果是不了解SeimiCrawler的朋友也可以通过此文简单了解下SeimiCrawler

SeimiCrawler简介

SeimiCrawler是一个敏捷的,独立部署的,支持分布式的Java爬虫框架,希望能在最大程度上降低新手开发一个可用性高且性能不差的爬虫系统的门槛,以及提升开发爬虫系统的开发效率。在SeimiCrawler的世界里,绝大多数人只需关心去写抓取的业务逻辑就够了,其余的Seimi帮你搞定。设计思想上SeimiCrawler受Python的爬虫框架Scrapy启发,同时融合了Java语言本身特点与Spring的特性,并希望在国内更方便且普遍的使用更有效率的XPath解析HTML,所以SeimiCrawler默认的HTML解析器是JsoupXpath(独立扩展项目,非jsoup自带),默认解析提取HTML数据工作均使用XPath来完成(当然,数据处理亦可以自行选择其他解析器)。并结合SeimiAgent彻底完美解决复杂动态页面渲染抓取问题。

项目源码

Github托管

下面正式开始整合Mybatis的内容。数据库以MySQL为例。

依赖

<dependency>
	<groupId>cn.wanghaomiao</groupId>
          <artifactId>SeimiCrawler</artifactId>
          <version>1.2.0</version>
</dependency>
<dependency>
	<groupId>org.apache.commons</groupId>
	<artifactId>commons-dbcp2</artifactId>
	<version>2.1.1</version>
</dependency>
<dependency>
	<groupId>org.apache.commons</groupId>
	<artifactId>commons-pool2</artifactId>
	<version>2.4.2</version>
</dependency>
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.37</version>
</dependency>
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis-spring</artifactId>
	<version>1.3.0</version>
</dependency>
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis</artifactId>
	<version>3.4.1</version>
</dependency>

数据表结构

假设建有数据库,库名为xiaohuo,内含表结构如下:

CREATE TABLE `blog` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(300) DEFAULT NULL,
  `content` text,
  `update_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

对应的Model对象

package cn.wanghaomiao.model;

import cn.wanghaomiao.seimi.annotation.Xpath;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;

/**
 * Xpath语法可以参考 http://jsoupxpath.wanghaomiao.cn/
 * @since 2015/10/27.
 */
public class BlogContent {
    private Integer id;

    @Xpath("//h1[@class='postTitle']/a/text()|//a[@id='cb_post_title_url']/text()")
    private String title;

    //也可以这么写 @Xpath("//div[@id='cnblogs_post_body']//text()")
    @Xpath("//div[@id='cnblogs_post_body']/allText()")
    private String content;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public String toString() {
        if (StringUtils.isNotBlank(content)&&content.length()>100){
            //方便查看截断下
            this.content = StringUtils.substring(content,0,100)+"...";
        }
        return ToStringBuilder.reflectionToString(this);
    }
}

整合Mybatis的配置文件

  • resources下添加 mybatis-config.xml文件

一些基本的全局设置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
</configuration>
  • resources下添加seimi-mybatis.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">


    <context:annotation-config />
    <bean id="mybatisDataSource" class="org.apache.commons.dbcp2.BasicDataSource">
        <property name="driverClassName" value="${database.driverClassName}"/>
        <property name="url" value="${database.url}"/>
        <property name="username" value="${database.username}"/>
        <property name="password" value="${database.password}"/>
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" abstract="true">
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>
    <bean id="seimiSqlSessionFactory" parent="sqlSessionFactory">
        <property name="dataSource" ref="mybatisDataSource"/>
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="cn.wanghaomiao.dao.mybatis"/>
        <property name="sqlSessionFactoryBeanName" value="seimiSqlSessionFactory"/>
    </bean>
</beans>

配置文件中的${database.driverClassName}是由于SeimiCrawler的demo工程还有动态配置的相关设置,此处亦可直接写死,不必再读其他配置。

  • cn.wanghaomiao.dao.mybatis目录下添加DAO
package cn.wanghaomiao.dao.mybatis;

import cn.wanghaomiao.model.BlogContent;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;

/**
 * @since 2016/7/27.
 */
public interface MybatisStoreDAO {

    @Insert("insert into blog (title,content,update_time) values (#{blog.title},#{blog.content},now())")
    @Options(useGeneratedKeys = true, keyProperty = "blog.id")
    int save(@Param("blog") BlogContent blog);
}

至此,Mybatis部分的已经就绪了。

使用

package cn.wanghaomiao.crawlers;

import cn.wanghaomiao.dao.mybatis.MybatisStoreDAO;
import cn.wanghaomiao.model.BlogContent;
import cn.wanghaomiao.seimi.annotation.Crawler;
import cn.wanghaomiao.seimi.def.BaseSeimiCrawler;
import cn.wanghaomiao.seimi.struct.Request;
import cn.wanghaomiao.seimi.struct.Response;
import cn.wanghaomiao.xpath.model.JXDocument;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * 将解析出来的数据直接存储到数据库中,整合mybatis实现
 *
 * @author 汪浩淼 [et.tw@163.com]
 * @since 2016/07/27.
 */
@Crawler(name = "mybatis")
public class DatabaseMybatisDemo extends BaseSeimiCrawler {
    @Autowired
    private MybatisStoreDAO storeToDbDAO;

    @Override
    public String[] startUrls() {
        return new String[]{"http://www.cnblogs.com/"};
    }

    @Override
    public void start(Response response) {
        JXDocument doc = response.document();
        try {
            List<Object> urls = doc.sel("//a[@class='titlelnk']/@href");
            logger.info("{}", urls.size());
            for (Object s : urls) {
                push(Request.build(s.toString(), "renderBean"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void renderBean(Response response) {
        try {
            BlogContent blog = response.render(BlogContent.class);
            logger.info("bean resolve res={},url={}", blog, response.getUrl());
            //使用神器paoding-jade存储到DB
            int changeNum = storeToDbDAO.save(blog);
            int blogId = blog.getId();
            logger.info("store success,blogId = {},changeNum={}", blogId, changeNum);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

接下来简单启动下,

public class Boot {
    public static void main(String[] args){
        Seimi s = new Seimi();
        s.start("mybatis");
    }
}

可以看到如下日志:

00:25:18 INFO  c.w.crawlers.DatabaseMybatisDemo - store success,blogId = 257,changeNum=1
00:25:18 INFO  c.w.crawlers.DatabaseMybatisDemo - bean resolve res=cn.wanghaomiao.model.BlogContent@3edc08c3[id=<null>,title=CoordinatorLayout自定义Bahavior特效及其源码分析CoordinatorLayout自定义Bahavior特效及其源码分析,content=@[CoordinatorLayout, Bahavior] CoordinatorLayout是android support design包中可以算是最重要的一个东西,运用它可以做出一些不错的特效...],url=http://www.cnblogs.com/soaringEveryday/p/5711545.html
00:25:18 INFO  c.w.crawlers.DatabaseMybatisDemo - store success,blogId = 258,changeNum=1
00:25:18 INFO  c.w.crawlers.DatabaseMybatisDemo - store success,blogId = 259,changeNum=1
00:25:18 INFO  c.w.crawlers.DatabaseMybatisDemo - store success,blogId = 260,changeNum=1

整合完毕!

后记

生产环境工程打包部署以及启动,推荐使用maven-seimicrawler-plugin打包插件,详细请继续参阅maven-seimicrawler-plugin或是“Seimi基础系列1-SeimiCrawler打包部署工具使用”。

完整的Demo工程地址

完整版demo

转载于:https://my.oschina.net/u/589889/blog/719305

SeimiCrawler An agile,powerful,standalone,distributed crawler framework. SeimiCrawler的目标是成为Java里最实用的爬虫框架,大家一起加油。 简介 SeimiCrawler是一个敏捷的,独立部署的,支持分布式的Java爬虫框架,希望能在最大程度上降低新手开发一个可用性高且性能不差的爬虫系统的门槛,以及提升开发爬虫系统的开发效率。在SeimiCrawler的世界里,绝大多数人只需关心去写抓取的业务逻辑就够了,其余的Seimi帮你搞定。设计思想上SeimiCrawler受Python的爬虫框架Scrapy启发,同时融合了Java语言本身特点与Spring的特性,并希望在国内更方便且普遍的使用更有效率的XPath解析HTML,所以SeimiCrawler默认的HTML解析器是JsoupXpath(独立扩展项目,非jsoup自带),默认解析提取HTML数据工作均使用XPath来完成(当然,数据处理亦可以自行选择其他解析器)。并结合SeimiAgent彻底完美解决复杂动态页面渲染抓取问题。 号外 2016.04.14 用于实现浏览器级动态页面渲染以及抓取的SeimiAgent已经发布。SeimiAgent基于Qtwebkit开发,主流浏览器内核(chrome,safari等),可在服务器端后台运行,并通过http协议发布对外调用API,支持任何语言或框架从SeimiAgent获取服务,彻底的解决动态页面渲染抓取等问题。具体可以参考SeimiAgent主页。SeimiCrawler已经在v0.3.0中内置支持SeimiAgent的使用并添加了demo,具体请查看demo或是官方文档。 2016.01.05 专门为SeimiCrawler工程打包部署的maven-seimicrawler-plugin已经发布可用,详细请继续参阅maven-seimicrawler-plugin或是下文工程化打包部署章节。
SeimiCrawler An agile,powerful,distributed crawler framework. SeimiCrawler的目标是成为Java世界最好用最实用的爬虫框架。简介      SeimiCrawler是一个敏捷的,支持分布式的Java爬虫开发框架,希望能在最大程度上降低新手开发一个可用性高且性能不差的爬虫系统的门槛,以及提升开发爬虫系统的开发效率。在SeimiCrawler的世界里,绝大多数人只需关心去写抓取的业务逻辑就够了,其余的Seimi帮你搞定。设计思想上SeimiCrawler受Python的爬虫框架Scrapy启发很大,同时融合了Java语言本身特点与Spring的特性,并希望在国内更方便且普遍的使用更有效率的XPath解析HTML,所以SeimiCrawler默认的HTML解析器是JsoupXpath,默认解析提取HTML数据工作均使用XPath来完成(当然,数据处理亦可以自行选择其他解析器)。 原理示例基本原理集群原理快速开始 添加maven依赖(已经同步到中央maven库,最新版本参见项目主页):     cn.wanghaomiao     SeimiCrawler     0.1.0 在包crawlers下添加爬虫规则,例如:@Crawler(name = "basic") public class Basic extends BaseSeimiCrawler {     @Override     public String[] startUrls() {         return new String[]{"http://www.cnblogs.com/"};     }     @Override     public void start(Response response) {         JXDocument doc = response.document();         try {             List<Object> urls = doc.sel("//a[@class='titlelnk']/@href");             logger.info("{}", urls.size());             for (Object s:urls){                 push(new Request(s.toString(),"getTitle"));             }         } catch (Exception e) {             e.printStackTrace();         }     }     public void getTitle(Response response){         JXDocument doc = response.document();         try {             logger.info("url:{} {}", response.getUrl(), doc.sel("//h1[@class='postTitle']/a/text()|//a[@id='cb_post_title_url']/text()"));             //do something         } catch (Exception e) {             e.printStackTrace();         }     } } 然后随便某个包下添加启动Main函数,启动SeimiCrawler:public class Boot {     public static void main(String[] args){         Seimi s = new Seimi();         s.start("basic");     } } 以上便是一个最简单的爬虫系统开发流程。 更多文档      目前可以参考demo工程中的样例,基本包含了主要的特性用法。更为细致的文档移步SeimiCrawler主页中进一步查看 标签:爬虫
SeimiCrawler一个敏捷强大的Java爬虫框架 •1.简介 •2.需要 •3.快速开始 ◦3.1.maven依赖 ◦3.2.在SpringBoot中 ◦3.3.常规用法 •4.原理 ◦4.1.基本原理 ◦4.2.集群原理 •5.如何开发 ◦5.1.约定 ◦5.2.第一个爬虫规则类-crawler ◾5.2.1.注解@Crawler ◾5.2.2.实现startUrls() ◾5.2.3.实现start(Response response) ◾5.2.4.Response数据提取 ◾5.2.4.1.内部属性一览 ◾5.2.5.回调函数 ◾5.2.6.Request内部一览 ◾5.2.7.自定义UserAgent(可选) ◾5.2.8.启用cookies(可选) ◾5.2.9.启用proxy(可选) ◾5.2.10.设置delay(可选)* ◾5.2.11.设置请求URL白名单匹配规则 ◾5.2.12.设置请求URL黑名单匹配规则 ◾5.2.13.设置动态代理 ◾5.2.14.是否开启系统去重 ◾5.2.15.关于自动跳转 ◾5.2.16.异常请求处理 ◾5.2.17.SeimiAgent支持 ◾5.2.17.1.基本配置 ◾5.2.17.1.1.直接运行 ◾5.2.17.1.2.SpringBoot项目 ◾5.2.17.2.使用 ◾5.2.18.启动爬虫系统 ◾5.2.18.1.SpringBoot(推荐) ◾5.2.18.2.直接运行,独立启动 ◦5.3.工程化打包部署 ◾5.3.1.SpringBoot(推荐) ◾5.3.2.独立直接运行 ◦5.4.定时调度 ◦5.5.自动解析Bean ◾5.5.1.注解@Xpath ◾5.5.2.使用 ◦5.6.拦截器 ◾5.6.1.注解@Interceptor ◾5.6.2.接口SeimiInterceptor ◾5.6.3.拦截器样例 ◦5.7.关于SeimiQueue ◾5.7.1.配置使用DefaultRedisQueue ◾5.7.1.1.SpringBoot项目 ◾5.7.1.2.直接运行(非SpringBoot) ◾5.7.2.自行实现SeimiQueue ◾5.7.3.SeimiQueue样例 ◦5.8.集成主流数据持久化 ◾5.8.1.准备工作 ◾5.8.2.写一个DAO ◾5.8.3.开始使用DAO ◦5.9.分布式 ◦5.10.通过http服务接口操作 ◾5.10.1.Request必填参数 ◾5.10.2.SpringBoot(推荐) ◾5.10.3.直接运行 ◾5.10.3.1.发送抓取请求 ◾5.10.3.2.接口描述 ◾5.10.3.3.查看抓取状态 •6.常见问题汇总 ◦6.1.如何设置网络代理 ◦6.2.如何开启cookie ◦6.3.如何启用分布式模式 ◾6.3.1.参考 ◾6.3.2.特别注意 ◦6.4.如何设置复杂的起始请求 •7.社区讨论 •8.项目源码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值