Redis相关知识:
1、Redis 哈希(Hash):
Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。
2、Redis 列表(List):
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
3、Redis 集合(Set):
Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
4、Redis 有序集合(sorted set):
Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
采集相关说明:
1、作业(Job):
指用户出于数据需求针对特定信源的采集工作。包括对该采集作业的执行计划(包括指定执行周期或立即执行)和执行内容(采集的网站、关键词、有效数据的发布时间范围、需要的字段等)。作业执行系统将这些作业分解为不同的执行节点,通过进行数据查询、转换或交由数据平台执行,并跟踪作业的执行状态。节点可进行组合,并通过并行(可同时进行)和顺序(存在前后依赖)等方式执行。
2、任务(Task):
整个采集流程中的最小单元,一个采集任务就是一个采集节点,一个作业可分解成一个或多个任务。采集任务包含抓取源码、抽取结构化数据、存储数据、衍生下一页或详情页任务等模块所需的所有参数。
实现:
方案1、基于 Redis List 的简单实现:
采集任务的抓取源码模块,根据网站的不同,需要用到不同的资源,比如:采集微博需要微博Cookie资源、采集雪球网需要雪球Cookie资源、采集汽车之家需要IP代理池资源等。
我们可根据资源的不同,将采集任务推送到不同资源List;再由统一的任务分发者(Redis List 是线程安全的。若采集量庞大,可考虑水平拓展,搭建多个独立的任务发布+执行集群,此处的独立包含资源、爬虫等)遍历各资源List 分配资源、爬虫,并分发采集任务(分发可基于Netty实现,本处不累述)。
为了及时有效执行作业,我们先执行衍生采集任务,再执行原生采集任务。
1)Redis的键值说明:
参数 | Redis类型 | 意义 |
---|---|---|
origin.queue.{资源} | List | 资源原生采集任务队列 |
derive.queue.{资源} | List | 资源衍生采集任务队列 |
resource.monitor.{资源}.num.used | 计数器 | 资源使用情况 |
crawler.monitor.{作业}.num | 计数器 | 作业执行情况 |
2)流程:
方案2、多数据结构混合实现:
采集中经常会遇到两个问题:a)屏蔽;b)抓取源码需要多资源,比如:雪球网,即要用到Cookie资源、又要用到IP代理池资源。
以上两个问题,方案1,可简单通过资源队列的横向拓展来实现:屏蔽的网站(因无法申请到Soldier而被放至对尾;尴尬的是,网站间隔10s后就可以正常访问了;而我们由于采集量庞大,执行到对尾要2h甚至更多),如果数据时效性要求不高,可忽略;如果时效性要求高,可专门定义一个资源队列。多资源问题,也是专门定义一个资源队列,资源是资源包(已雪球网为例,资源包包含Cookie资源和IP代理池资源)。
方案1,专门定义的资源队列越是特殊就越会有大量的资源和爬虫的浪费。所以,方案1,是一个简单粗暴且有钱任性的方案!
即将展示的方案2,对方案1进行了设计上的优化,减少了不必要的横向拓展。对每个任务定义了一个执行时间,执行时间内的任务先进先出;若网站被屏或资源紧缺,根据网站的解屏时间或资源的释放时间,调整任务的执行时间。
1)Redis的键值说明:
参数 | Redis类型 | 意义 |
---|---|---|
crawler-task-sorted-origin | Zset | 原生采集任务有序排队集合 |
crawler-task-sorted-derive | Zset | 衍生采集任务有序排队集合 |
crawler-task-distribute | List | 采集任务分发队列 |
crawler.monitor.{作业}.num | 计数器 | 作业执行情况 |
2)流程:
a)任务排队流程:
b)任务发布流程: