AJSprider

github: https://github.com/zhuchangwu/AJSpider

概述

AJSprider是笔者基于Reactor线程模式+Jsoup+HttpClient封装的一款轻量级java多线程网络爬虫框架,简单上手,小白也能玩爬虫,

使用本框架,只需要关注如何解析(提供了无脑的匹配取值方法),而不必关心线程的调度,源码的下载;

本项目仅供学习使用,禁止任何人用它非法盈利

目前是第一册测试版本,可以使用,后续会进行规整,简化使用

坐标

<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories> <dependency>
<groupId>com.github.zhuchangwu</groupId>
<artifactId>AJSpider</artifactId>
<version>1.0.0.SNAPSHOT</version>
</dependency>

使用说明

使用方法简单的没商量,三步打完收工

  • 在自己的项目中引入坐标
  • 继承SpiderSingleThreadExecutor<T>实现它的抽象方法
  • 在main方法,创建启动器类SpiderBootStrap完成爬虫的启动

ok,现在进行第二步,重写SpiderSingleThreadExecutor<T>的抽象方法,他有两个抽象方法,子类必须实现,如下:

解析:

  • 入参1: var1是框架根据url下载下来的String类型的html的源码,需要用户把这里面需要的属性从html中解析下来封装进新创建的java对象中
  • 入参2: var2是框架自定义的容器,里面存放着两个集合
    • 集合1: 盛放用户在第一步新创建的对象并且已经付好值的对象
    • 集合2: 盛放需要下载二级任务 (比如,拿新闻来说,新闻的标题在url1上,点击标题查看新闻体进入的新的url算作是二级任务)
  • 入参3: 框架提供的工具类,辅助第一步的解析
  • 返回值: 将入参位置的容器返回
protected abstract SpiderSingleThreadExecutor<T>.SpiderContainer<T> resolution1(String var1, SpiderSingleThreadExecutor<T>.SpiderContainer<T> var2, SpiderResolutionUtil var3);

解析拓展:

如果用户存在二级任务,需要用户重写SpiderSingleThreadExecutorresolution2,使用方式和resolution1相同

  • 入参1: 存放的是 根据用户在resolution1()中放入容器的url集合批量下载的对应的html源码
  • 入参2: spiderContainer是用户在resolution1()中返回的容器
  • 入参3: 工具类,辅助用户将入参1html数组中的源码,解析进容器中的bean集合中
  • 返回值: 将入参2返回
protected SpiderSingleThreadExecutor<T>.SpiderContainer<T> resolution2(String[] htmls, SpiderSingleThreadExecutor<T>.SpiderContainer<T> spiderContainer, SpiderResolutionUtil util) {
}

持久化

  • 入参1: 是用户自己解析并封装的容器中的bean集合
  • 入参2: 工具类,辅助持久化
public abstract void persistence(List<T> var1, PersistenceUtil<T> var2);

启动爬虫

创建启动器对象

  • 添加任务队列
  • 初始化线程执行器组
    • 入参1: 开启的线程数(不填,默认是2*CPU核数)
    • 入参2: 用户自定义的SpiderSingleThreadExecutor的实现类
new SpiderBootStrap()
.setTaskUrlQueue(taskQueue)
.initThreadExcutorGroup(1,MyExecutor.class)
.build();

完整Demo-拉取新闻

快捷键F12,观察需要爬取的网页的源码,DIY解析过程(使用提供的辅助类基础的解析都ok,当然你是一个正则大牛,按自己的解析方式也很好)

