文档章节

Java爬虫框架:SeimiCrawler——结构化解析与数据存储

无极小子
 无极小子
发布于 2016/04/21 00:19
字数 802
阅读 201
收藏 1
点赞 1
评论 0

本文将介绍如何使用SeimiCrawler将页面中信息提取为结构化数据并存储到数据库中,这也是大家非常常见的使用场景。数据抓取将以抓取博客园的博客为例。

建立基本数据结构

为了演示,简单起见只建立一个用来存储博客标题和内容两个主要信息的表即可。表如下:

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;

同时建一个与之对应的Bean对象,如下:

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/
 */
public class BlogContent {
    @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 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);
    }
}

这里面的@Xpath注解要着重介绍下,注解中配置的是针对对应字段的XPath提取规则,后面会介绍到SeimiCrawler会调用Response.render(Class<T> bean)来自动解析填充对应字段。对于开发者而言,提取结构化数据所要做的最主要的工作就在这里,且就这么多,接下来介绍的就是整体上如何串起来的。

实现数据存储

本文演示使用的是paoding-jade,一款人人网早期开源出来的ORM框架。由于SeimiCrawler的对象池以及依赖管理是使用spring来实现的,所以SeimiCrawler天然支持一切可以和spring整合的ORM框架。 要启用Jade需添加pom依赖:

<dependency>
	<groupId>net.paoding</groupId>
	<artifactId>paoding-rose-jade</artifactId>
	<version>2.0.u01</version>
</dependency>
<dependency>
	<groupId>org.apache.commons</groupId>
	<artifactId>commons-dbcp2</artifactId>
	<version>2.1.1</version>
</dependency>
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.37</version>
</dependency>

添加resources下seimi-jade.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

       <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
           <property name="driverClassName" value="com.mysql.jdbc.Driver" />
           <property name="url" value="jdbc:mysql://127.0.0.1:3306/xiaohuo?useUnicode=true&characterEncoding=UTF8&autoReconnect=true&autoReconnectForPools=true&zeroDateTimeBehavior=convertToNull" />
           <property name="username" value="xx" />
           <property name="password" value="xx" />
       </bean>
       <!-- 启用Jade配置 -->
       <bean class="net.paoding.rose.jade.context.spring.JadeBeanFactoryPostProcessor" />
</beans>

编写DAO,

package cn.wanghaomiao.dao;

import cn.wanghaomiao.model.BlogContent;
import net.paoding.rose.jade.annotation.DAO;
import net.paoding.rose.jade.annotation.ReturnGeneratedKeys;
import net.paoding.rose.jade.annotation.SQL;

@DAO
public interface StoreToDbDAO {
    @ReturnGeneratedKeys
    @SQL("insert into blog (title,content,update_time) values (:1.title,:1.content,now())")
    public int save(BlogContent blog);
}

数据存储搞定后接下来就是我们的爬虫规则类了

Crawler

直接上:

package cn.wanghaomiao.crawlers;

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

import java.util.List;

/**
 * 将解析出来的数据直接存储到数据库中
 */
@Crawler(name = "storedb")
public class DatabaseStoreDemo extends BaseSeimiCrawler {
    @Autowired
    private StoreToDbDAO 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) {
            //ignore
        }
    }
    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 blogId = storeToDbDAO.save(blog);
            logger.info("store sus,blogId = {}",blogId);
        } catch (Exception e) {
            //ignore
        }
    }
}

Github上亦有完整的demo,大家可以下载下来,自行尝试,点击直达

© 著作权归作者所有

共有 人打赏支持
无极小子
粉丝 11
博文 7
码字总数 5913
作品 4
朝阳
程序员
[原创]手把手教你写网络爬虫(3):开源爬虫框架对比

原文出处:拓海 介绍 大家好!我们从今天开始学习开源爬虫框架Scrapy,如果你看过《手把手》系列的前两篇,那么今天的内容就非常容易理解了。细心的读者也许会有疑问,为什么不学出身名门的A...

拓海 ⋅ 04/28 ⋅ 0

Java软件开发者,如何学习大数据?(了解一下)大数据的前世今生

Java软件开发者,如何学习大数据? 最近有不少初学者来问到,学习大数据,学习spark,公司主要使用那些语言编写,每听到这一个问题,起码还是很不错的,证明你已经开始学习大数据了,并了解大...

董黎明 ⋅ 06/05 ⋅ 0

2018年Java编程学习面试最全知识点总结

Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于PC、数据中心、游戏控制台、科学超级计算机、移动电话和互...

Java小辰 ⋅ 05/14 ⋅ 0

用 Maven 实现一个 protobuf 的 Java 例子

