水利信息化经过多年的发展,全国各水利厅局及流域机构积累了大量的水利数据,包括水雨情数据、水质数据、水涝灾害数据、遥感数据、防汛抗旱知识和应急管理知识等,这些数据可为防汛抗旱决策支持、水利工程建设、水文水资源研究等提供重要支持[1]。然而,水利数据分散在各水利机构内,结构复杂、种类繁多,即便是同一种类的业务数据,数据结构也有所差异,阻碍了水利数据的开发、利用与研究。因此,本文从各水利政务网站的公开数据入手,结合水利部发布的数据库规范设计数据库表,以水雨情数据为例,基于爬虫框架Scrapy 设计和开发水利爬虫,着重介绍了各种水雨情网页的获取和解析过程,包括Scrapy Selector CSS 选择器解析HTML 结构数据、正则表达式解析JavaScript 数据、
获取和解析Ajax 数据、Selenium 结合浏览器驱动解析网页加密数据等;并对爬取的数据进行规范化存储,最后使用SpiderKeeper 对分布于不同服务器的爬虫进行统一管理。
1网络爬虫
网络爬虫(web crawler ),又称作网络机器人
(web robot )或网络蜘蛛(web spider ),是以一定的规则自动抓取互联网资源的计算机应用程序[2]。网络爬虫通常由调度器(scheduler )、下载器(download ⁃
er )、解析器(parser )、待爬行队列(URL queue )4个部分组成,如图1所示[3-4]。调度器首先将
设定的初始URL 传递给下载器,下载器从互联网下载信息并传给解析器,解析器根据既定规则提取信息和待爬取
的URL ,提取的信息经处理后存入数据库或文件,URL 经去重后传输给待爬行队列,等待调度器调用,循环往复[5-7]
图1网络爬虫执行流程
网络爬虫分为通用(General Purpose )网络爬虫和聚焦(Focused )网络爬虫[8]。通用网络爬虫又称作全网爬虫,爬取整个互联网资源以供搜索引擎建立索引;聚焦网络爬虫又称作主题网络爬虫,爬取用户需要的特定主题网页(经相关网站许可)收集数据,并在标注出处的前提下加以数据分析、用于
收稿日期:2019-12-03作者简介:游攀利,男,工程师,硕士,主要从水利信息化建设及软件开发工作。E-mail :youpanli@cjwsjy
基于Scrapy 的水利数据爬虫设计与实现
游攀利,杨琳,喻淼
(长江信达软件技术(武汉)有限责任公司,湖北武汉
430010)
摘要:为解决目前各级水利部门数据共享能力弱、数据格式不统一的问题,建立了一种水利数据整合方
法。针对互联网公开的水利数据特点,结合水利行业标准规范,介绍了基于Scrapy 框架设计和开发的水利数据爬虫,并规范化存储数据。在总结各种水利数据的获取和解析原理及方法基础上,提出了使用Scra ⁃pyd 部署爬虫和SpiderKeeper 管理爬虫的方法,并成功应用于长江大数据中心的建设,为水雨情预警、防汛抗旱、应急管理提供了重要支持。
关键词:水利数据;网络爬虫;Scrapy ;数据采集
中图法分类号:TP391
文献标志码:A
DOI :10.15974/jki.slsdkb.2020.05.014
文章编号:1006-0081(2020)05-0071-07
·
·71
建模及展示。水雨情爬虫为聚焦网络爬虫,目标是爬取各级水利部门网站公开的水雨情信息。
网络爬虫技术伴随着搜索引擎共同发展,在网络爬虫发展初期,开发者需考虑很多底层功能的实现,例如,模拟HTTP 请求、下载网页、解析数据、队列管理等。搜索引擎面对的问题日趋复杂,促使爬虫程序的编写难度越来越大。为了提高爬虫的编写效率,逐渐出现了网络请求库urllib 、urllib2、re ⁃quests 等和解析库Xpath 、Beautiful Soup 、pyquery 等,
进而抽象出模块的概念,有了爬虫框架的雏形。此时,开发者虽不需考虑这些常用模块的实现,但仍需考虑爬虫的流程、异常处理及任务调度等。对于今后进一步出现的pyspider 、scrapy 等爬虫框架,开发者只需考虑爬虫的业务逻辑部分。而且,pyspi ⁃der 、scrapy 的可配置性更高,异常处理能力更强,故水利数据采集系统采用scrapy 作为爬虫框架。2Scrapy 框架
Scrapy 是使用Python 语言实现的一种爬虫框
架,它将网络爬虫工程化、模块化,帮助开发人员方便地构建网络请求、解析网页响应,从而快速实现
特定业务的爬虫[8-9]。Scrapy 基于Twisted 异步网络库处理网络通讯,实现了分布式爬取。
2.1Scrapy 组件
Scrapy 爬虫框架结构如图2所示[10],包括以下6
个主要组件:
(1)Scrapy 引擎(engine )。负责控制数据在组件中流动,并在相应动作发生时触发事件,是整个框
架的核心。
(2)调度器。从Scrapy 引擎接受请求,并将其加入到队列中,以便之后请求它们时提供。
(3)下载器。接受请求,负责获取数据并提供给引擎,而后提供给爬虫。
(4)爬虫。用户编写的针对特定业务的爬虫程序,定义了初始请求网址、网页的爬取逻辑和解析规则,用于发起初始请求和解析返回的数据并提取项目(Item ,定义了爬取结果的数据结构,可对应web 开发中的实体类)和生成新的请求,每个爬虫负责处理某个特定或一些网站。
(5)项目管道(Item Pipeline )。负责处理爬虫提取出来的项目,典型的处理有数据清洗、验证数据完整性、数据重复及数据持久化(例如按指定格式保存到文件或存取到数据库中)。
(6)中间件(middleware )。包括下载器中间件(downloader middleware )和爬虫中间件(spider mid ⁃dleware )。下载器中间件是在引擎与下载器之间的特定钩子(specific hook ),主要处理引擎发过来的请求,用户可编写程序扩展Scrapy 的功能,以自定义的方式协调下载器的工作;爬虫中间件是在引擎与爬虫之间的特定钩子,处理爬虫的输入响应(response )和输出条目及请求。同下载器中间件一样,用户可扩展Scrapy 功能,以协调爬虫工作。
2.2Scrapy 数据流
Scrapy 的数据流向是由引擎控制的,数据流动
的过程如下:
(1)引擎到处理该网站的爬虫,向爬虫请求
图2Scrapy 架构
数据数据/请求
请求
请求
响应
响应
请求请求
调度器
下载器
引擎
数据管道
中间件
爬虫
互联网
·
·72
初始要爬取的URL,根据URL解析域名。
(2)引擎从爬虫中获取到第一个要爬取的URL,并通过调度器以请求的形式调度。
(3)引擎向调度器请求下一个要爬取的URL。
(4)调度器返回下一个要爬取的URL给引擎,引擎将URL通过下载器中间件转发给下载器。
(5)一旦页面下载完毕,下载器生成该页面的响应,并将其通过下载器中间件发送给引擎。
(6)引擎从下载器处接收到响应,并通过爬虫中间件发送给爬虫处理。
(7)爬虫处理响应,解析数据并封装成条目,返回条目及新的请求给引擎。
(8)引擎将爬虫返回的条目给项目管道处理,将新的请求加入到调度器的待爬队列中。
(9)重复步骤(2)~(8),直到调度器中没有请求,待爬队列为空,或者满足自定义的停止条件,引擎则关闭该网站,爬行结束。
通过多个组件的分工协作、组件对异步处理的支持,Scrapy充分利用了带宽,提高了数据爬取效率。3水雨情爬虫设计
3.1网页获取
爬虫设计的首要工作即是得到获取目标数据的网络请求。访问水雨情网页,查看网页的源代码会发现,
其中可能根本不存在水雨情数据,这是因为现在很多网页是由JavaScript渲染出来,原始的HTML代码可能只是一个仅有水雨情表头的表格。向网站发送一个HTTP请求,返回的就是网页源代码。综合分析各级水利部门网站公开的水雨情网页,存在以下几种情况。
(1)网页源代码中的HTML代码包含目标数据,该网页的URL就是获取目标数据的URL;
(2)网页源代码中的JavaScript代码包含目标数据,该网页的URL就是获取目标数据的URL;
(3)网页源代码不包含目标数据,通过Ajax加载数据渲染网页,Ajax请求的URL即是获取目标数据的URL;
(4)网页源代码不包含目标数据,通过JavaS⁃cript算法生成数据渲染网页,该网页的URL即是获取目标数据的URL;
(5)网页源代码不包含目标数据,网页数据被加密,该网页的URL即是获取目标数据的URL;
(6)网页源代码不包含目标数据,而是使用if⁃rame嵌入另一个HTML网页展示数据,这种情况下,为了确认获取目标数据的URL,需再次分析,以确定是上述(1)~(5)中的哪种情况。
3.2数据解析
确定获取目标数据的URL之后,使用Scrapy模拟HTTP请求,对返回的响应数据进行解析。根据响应的不同,解析方式也不同。对于3.1节中的情况(1),需要解析的是HTML结构数据,使用Scrapy 的Selector CSS选择器进行解析;对于情况(2),由于JavaScript数据不是结构化的,通常使用正则表达式进行解析;对于情况(3),需要解析的是JSON数据;对于情况(4)和(5),通常JavaScript的算法规律并不容易到,即使到,花费的时间代价太大,若网页数据加密,分析难度更大,所以这两种情况通常采用Selenium或PhantomJS来驱动浏览器加载网页,直接获得JavaScript渲染后的网页,做到“可见即可爬”,接下来解析HTML结构数据即可,与(1)的解析方式相同[11]。对于情况(6),iframe嵌入网页的数据解析,只需分析iframe嵌入的网页源代码即可,后续步骤与(1)~(5)相同。具体的数据解析如下。
(1)HTML结构数据解析。以重庆水利水务网“水文管理”模块下的“今日八时水情”(URL为http://v/swxx/jrbssq/Pages/Default.aspx)为例,如图3所示。查看源代码,发现目标数据已包含在HTML代码结构中,可利用CSS选择器进行解析。例如使用Scrapy shell提取寸滩和武隆水文站的水位数据,可分别使用如下代码进行提取,如图4
所示。
图3重庆水利水务网“今日八时水情”
游攀利等基于Scrapy的水利数据爬虫设计与实现
·
·73
2020年5月水利水电快报EWRHI 第41卷第5
图4HTML 文档解析
需要注意的是,由于代表站占据了第2行的第1列,所以寸滩的水位数据在第2行的第5列,而武隆的水位数据在第4行的第4列。
(2)JavaScript 数据解析。以长江水文网的“实时水情”(http ://www.cjh/)为例,如图5所示。分析网页的源代码,目标数据是通过<a>标签嵌入的URL (http ://www.cjh/sqindex.html )加载的,分析此HTML 的源代码,发现目标数据包含在Ja ⁃vaScript 代码中(见图6),其作为一个JSON 字符串赋值给了一个JS 变量,这种情况使用正则表达式获取目标数据最为方便,如图7
所示。
图5长江水文网
“实时水情”
图6JavaScript 包含水情数据
(3)Ajax 数据解析。以湖北省水利厅网站的“水旱灾害防御”模块的“江河水情”栏目(http ://113.57.190.228:8001/web/Report/RiverReport )为例,
该栏目展示了湖北省56个河道水文站当天08:00的水位、流量信息,如图8所示。目标是从表格中解析出实时数据(水位、流量)和防洪指标数据(设防水位、警戒水位和历史最高水位),查看江河水情网页的源代码发现表格中并没有相关数据。打开Chrome 开发工具,选择Network 选项卡,通过XHR 筛选出Ajax 请求,选定请求,点击Headers 可查看请求和响应的头部详情。分析可知,请求类型为GET ,请求URL 为http ://113.57.190.228:8001/Web/Report/GetRiverData ?date=2019-11-11+08%3A00,请求的参数只有1个date ,参数值经过了HTML URL 编码,表示的是ASCII 字符“:”,所以真实的参数值为:
2019-10-3008:00。据此,可以使用Scrapy 模拟Ajax 请求,如需获取历史数据,只需要按照一定规则改变date 参数即可。点击Preview 可预览返回的JSON 数据,如图9所示,对照网页数据可知每个字
段的业务意义。
图8
湖北省江河水情
(4)加密网页数据解析。以江苏水情信息(http ://221.226.28.67:88/jsswxxSSI/Web/Default.ht ⁃ml ?m=2)为例,使用如图10所示代码即可获取表格数据。后续使用HTML 结构数据解析方法即可,
不再赘述。
图7
使用正则表达式解析JavaScript 中的水情数据
·
·74
3.3爬取对象定义
Scrapy 的条目类用来定义爬取的对象。综合分
析各级水利部门网站公开的河道水情信息,并结合水利部发布的SL323-2001《实时水雨情数据库表结
构与标识符》数据库规范,对于河道水文站爬虫,用StRiverItem 定义爬取对象,需定义的属性有:测站编码、时间、测站名称、河流名称、水位、流量、设防水
位、警戒水位、保证水位和历史最高水位。3.4数据存储
爬虫将解析的数据封装成条目之后交给项目
管道,进行数据持久化。数据持久化的方式有多种,可直接本地化存储为文件,如TXT 、CSV 、JSON 等,也可保存至Oracle 、MySQL 等关系型数据库或者MongoDB 、Redis 等非关系型数据库。本文采用MySQL ,根据爬虫对象StRiverItem 设计河道水情数据表结构,如表1所示。
表1河道水情
序号12345678910111213
字段名测站编码时间测站名称河流名称水位流量设防水位警戒水位保证水位历史最高水位网站域名创建时间更新时间
字段标识STCD TM STNM RVNM Z
Q
FRZ
WRZ GRZ MAXZ
crawlDomain createTime updateTime
类型及长度varchar(8)datetime varchar(30)varchar(30)decimal(7,3)decimal(9,3)decimal(7,3)decimal(7,3)decimal(7,3)decimal(7,3)varchar(100)
datetime datetime 是否
允许
空值N N
N
N N N 主键序号12
爬取的数据保存至MySQL 数据库,结果如图11
所示。
图11
MySQL 数据库保存的已爬取的数据
3.5异常处理
爬虫执行一段时间后,可能会出现“403Forbid ⁃
den ”的错误,这是因为IP 访问频率过高,网站采取了反爬措施,检测到某个IP 单位时间内访问次数超过某个设定的阈值,就会拒绝服务。解决办法包括
使用代理和降低访问频率,鉴于水利数据每天更新数量不多,可采用降低访问频率办法采集历史数据,在采集一个时间段的数据后,程序停顿1~2s 。如果采集的是实时数据,水文网站每天更新的数据量并不多,通常不会出现访问频率过高的问题。
python爬虫开发网站反爬的措施还包括登录、验证码和返回动态网页等。水雨情等水利数据通常是公开的,无需登陆和输入验证码即可访问。如果确有登录和验证码的要求,
需在爬虫业务逻辑内增加登陆模块,
图9Chrome Network 分析Ajax
请求
图10Selenium 结合浏览器驱动解析加密网页的数据
游攀利等基于Scrapy 的水利数据爬虫设计与实现
·
·75