public class MyThreadExcutor extends SpiderSingleThreadExecutor<News> {

protected SpiderContainer<News> resolution1(String s, SpiderContainer<News> spiderContainer, SpiderResolutionUtil spiderResolutionUtil) {

    // 观察上图,我需要的新闻信息在一个id为wp_news_w6的div下
// 选择如下方法,根据id以及标签名获取出li的数组
String[] lis = spiderResolutionUtil.getElementsByIdAndTaggetName(s, "wp_news_w6", "ul", "li"); // 大家一定要注意, 解析的步骤是一遍遍历上面的数组,一遍解析它,每次循环都创建一个新的对象盛放解析出来的字段
for (int i = 0; i < lis.length; i++) {
String html = lis[i];
News qluNew = new News(); // 使用工具方法,把用户提供的 前后缀 之间的值取出来
// 注意了, 这里的前后缀一定得是先把源码输出到控制台,再复制过来
String title = spiderResolutionUtil.getValueByPrefixSuffix(html, "\">", "</a></span> <span class=");
String time = spiderResolutionUtil.getValueByPrefixSuffix(html, "<span class=\"news_meta\">", "</span> </li>");
String url1 = spiderResolutionUtil.getValueByPrefixSuffix(html, "class=\"news_title\"><a href=\"", "\" target=\"_blank\""); // 将解析出来的值存放在用户创建出来的对象中
qluNew.setTitle(title);
qluNew.setDate(time); // 大家可以看到上面的图片,只用标题,时间,新闻体在二级url中,需要用户在这里完成拼接
String perfix = "http://www.qlu.edu.cn";
String targetUrl = perfix + url1; // 推荐大家在解析拼接二级url时多加几层判断,保证二级url的正确性
// 我们学校的新闻模块,就存在使用中不同的url的现象, 拼接出来的效果是这个样"http://www.qlu.edu.cnhttp://2019sdh.qlu.edu.cn/2019/0305/c7334a122472/page.htm";
// 当时也挺蒙的,不过在700条新闻中,大概存在5条
// 我的处理是直接跳过这个url, 如果不处理,框架根据错误的url下载,解析就终止了
if (targetUrl.substring(5, targetUrl.length()).contains("http:")) {
// 说明上面的url拼接从新处理这个 url
// url不同很大程度上意味着 resolution2() 按照不同的模板解析
continue;
} //最后,别忘了创建的bean添加的容器中,往后传播
// 第一步
spiderContainer.getBeanList().add(qluNew);
// 第二步
spiderContainer.getUrlList().add(perfix + url1); }
// 返回容器
return spiderContainer;
}

resolution2() 并不是抽象方法,只有当存在二级任务时,用户选择实现

    @Override
protected SpiderContainer<News> resolution2(String[] htmltxt, SpiderContainer<News> spiderContainer, SpiderResolutionUtil util) { // 遍历入参1中的下载好了的源码, 从新解析出新闻体的新的字段放入容器中的bean集合
for (int i = 0; i < htmltxt.length; i++) {
String body = util.getFirstElementValueByClass(htmltxt[i], "wp_articlecontent");
spiderContainer.getBeanList().get(i).setBody(body);
} for (News news : spiderContainer.getBeanList()) {
System.out.println("Thread.name = "+Thread.currentThread().getName()+news);
}
// 返回容器
return spiderContainer;
}

持久化,用户根据自己的需求,选择如何持久化, list中存放的是前面用户解析出来的bean的集合


// persistenceUtil可以持久化图片到本地,前提是bean中仅有一个图片的url字段
public void persistence(List<News> list, PersistenceUtil<News> persistenceUtil) { }
}

启动:

public static void main(String[] args) {

    // 创建任务队列, 任意队列都可以,不要求线程安全
LinkedBlockingQueue<String> taskQueue = new LinkedBlockingQueue();
// 假设在准备任务
String url ="http://www.xxx.edu.cn/38/list.htm";
taskQueue.offer(url); for (int i=2;i<50;i++){
String url2 = "http://www.xxx.edu.cn/38/list"+i+".htm";
taskQueue.offer(url2);
} SpiderBootStrap spiderBootStrap = new SpiderBootStrap();
spiderBootStrap
.initThreadExcutorGroup(10,MyThreadExcutor.class)
.setTaskUrlQueue(taskQueue)
.build();
}

重要的事情说三遍

使用工具方法,需要的 前后缀 是需要从编译器的控制台复制过来的,直接赋值网页上的无效

使用工具方法,需要的 前后缀 是需要从编译器的控制台复制过的,直接赋值网页上的无效

使用工具方法,需要的 前后缀 是需要从编译器的控制台复制过的,直接赋值网页上的无效


笔者水平有限,请大佬批评指教!, 有任何issue请联系笔者, 如果您觉得还不错,欢迎star

