大规模电商平台商品信息采集系统的设计与实现

2020-07-01 07:04马乐荣
关键词:商品信息页面京东

刘 哲,马乐荣

(延安大学 数学与计算机科学学院,陕西 延安 716000)

随着电子商务的快速发展,网上购物已经成为一种重要的购物形式,大规模电商平台每天都会产生海量的商品交易数据。大量研究人员选择电商平台的商品数据作为实验数据集[1-3]。针对这些数据的挖掘和分析,对于优化平台建设、增加产品销量、改进消费者购物体验等,都有着重要的研究价值。

电商平台出于性能和安全的考虑,往往通过异步加载的方法对数据进行展示。一些商品页面在浏览器中查看时,可以显示所有的商品数据,但通过爬虫将页面下载到本地,却无法获取想要采集的数据或者只能获取到部分数据。例如,京东商城和天猫的商品详情页中,商品价格的显示都是动态加载的,通过查看页面源代码的方式,会发现显示价格的html标签中没有内容。针对这些大规模电商平台数据,常见的做法是对页面中的http请求进行抓包和分析,寻找数据源。这种做法通常是非常值得的,最终可以得到结构化的、完整的数据和更快的抓取速度。然而,大规模电商平台页面内容丰富、结构复杂,一个商品页面能产生上百个请求。而且,接口所需参数往往难以获取并且不好分析。如果对参数进行删减,则得到的数据准确性难以保证,并且容易被平台的反爬机制发现,导致被屏蔽。随着平台的业务发展和技术迭代,接口参数也会发生变动。

当需要获取不同平台的商品数据时,必须重新抓包、分析,时间成本高,技术难度大。本文通过Splash模拟浏览器操作,结合Scrapy爬虫框架,设计并实现了一个商品信息采集系统,可以快速实现对不同平台商品信息的采集。

1 实验设计

大规模电商平台拥有的商品数以万计,商品种类繁多,为了方便消费者快速定位感兴趣的产品,通常都提供了“商品检索”的功能。国内一些常见的大规模电商平台,例如京东商城、天猫、淘宝、当当网等,都提供了商品检索的功能。这些平台在商品详情页对商品进行描述时,都包括以下几个部分:商品基本信息、规格参数、用户评论,以及一组用于商品展示的图片等。因此,本实验主要针对商品详情页包含的四部分内容进行数据采集。

1.1 基本概念

在电商平台进行商品检索时,首先需要在“商品搜索框”中输入商品名称,点击“搜索按钮”后会返回搜索结果的第一页,选定此时的页面地址作为程序的启动URL(start_url)。同时,在电商平台的商品检索页面下方,通常有一排用于显示页码信息的分页按钮(例如京东,天猫,当当网等),如下图1所示为京东商城商品检索页中的分页按钮,图中箭头所示文本输入框中的页码,称作页号(page_num)(初始值设置为1,可根据需求适当调整)。

图1京东平台商品检索页面中的分页按钮

1.2 系统算法设计

实验设计的基本步骤如下(图2为对应的程序流程图):

图2 系统流程图

(1)首先,访问start_url页面。

(2)模拟浏览器操作,访问商品检索结果中的第page_num页:

a.在图1所示的文本输入框中填入page_num。

b.点击‘确定’按钮,访问检索结果中的page_num页。

c.根据实际情况,选择性增加页面滚动的操作(适用于那些通过页面滚动的方式加载当前检索页剩余商品的电商平台,例如京东商城、苏宁易购等)。

(3)将检索结果中的第page_num页内容下载至本地,解析出页面中的商品详情页地址。

(4)遍历商品详情页,依次采集商品的基本信息、图片、评论数据。

(5)将采集到的商品信息存入数据库。

(6)page_num+=1,重复步骤(1)~(5),抓取剩余检索页中的商品信息。

(7)程序运行结束。

说明:通过这种方式,避免了对平台的http请求进行抓包和分析的繁琐工作,当需要采集其它平台的商品数据,或者接口参数发生变动时,只需更换start_url,便可快速开始采集。

