【分享吧】基于GO语言的爬虫框架在互联网项目中的应用

论坛 期权论坛 程序化     
大连飞创   2018-7-26 17:29   18980   0
  产品的爬虫框架设计之初,仅仅是为了爬取资讯。但是随着产品覆盖信息的扩大,爬虫的目标范围和需要支持的功能也在不断地进行扩展和复杂化。截至目前我们产品中的爬虫系统已经具备了高并发,高可用和高可扩展性的特点,可以利用很小的开发成本,就完成所有新增的需求。本文我们从基本概念为切入点,简单介绍一下目前主流的爬虫框架及特点,最终将项目中使用的爬虫框架结构,以及做了哪些优化和定制介绍给大家。希望能够让大家对网络爬虫有一个清楚的认识。
一、什么是爬虫
1概念
  网络爬虫是一种按照一定的规则,自动地抓取万维网(www)信息的程序或者脚本。是搜索引擎的一个重要组成部分。
  通过爬虫软件更新自身的网站内容或其他网站的索引。网络爬虫可以将自己所访问的页面保存下来,以便搜索引擎事后生成索引供用户搜索。传统爬虫从一个或若干初始网页的URL开始,获得初始网页上的URL,在抓取网页的过程中,不断从当前页面上抽取新的URL放入队列,直到满足系统的一定停止条件;定向爬虫则可以根据一定的网页分析算法过滤与主题无关的链接,保留有用的链接并将其放入等待抓取的URL队列。然后,它将根据一定的搜索策略从队列中选择下一步要抓取的网页URL,并重复上述过程,直到达到系统的某一条件时停止。以此来避免重复及不相关的页面被搜索到。

图1-1 工作原理
2
分类和目前主流爬虫
  网络爬虫按照系统结构和实现技术,大致可以分为以下几种类型(实际的网络爬虫系统通常是几种爬虫技术相结合实现的):
① 通用爬虫:爬行对象从一些种子 URL 扩充到整个 Web,主要为门户站点搜索引擎和大型 Web 服务提供商采集数据。这类网络爬虫的爬行范围和数量巨大,对于爬行速度和存储空间要求较高,对于爬行页面的顺序要求相对较低;
② 定向爬虫:选择性地爬行那些与预先定义好的主题相关页面的网络爬虫。只需要爬行与主题相关的页面,极大地节省了硬件和网络资源,保存的页面也由于数量少而更新快,还可以很好地满足一些特定人群对特定领域信息的需求;
③ 增量爬虫:对已下载网页采取增量式更新和只爬行新产生的或者已经发生变化网页的爬虫,它能够在一定程度上保证所爬行的页面是尽可能新的页面。不重新下载没有发生变化的页面,可有效减少数据下载量,及时更新已爬行的网页,减小时间和空间上的耗费,但是增加了爬行算法的复杂度和实现难度;
④ 深层爬虫:对深层网页的爬取。深层网页是那些大部分内容不能通过静态链接获取的、隐藏在搜索表单后的,只有用户提交一些关键词才能获得的 Web 页面。例如那些用户注册后内容才可见的网页就属于这个范畴。
   网络爬虫诞生之初,大都是由解释型脚本语言编写(主要是python)。但随着互联网的高速发展,各主流开发语言基本都出现了一些出色的爬虫框架。整理了目前业内比较有名的框架如下:
Scrapy(Python):一个快速、高效且易扩展的爬虫框架。可快速整合到python主流的web应用框架,并且能够使用redis等中间件快速搭建起分布式集群;
Pyspider(Python):功能强大的爬虫框架,有自己的web UI;
Apache Nutch(Java):灵活及高可扩展的爬虫框架;
Crawler4j(Java):简单易用、轻量级的网络爬虫;
Scraperjs(JS):高可用,功能强大的网络爬虫;
node-crawler(JS):封装了简单易用的API;
Goutte(PHP):可快速整合到Laravel引用框架;
Pholcus(GO):分布式、高并发的强大爬虫,可通过web UI配置爬取策略(扩展性较差);
go_spider(GO):一个出色,并且支持高并发的爬虫。扩展性强。
二、我们的爬虫
1技术选型
  文章开头的时候我们提到过产品的爬虫功能原始需求,主要是抓取新闻资讯。这样就要求爬虫必须具备以下功能:
1. 增量提取(已抓取的资讯不能重复抓取);
2. 页面指纹去重(存在url不同,但是内容完全相同的资讯,要识别出这部分资讯,避免重复抓取);
3. 翻页递归提取(资讯都是分页展示,要支持翻页抓取);
4. 定时提取(常驻内存爬虫进程浪费资源,需要能够定时运行爬虫)。
  根据以上需求,项目的爬虫初版选用scrapy框架来实现,可以很好地满足业务需要。但是考虑到项目组成员熟悉python的比例不高,并且scrapy也需要一定的学习成本,新的开发人员无法快速熟悉代码并开发新功能。从2.0版本开始,重构采用go语言来实现爬虫系统。我们选择的就是上面提到的go_spider框架,主要考虑到的也是它的高可扩展性,方便我们做个性化开发及优化。从目前的效果来看还是很不错的,它的框架原型功能结构如下图:

图2-1 go_spider框架原型
2
定制化开发与优化
  从上图中可以看到,go_spider框架本身优雅地分解了爬虫中各个模块,每部分只要专注自己的工作,最后由框架整合到一起完成爬虫功能。它本身支持高并发。但是结合上面的需求来看,通用的go_spider框架并不能满足我们的全部需求,比如增量和翻页抓取等功能都无法完全自动化来实现。所以,我们根据实际需求做了如下的扩展(蓝色部分为扩展与优化的功能点):