[原创]一款基于Reactor线程模型的java网络爬虫框架的更多相关文章

  1. 【Netty源码分析】Reactor线程模型

    1. 背景 1.1. Java线程模型的演进 1.1.1. 单线程 时间回到十几年前,那时主流的CPU都还是单核(除了商用高性能的小机),CPU的核心频率是机器最重要的指标之一. 在Java领域当时比 ...

  2. Netty Reactor 线程模型笔记

    引用: https://www.cnblogs.com/TomSnail/p/6158249.html https://www.cnblogs.com/heavenhome/articles/6554 ...

  3. Netty高性能之Reactor线程模型

    Netty是一个高性能.异步事件驱动的NIO框架,它提供了对TCP.UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作都是异步非阻塞的,通过Future-Listener机制,用 ...

  4. Reactor 线程模型以及在netty中的应用

    这里我们需要理解的一点是Reactor线程模型是基于同步非阻塞IO实现的.对于异步非阻塞IO的实现是Proactor模型. 一 Reactor 单线程模型 Reactor单线程模型就是指所有的IO操作 ...

  5. 网络编程NIO之Reactor线程模型

    目录 单Reactor线程模型 基于工作线程的Reactor线程模型 多Reactor线程模型 多Reactor线程模型示例 结束语 上篇文章中写了一些NIO相关的知识以及简单的NIO实现示例,但是示 ...

  6. 深入Netty逻辑架构,从Reactor线程模型开始

    本文是Netty系列第6篇 上一篇文章我们从一个Netty的使用Demo,了解了用Netty构建一个Server服务端应用的基本方式.并且从这个Demo出发,简述了Netty的逻辑架构,并对Chann ...

  7. Netty源码分析之Reactor线程模型详解

    上一篇文章,分析了Netty服务端启动的初始化过程,今天我们来分析一下Netty中的Reactor线程模型 在分析源码之前,我们先分析,哪些地方用到了EventLoop? NioServerSocke ...

  8. Java网络编程和NIO详解3:IO模型与Java网络编程模型

    Java网络编程和NIO详解3:IO模型与Java网络编程模型 基本概念说明 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32 ...

  9. 基于java的网络爬虫框架(实现京东数据的爬取,并将插入数据库)

    原文地址http://blog.csdn.net/qy20115549/article/details/52203722 本文为原创博客,仅供技术学习使用.未经允许,禁止将其复制下来上传到百度文库等平 ...

随机推荐

  1. Hive 学习之路(五)—— Hive 分区表和分桶表

    一.分区表 1.1 概念 Hive中的表对应为HDFS上的指定目录,在查询数据时候,默认会对全表进行扫描,这样时间和性能的消耗都非常大. 分区为HDFS上表目录的子目录,数据按照分区存储在子目录中.如 ...

  2. mysql group_concat的长度问题

    mysql group_concat的长度问题 show variables like 'group_concat_max_len';+----------------------+-------+| ...

  3. python多线程爬取图片实例

    今天试着把前面那个爬取图片的爬虫改成了多线程爬取,虽然最后可以爬取存储图片了,但仍存在一些问题.网址还是那个网址https://www.quanjing.com/category/1286521/1. ...

  4. Java程序运行原理分析

    class文件内容 class文件包含Java程序执行的字节码 数据严格按照格式紧凑排列在class文件的二进制流,中间无分割符 文件开头有一个0xcafebabe(16进制)特殊的标志 JVM运行时 ...

  5. python中的 == 和 is 的区别

    == 比较的是两边的值 is 比较的是两边的内存地址  通过 id()获取内存地址 小数据池:我们使用过的值存储在小数据池中,供其他数据使用. 小数据池仅限于 数字 和 字符串: 数字的小数池范围  ...

  6. Cow Exhibition POJ - 2184

    题目地址:https://vjudge.net/problem/POJ-2184 下面的解释是从一个大佬那搬来的,讲的很清楚题意:给定一些奶牛,每个牛有s和f两个属性值,有正有负,要求选出一些牛,使得 ...

  7. MySQL Explain查看执行计划

    这篇博客写的还是蛮全的 http://www.cnblogs.com/songwenjie/p/9409852.html 在执行SQL的时候,经常有些SQL未正确使用到索引,导致程序变慢,通过使用ex ...

  8. bzoj3125: CITY 题解

    3125: CITY Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 486  Solved: 213[Submit][Status][Discuss] ...

  9. CAD2014学习笔记-文字编辑与尺寸标注

    基于 虎课网huke88.com CAD教程 文字与表格 输入文字:TEXT.MTEXT 插入表格:table 新建表格样式 尺寸标注 测量工具:Di.DLI 开启标注:打开工具-工具栏-标注 对齐/ ...

  10. java高并发系列 - 第16天:JUC中等待多线程完成的工具类CountDownLatch,必备技能

    这是java高并发系列第16篇文章. 本篇内容 介绍CountDownLatch及使用场景 提供几个示例介绍CountDownLatch的使用 手写一个并行处理任务的工具类 假如有这样一个需求,当我们 ...