2 技术实现

本次实验使用Python 3.0作为编程语言,使用Splash模拟浏览器操作,并对商品页面中的javascript代码进行预渲染,爬虫部分使用了Scrapy爬虫框架对数据进行抓取和解析,数据的持久化储存选择了MongoDB[4,5],系统总体框架如图3所示。

2.1 主要组件介绍

Splash是一种javascript渲染引擎,它的本质是一个带有HTTPAPI的轻量级Web浏览器。其支持以下功能:并行处理多个Web页面;为用户返回经过渲染的页面;可以方便的禁止图片的加载,使页面渲染速度得到极大的提升;在页面上下文(pagecontext)中执行用户自定义的javascript脚本等。由于Splash是以html的形式返回了网页的document树结构,我们可以方便的选择自己熟悉的html解析器对页面进行解析。通过执行用户编写的自定义脚本,Splash的作用类似于浏览器自动化工具。

图3 系统框架

Scrapy是一款基于Python实现的,已经非常成熟的爬虫框架。它提供了很多方便实用的功能,让数据爬取变得简单而高效。Scrapy自带的选择器(selector),让用户可以通过xpath表达式或者css选择器从html/xml结构中选择和提取数据,并对css选择器进行了扩展。交互式的shell控制台,在编写或者调试爬虫程序时非常有用。同时也提供了强大的可扩展性支持,允许用户使用自定义的中间件对框架进行扩展。

MongoDB是当前很受欢迎的新一代数据库,它由C++语言编写,是一个基于分布式文件存储的开源数据库系统。相比于传统关系数据库,MongoDB对于大数据,高并发以及高可靠性有强大的支持。相比于其他的NoSQL数据库,MongoDB的基于文档的数据模型及其动态建模的特性使得它更加自由灵活,适用于各种应用场景。它支持几乎所有的主流编程语言,例如Python,java,php等。

2.2 具体实现技术

搭建实验环境时,有几项准备工作要做:①由于Splash是运行在Docker容器中的,所以需要先安装Docker,安装成功后,从DockerHub拉取Splash镜像特别慢,容易拉取失败,配置国内的镜像源后可以解决,可以参考文献[6]。②采用Scrapy+Splash结构时,还需要安装Python包Scrapy-Splash,达到二者之间的无缝结合。安装Scrapy-Splash时,要注意阅读“配置”部分的内容。③通过“pipinstallscrapy”命令安装爬虫框架时,容易遇到因为超时抛出异常,无法下载成功的情况。可以选择一些比较稳定、下载速度快的国内镜像来下载,安装其他Python包时也可使用该方法进行加速。表1中列出了一些常用的Python镜像站。

表1 常用的国内镜像站

关于Scrapy的框架结构,内部各组件间数据的处理和流向,官方文档中有详细的介绍,也可以参考文献[7,8],本文不再赘述。这里主要介绍如何通过上述各种工具,快速实现对不同平台商品信息的采集。下图3给出了完整的系统框架,从图中可以看出,商品信息的采集可以分为两大类:一种是需要通过Splash对页面中的内容进行动态渲染(模拟浏览器操作的本质也是动态执行自定义js脚本),例如,商品检索页面,商品详情页面。采集时,需要将默认的Scrapy Request对象,经过包Scrapy-Splash转换为Splash可以接受的Splash Request对象,再由Splash访问对应的页面,返回经过渲染后的内容。另一种不需要经过Splash预渲染,可以直接通过访问获取到数据,例如,商品的展示图片和商品评论。当Splash返回商品详情页的内容后,通过解析可以获取到商品图片的地址,Scrapy中有自带的ImagesPipeline模块,可以自动将图片下载到本地文件系统中。不过为了便于持久化储存,我们直接将下载到的图片内容(二进制格式)记录在对应的商品Item中,当商品评论下载完成时,再将Item储存在数据库中。由于我们在商品详情页浏览评论时,评论页的跳转产生的http请求较少,数据源容易确定,实验中直接通过评论接口抓取商品的评论信息。