图2-2扩展优化后的框架
1. 规则引擎
  参照业内应用最广泛,且表现出色的Python Scrapy框架的规则设计理念。自主开发了规则解析引擎,最大程度抽象出业务以外的代码处理。每新增一个爬虫的时候,理论上我们只需要配置相应的规则及页面解析代码,框架就可以自动去实现递归爬取相应链接、解析内容、提交入库等操作。支持的规则配置项包括但不限于:
Allow:正则表达式,当匹配到元素时,发起新的请求,进入下一次递归;
Callback:回调函数名,当匹配到相应元素,就将该页面的DOM传递给回调函数,进行页面内容的抓取;
Restrict_selector:HTML选择器,当前页面中要匹配的元素(一般这个元素都是包含href属性,可以交给Deny或Allow进行匹配的);
Attrs:一般默认我们都是访问元素的href属性,当配置了这个内容以后,可以从指定的属性中读取属性值;
IsResource:bool,特殊配置,针对那些指向的内容为资源文件的链接抓取。(如XLSX、PDF、PPT和视频等);
Method:默认发起的网络请求都是GET方式,但针对一些深层页面,需要支持表单提交后返回。这里可以通过制定该属性的值为“POST”来实现表单提交;
PostData:当指定POST表单请求时,可以通过该属性设定表单的值。

图2-3规则配置样例
2. 增量提取
  在scheduler将网页请求插入请求队列以前,需要识别该请求是否已经抓取过。典型的key-value应用场景,但是每个爬虫要发起的网页请求可能达到几千甚至上万,所以该识别处理的运行速度一定要快,而且需要持久化处理。go_spider框架的任务队列默认是在内存中维护的,没有持久化操作,在异常退出或是重启时无法记录之前的爬取位置。为了保证该部分内存管理的高可用及稳定性,我们选择了成熟的内存数据库中间件redis来保存这些已经爬取的状态数据。Key就是页面请求的url、Value为数字1,可以实现随机访问,且避免了因为递归层级增多而导致的内存泄漏问题。
3. 指纹去重
  已经具备上述url增量前提下,基本上不会出现重复爬取情况的出现。但是在实际过程中,确实存在url不同,但是内容却完全相同的网页。所以对我们就提出了一个全新的要求,就是如何快速识别出每一个网页的唯一标识呢?我们采取的主要是以下两种方式:
“资讯标题+发布时间”转换成MD5编码;
  页面全文转换成MD5编码;
  实际使用中前者的比例会更高。MD5编码也是业内最常用的内容校验编码方式之一,在转码速度和内存消耗上有一个很好的平衡。指纹的生成策略方式是完全开放给开发者的,它是通过规则引擎的“FingerInput”属性来指定的,可以传入一个函数名,框架会自动调用该函数来获取指纹的定义。同样它也会被保存到redis中来使用。

图2-4指纹配置样例
4. 其他优化
  针对既有框架,以及不断增加的业务需求,做了很多框架优化的工作。举例如下:
数据库连接池:原型框架的官方文档给出的示例是在pipeline中创建数据库连接,执行DML之后,就直接释放该连接。在我们实际应用场景中单次执行的爬虫数量非常庞大,如果采用上面的方式就会造成很多资源的消耗,并且影响整体性能。所以这里引入数据库连接池,在整个框架中是以单例模式的实例被使用,在每次执行DML之后,会将该连接释放回连接池,其他爬虫可以继续使用;
数据库pipeline共享:高度抽象了SQL语句代码的生成及执行,对于开发者来说,我们只需要在创建爬虫时传入一个数据库表名,并且在callback函数中将想要插入的字段值通过Page.AddField函数设定到参数实例中。框架就可以自动生成SQL并将数据插入数据库。这样所有爬虫可以共用一个pipeline,而不需要重复编写该部分代码 。
命令模式:可以通过在启动爬虫应用程序时,传入不同的参数来控制爬虫的运行频率,以及开关增量爬取去重处理(针对发起POST请求的URL是不能插入到redis去重数据队列的,否则就只能请求一次,第二次以后都会被去重机制阻断)等操作。好处是通过参数配置可以运行一个行为完全不同的爬虫,避免了维护多个应用程序带来的高昂成本。
三、未来展望
  任何一个好的产品都不是一蹴而就的,基本需要在一个原型基础上,通过不断的扩展和优化,使其功能和性能达到一个极致。我们的爬虫框架也经历了这样一个历程,从代码重构,到各种个性化的性能优化和框架改进,使该框架已经可以很好地为我们的产品服务。但这并不是终点,未来我们还要从以下几个方面继续推动爬虫框架的发展:
动态JS渲染:有些页面中的数据,是需要经过JS渲染后才能够显示。目前我们遇到类似的业务场景,也都是采取直接从数据源头进行解析,进而绕开了JS渲染的处理。在未来我们考虑引入“JS解释器”来融合到框架中来解决该问题,可以预想到需要解决的问题还有很多;
支持分布式:如果要实现一个功能强大的爬虫系统,分布式是必须要解决的问题,尤其是在大数据分析场景下。未来我们也考虑引用zookeeper中间件,并且选定一个合适的分布式文件系统来支持任务的水平和垂直拆分;
平台化:我们的远期目标是将爬虫系统的功能和性能做到极致,然后优化封装和接口的设计。使该框架可以快速整合到公司其他爬虫应用场景的项目中去。也希望有兴趣的同事能多给提出意见或者一起讨论。

[参照资料]
https://en.wikipedia.org/wiki/Web_crawler
https://baike.baidu.com/item/网络爬虫/
https://github.com/BruceDone/awesome-crawler
https://github.com/hu17889/go_spider


分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3174
帖子:645
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP