【目的】爬取某外卖网站水果类店铺的销售品类、价格,优化购买决策。
【工具】R、jsonlite
【难点】根据JSON的特点:1、是一种纯文本的层级结构;2、对象可以包含多个名称/值对;3、数组可以包含多个对象。
经fromJson函数后,我们发现data.frame的结构非常特殊并且复杂:
1、列中套有list;
2、list的元素可能又是data.frame。
因此,做好JSON爬取的核心策略:熟悉目标网页中包含的字段(名称)和各字段的类型成为首要步骤,随后制定目标字段和个性化的解析提取方法。
【参考资料】
2、R语言解析JSON格式数据文件
3、JSON 教程
【代码及步骤:Raw Data】
#加载相应包
library(xml2)
library(RCurl)
library(jsonlite)
library(stringr)
#创建存储数据的对象
products <- NULL
products_2 <- NULL
pro_spec = NULL
#选择经常买的三家水果店铺,作为本项目的样本
shops<-c("152344848","156275821","1511287")
for(m in shops){
#构建连接地址
EUrl <-paste0("https://www.ele.me/restapi/shopping/v2/menu?restaurant_id=",
m,
"&terminal=web")
#将JSON转化为R对象
DataJs <- fromJSON(EUrl,simplifyMatrix = T,flatten = T)
#分析R对象DataJs,虽然是数据框,但存在一些列是list格式,其中foods包含了本项目需要的大部分数据。
#通过火狐浏览器元素查看器,我们发现:
# 1、每一个店铺都有一个foods元素;
# 2、每一个foods元素都包含数个水果品类,水果品类数=DataJs$description的长度;
# 3、每一个水果品类都有一个名称为spcfoods的list,spec_foods则包含了每个类别中的商品信息,每个商品则为一个单独的data.frame。
#因此,我们采用由低向上的遍历方法,生成2个数据框:pro_spec存储spec_foods中的内容;products则存储foods中非list对象。
Food_detial <- DataJs$foods
#Food_detial
for(i in 1:length(DataJs$description)) {
print(paste0("i=",i))
a<-Food_detial[i][[1]]$specfoods
for(j in 1:length(a)){
print(paste0("j=",j))
pro_spec <- a[[j]][,!sapply(a[[j]],is.list)]%>%
rbind(pro_spec,.)
}
}
#循环的执行过程中,我们发现,Food_detial可能存在两种形式,20个字段的data.frame和44个字段的data.frame。
#返回网页观察,在优惠的类别当中需要记录活动信息,所以字段数会更多。故增加了条件判断,存储为2个data.frame。
for(k in 1:length(DataJs$description)) {
a<-Food_detial[[k]][!sapply(Food_detial[[k]],is.list)]
if(is.null(products)){
products<-rbind(products,a)
}else if(!is.null(products) & ncol(products)==ncol(a)){
products<-rbind(products,a)
}else{
products_2<- rbind(products_2,a)
}
}
}
【代码及步骤:Database存储】
上述代码初步获取了我们需要的数据,仍存在需要我们不需要的字段,根据网页详情,我们进一步清理字段、标准化字段取值,并将清洁数据存储到数据库Mysql中,以备日后分析使用。