需要指出的是,在程序的运行过程中,检索结果页、商品详情页、商品图片及商品评论的下载是同时进行的,Scrapy会自动维护请求和响应队列,我们也可以为Request对象设置优先级(priority)来指定请求的执行顺序。

3 主要问题及解决方案

本部分内容选取了实验过程中遇到的一些难点问题,并给出了详细的解决方案,总结如下。

问题1:程序运行一段时间后,频繁出现Splash服务停止运行的情况。

Splash运行时使用的不是一块固定的内存(use an unbound in-memory cache),随着时间的推移,会消耗掉所有的内存资源。解决方法是,当使用过多内存时重新启动该进程。可通过Splash中的—maxrss选项来指定该阈值。同时,为了防止异常错误导致的Splash服务停止运行,可以在Docker启动命令中增加—restart选项。启动一个需要长时间运行的Splash服务命令如下,当内存占用超过1000MB或者服务停止运行时,会重新启动Splash服务:

“dockerrun-d-p8050:8050-restart=always scrapinghub/splash-maxrss1000”

问题2:当某个商品的评论数据采集完成后,需要存入数据库,如何判断这个时间点。

假设商品有n页评论,通过循环的方式顺序发出m1,m2,……,mn个异步请求。Scrapy框架中虽然可以通过request.meta属性为每个请求标上序号,并传递给对应的response进行访问,但由于无法控制服务器的响应时间和网络传输的时间,这n个请求返回本地的顺序是不确定的,通过判断请求序号是否为n(最后一页)来确定商品评论采集完成的方法是错误的。Scrapy本身也没有给出这种情况下的解决方案。我们的解决方案是,为request.meta属性(字典结构)设置一个键,对应的值初始化为空列表,每成功抓取一页评论,向这个列表中增加一个计数元素(可以是数字0)。

该方法利用了Python语言中列表类型数据的特性,将用于计数的列表保存在本地,虽然请求是异步的,但是每个请求都指向了同一个列表(meta字段中实际保存的只是列表的内存地址)。当响应返回本地时,每个响应都可以通过判断该列表的长度和评论接口返回的“maxPage”(评论页总数)是否相等,来确定商品的所有评论信息是否采集完成。

商品详情页中图片的采集也采用类似的方法。当图片采集完成后,开始采集评论数据,评论数据采集完成后,存入数据库。

问题3:setting.py中的自定义配置项问题。

表2 setting.py中的自定义配置项

Scrapy框架的setting模块允许用户自定义项目中的所有组件行为,为项目增添或者修改任何配置的工作,都是在setting.py文件中进行的。Scrapy提供了大量的内建配置项,表2中列出了一些可以优化项目运行的配置项。其中,DOWNLOAD_DELAY用于限制从同一网站连续下载页面时,每次发送请求前程序应等待的时间,这样做的目的是为了限制爬取速度,避免对服务器造成太大的冲击,同时降低被爬虫检测程序发现的风险,该值的设置参考了文献[9]。LOG_FILE用于长时间数据采集时,将运行日志保存为本地文件。RETRY_TIMES用于设置页面下载失败时,尝试重新下载的最大次数,默认值是2次,可根据情况适当修改。RETRY_HTTP_CODES表示请求下载失败时,根据http状态码,决定是否重新发起请求,这里在原来的配置项中增加状态码400,因为在大规模的数据采集过程中,会遇到少量的状态码为400的情况。

问题4:采集商品评论时,评论接口参数的值获取困难。

实验中还遇到一个难点,当采集京东商城商品评论时,评论接口参数callback的值无法确定,它的值都具有“fetchJSON_comment98vv5289”的形式,不同商品的评论接口中,该字符串末尾的几位数字是随机的。虽然去掉callback参数时也可以采集到评论数据,但采集一段时间后,接口返回“套接字错误”的提示,无法继续获取到评论数据。通过多次分析发现,在商品页面中存在一个js变量comment Version,该变量的值和参数callback中最后几位数字是一致的,通过编写正则表达式和字符串拼接,就能得到完整的评论接口。

问题5:程序结构和逻辑的优化问题。

在对页面内容进行解析,得到原始数据后,往往还需要进一步的加工处理,例如,去除字符串两端的空白,检查采集到的url字符串是否缺少“https:”协议头等。随着采集字段的增加,这样的特殊处理越来越多,使主程序逻辑混乱,代码结构臃肿。大多数项目在数据采集的过程中,对这个问题都没有引起足够的重视[10-12],当遇到大规模的数据采集时,往往扩展困难,容易造成维护噩梦。

解决方案就是将原始数据的处理过程从主程序中分离出去,通过使用Scrapy中的ItemLoader模块可以实现这个功能。由于采集到的数据是以Item的形式进行传递的,ItemLoader为Item中的每个字段提供一个输入处理器和一个输出处理器,用户可以在输入、输出处理器中,扩展或者覆盖不同字段的解析规则。通过这样的方式,实现了对程序中原始数据的解析和处理两个过程的解耦,让程序结构和逻辑更加清晰,系统更易维护。

4 实验结果与分析

本次实验选取了“京东商城”和“当当网”2个平台作为数据采集的对象,以“手机”作为检索关键词,分别对所设计的系统进行验证。针对京东商城,采集前10页的手机商品数据,在商品详情页中采集商品介绍、规格包装、图片和全部的评论数据。对于当当网,采集前5页的手机商品数据,在商品详情页中,仅采集商品介绍和规格参数、图片等基本信息。

在采集京东商城的商品数据时,我们将外层循环的迭代次数设置为1,程序运行时,只对一个检索页面中的商品信息进行采集。通过将page_num的值依次设置为1、2、3、…、10,分次采集了前10页的商品信息。采集当当网时,外层循环的迭代次数设置为5,page_num初始值设置为1,程序运行时,直接采集前5页商品基本信息。图4给出对京东平台进行分页采集时,每页数据的耗时曲线,检索页商品的评论总数曲线,及采集产生的请求数曲线。图中的x轴代表采集时的检索页页码,两个y轴代表商品评论总数(或者产生的请求个数)和总耗时。最后,在图中标注出了对当当网一次性采集5页商品基本信息时的总耗时。

仔细观察图中的耗时曲线,并且对比当当网不采集商品评论情况下的总耗时可以发现,数据采集的耗时主要受到商品评论数量的影响。图中耗时曲线上有两个异常点,第5页和第10页的耗时均高于前一个点,但评论数量却均低于前一个点。通过分析日志发现,采集这两页数据时,程序中产生的retry_request更多,即由于各种原因导致某个请求下载失败时,重新发起的采集请求。对比请求曲线中,这两个点的值均高于前一个点,也可以印证这一分析。

图4 商品信息采集结果展示

图4客观反映出了本文提出的数据采集方法的运行效率,在实践中,用户可以根据需要,采集商品的若干页评论,提升采集效率。同时,由于实验条件的限制,程序运行时的爬虫系统,数据库服务,支撑Splash及Docker服务的虚拟机等均运行在同一台笔记本电脑上,一定程度上也影响了数据采集的效率,这也是以后的一个改进方向。数据采集的成功,直接说明了本文提出的大规模电商平台商品信息采集方法的可行性,该方法可以实现对不同平台商品数据的快速采集,为广大的研究人员节省开发时间。

5 结论

本文以大规模电商平台商品交易数据为采集对象,提出了一种快速采集不同平台商品数据的有效方法,并以京东商城、当当网为例进行数据采集。通过实验证明,该方法能有效降低数据采集的难度,也可以用于单个平台的数据采集。在后期的工作中,我们将采集不同平台的商品数据,在此基础上构建电商知识图谱。

猜你喜欢
商品信息页面京东
刷新生活的页面
做“最懂产业的云”,京东云首发云操作系统
答案
备战双十一
13年首次盈利,京东做对了什么?
电子商务背景下商品信息检索问题研究
在超市快送上超越京东
小黑犬
加强医院采购管理工作的思考与实施
Web安全问答(3)