原文出处:jihite 1. 介绍Protocol Buffers Protocal Buffers(简称protobuf)是谷歌的一项技术,用于结构化的数据序列化、反序列化,常用于RPC 系统(Remote Procedure Call Protocol System...

jihite ⋅ 05/14 ⋅ 0

书单丨5本Java后端技术书指引你快速进阶

一名Java开发工程师 不仅要对Java语言及特性有深层次的理解 而且需要掌握与Java相关的 框架、生态及后端开发知识 本文涉及多种后端开发需要掌握的技能 对于帮助提高开发能力非常有帮助 NO.1...

Java高级架构 ⋅ 05/30 ⋅ 0

java基础io流——配角也风流(不求甚解)

本章简单介绍几个常见的io流派生。 1:数据操作流(操作基本类型数据的流)(理解) 代码示例: 2:内存操作流(理解) 用于处理临时存储信息的,程序结束,数据就从内存中消失。 代码示例: 注:查看...

潇潇漓燃 ⋅ 05/30 ⋅ 0

【目录导航】JAVA零基础进阶之路

【JAVA零基础入门系列】(已完结)导航目录 Day1 开发环境搭建 Day2 Java集成开发环境IDEA Day3 Java基本数据类型 Day4 变量与常量 Day5 Java中的运算符 Day6 Java字符串 Day7 Java输入与输出...

MFrank ⋅ 昨天 ⋅ 0

Java压缩技术(一) ZLib

应好友需要,整理一下Java的压缩算法,先从ZLib开始。 相关链接: Java压缩技术(一) ZLib Java压缩技术(二) ZIP压缩——Java原生实现 Java压缩技术(三) ZIP解压缩——Java原生实现 Ja...

村长大神 ⋅ 2014/11/05 ⋅ 0

ZooKeeper学习笔记八 ZooKeeper典型应用场景——命名服务

《从Paxos到ZooKeeper分布式一致性原理与实践》 电子工业出版社 命名服务是分布式系统中比较常见的一类场景。命名服务是分布式系统最基本的公共服务之一。在分布式系统中,被命名的实体通常可...

xundh ⋅ 05/02 ⋅ 0

为什么Python成了开发AI的主流语言?Java系列、Ruby等编程语言不行吗?

导读 说道开发AI的语言,很多人说,不是Python吗?没错,Python是可以开发AI,但是不知道你们有没有想过,真的就只有Python才可以开发AI吗? 其实,像Java、Ruby也是开发AI很好的选择,但是为...

柯西带你学编程 ⋅ 05/29 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

R计算IV

参考文章 #读取文件 rawdata = read.csv("/path/to/csv/file",header=T) colnames(rawdata)[18] <- "y" //重命名因变量y #数据分区 训练集测试集 trainIdx <- sample(nrow(rawdata), round(......

火力全開 ⋅ 20分钟前 ⋅ 0

SQL老司机,在SQL中计算 array & map & json数据

摘要: 场景 通常,我们处理数据,一列数据类型要么是字符串,要么是数字,这些都是primitive类型的数据。 场景 通常,我们处理数据,一列数据类型要么是字符串,要么是数字,这些都是primi...

阿里云云栖社区 ⋅ 20分钟前 ⋅ 0

SQL老司机,在SQL中计算 array & map & json数据

摘要: 场景 通常,我们处理数据,一列数据类型要么是字符串,要么是数字,这些都是primitive类型的数据。 场景 通常,我们处理数据,一列数据类型要么是字符串,要么是数字,这些都是primi...

猫耳m ⋅ 30分钟前 ⋅ 0

关于ireport自定义变量类型为list的时候

自己摸石头过河,我真的应该去趟市中心图书馆,借本真正靠谱的教材 网上的东西,只有0.01%是有用的,还有0.99%是垃圾,剩下的99%是垃圾的复制品。。 哎!~ 问题是这样的,报表带sql,从db中获...

炑炑milina ⋅ 31分钟前 ⋅ 0

Spring mvc ContextLoaderListener 原理解析

对于熟悉Spring MVC功能,首先应从web.xml 开始,在web.xml 文件中我们需要配置一个监听器 ContextLoaderListener,如下。 <!-- 加载spring上下文信息,最主要的功能是解析applicationContex...

轨迹_ ⋅ 32分钟前 ⋅ 0

阿里云发布企业数字化及上云外包平台服务:阿里云众包平台

摘要: 阿里云正式发布旗下众包平台业务(网址:https://zhongbao.aliyun.com/),支持包括:网站定制开发,APP、电商系统等软件开发,商标、商品LOGO、VI、产品包装设计、营销推广、大数据人...

阿里云官方博客 ⋅ 33分钟前 ⋅ 0

Redis安装异常解决办法

官网地址:http://redis.io/ 官网下载地址:http://redis.io/download 1. 下载Redis源码(tar.gz),并上传到Linux 2. 解压缩包:tar zxvf redis-2.8.17.tar.gz 3. 进入解压缩后的文件夹:c...

slagga ⋅ 38分钟前 ⋅ 0

006. 深入JVM学习—年轻代

1. 年轻代图片 年轻代(Young)属于JVM堆内存空间的一个组成部分 所有使用关键字new新实例化的对象一定会在伊甸园区进行保存,而对于存活区保存的一定是已经在伊甸园区存在一段时间并且经过了...

影狼 ⋅ 39分钟前 ⋅ 0

如何成为一个合格的程序员

偶尔的,我会被人问道:如何成为一名优秀的程序员,更或者,如何成为一名程序员。每次人们问起,我都力图给出不同的答案。因此,我的答案是各种各样的。下面就是我认为的成为一名优秀的程序员...

柳猫 ⋅ 40分钟前 ⋅ 0

cups error_log日志暴增

日志内容 File \"/usr/lib/cups/notifier/dbus\" has insecure permissions 解决(未验证适用范围) sudo service cups stopsudo rm /etc/cups/subscriptions.conf*sudo rm -r /var/cac......

一介码夫_Hum ⋅ 43分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部