Code Monkey home page Code Monkey logo

pizzaprediction's Introduction

pizzaprediction

此项目爬取饿了么外卖网站的披萨商品信息,包括店铺坐标、店铺评星、店铺订单量、单品描述、单品销量、单品评星等信息,通过对商品描述进行分词,统计词频提取披萨商品的特征,运用神经网络和XGBoost两种机器学习算法,构建披萨商品预测模型。

爬虫思路

  • 分析饿了么官网地址关键字搜索:
    以在南京市搜索“新街口”为例,抓包查看到地址关键字搜索的访问地址为https://www.ele.me/restapi/v2/pois?extras%5B%5D=count&geohash=wtsw9yrc72y&keyword=%E6%96%B0%E8%A1%97%E5%8F%A3&limit=20&type=nearby
    分析url我们可以发现,关键字内容存放在参数keyword中,这样构造请求地址后就可以爬取到地址信息了,我们这里默认选择地址信息中店铺数量最多的地址。
    Tips: 需要注意的是如果爬取其他城市的话,需要更换geohash后的内容。
  • 分析店铺搜索结果页面
    shopsearch 选择某一地址后,进入店铺页面,选择“美食-汉堡披萨”类,抓包查看此类店铺搜索的访问地址为https://www.ele.me/restapi/shopping/restaurants?geohash=wtsqqbuz1u87&latitude=32.041479&limit=24&longitude=118.779792&offset=0&restaurant_category_ids%5B%5D=3&terminal=web
    分析url,geohash为地理编码信息,与上一个地址关键字搜索url相同;latitude和longitude为经纬度信息,可以通过地址搜索结果获取;offset为翻页信息,查看爬取数据结果可以发现每页有24条数据,因此可以迭代offset值获取所有的店铺信息。
    构造了请求地址后就可以爬取店铺信息了,需要注意的是,本项目只需要爬取披萨门店就可以了,因此可以在获取店铺信息前判断该门店是否是披萨门店,即判断店铺id中是否含有211,如果没有211就直接跳过爬取下一个店铺。
    for i in range(0, 10):  
        # search_result为爬取地址信息的具体内容
        shop_url = 'https://www.ele.me/restapi/shopping/restaurants?geohash=' + str(search_result['geohash']) + '&latitude=' + str(search_result['latitude']) + '&limit=24&longitude=' + str(search_result['longitude']) + '&offset=' + str(i*24) + '&restaurant_category_ids%5B%5D=3&terminal=web'  
        shop_ = requests.get(shop_url, headers=head, cookies=cookie)  
        time.sleep(2 + numpy.random.randint(0, 3))  
        shop_json = json.loads(shop_.text)  
        if len(shop_json) == 0:  
            break  
        for shop in shop_json:  
            # 首先判断是否为披萨店  
            json_id = []  
        for flv in shop['flavors']:  
            json_id.append(flv['id'])  
        if 211 not in json_id:  
            continue  
            # 店铺信息  
            shop_id = shop['id']  
            shop_address = shop['address']  
            shop_name = shop['name']  
            shop_delivery_fee = shop['float_delivery_fee']  
            shop_latitude = shop['latitude']  
            shop_longitude = shop['longitude']  
            shop_opening_hours = shop['opening_hours']  
            shop_recent_order_num = shop['recent_order_num']  
            shop_rating = shop['rating']  
    
  • 分析店铺页面爬取披萨品类信息
    itemcategory 查看某个店铺的页面,可以看到页面中对所有商品做了一个分类,分类内容会有沙拉、披萨、饮料等等,因此我们可以先判断该分类是否为披萨品类,然后再爬取改分类下的披萨商品,判断披萨品类方法如下。
    def is_pizza(str):
        if '披萨' in str:
            return True
        if '比萨' in str:
            return True
        if 'Pizza' in str:
            return True
        if 'pizza' in str:
            return True
        return False
    
    找到披萨分类后遍历所有披萨商品,爬取披萨商品的信息包括商品描述、月销量、名称、评星、评星数等,最终结果保存在csv文件中。
    pizza_ = requests.get(pizza_url)
    time.sleep(2 + numpy.random.randint(0, 3))
    pizza_json = json.loads(pizza_.text)
    for menu_json in pizza_json:
        if is_pizza(menu_json['name']):
            # 目录信息
            menu_name = menu_json['name']
            food_json = menu_json['foods']
            for foods in food_json:
            # 单品信息
            foods_id = foods['item_id']
            foods_description = foods['description']
            foods_month_sales = foods['month_sales']
            foods_name = foods['name']
            foods_rating = foods['rating']
            foods_rating_count = foods['rating_count']
            foods_satisfy_count = foods['satisfy_count']
            foods_satisfy_rate = foods['satisfy_rate']
    

数据处理

  • 数据清洗
    因为不同的地理位置覆盖的范围可能重叠,因此我们爬到的店铺可能存在重复,所以我们需要将重复的数据去除,可以根据item_id去除重复数据

    data = data.drop_duplicates(['item_id'])
    

    同时,评星为0的数据对我们来说意义也不大,这些大多是销量很低还没有人评星的商品,可以视为离群数据将其取出

    data = data[data['item_rating'] > 0]
    
  • 自然语言处理
    为了提取商品原料的信息内容,将爬取到的商品描述信息进行分词处理,使用的是jieba库

    def jieba_cut(word):
        s = jieba.cut(word)
        s = [word.encode('utf-8') for word in list(s)]
        stoplist = {}.fromkeys([line.strip() for line in open("./stopwords.txt")])
        segs = [word for word in list(s) if word not in stoplist]
        return segs
    

    将分词的结果进行词频统计,使用pyecharts库将词频统计结果通过词云图可视化

    def draw_word_cloud(word_data):
        from pyecharts import WordCloud
        word = []
        value = []
        for (k, v) in word_data.items():
            if v < 50:
                del word_data[k]
            else:
                word.append(k)
                value.append(v)
            wordcloud = WordCloud(width=1300, height=620)
            wordcloud.add("", word, value, word_size_range=[5, 1000], shape='diamond')
            wordcloud.render("./wordcloud.html")
    

    wordcloud 可以看到,词频较高的词有牛肉、鸡肉、培根、奶酪等,这些都是我们希望获得的披萨原料和风味,但是也有一些词,比如纸巾、风味、超级等没有多大意义的词,这里展示一下词频排在前30的有哪些词

    词频
    芝士 2224
    披萨 1292
    1018
    菠萝 826
    青椒 823
    牛肉 755
    748
    火腿 737
    洋葱 718
    原料 666
    培根 641
    比萨 639
    蘑菇 603
    里拉 557
    马苏 515
    搭配 443
    12 413
    面团 409
    榴莲 399
    美味 395
    389
    玉米 381
    鸡肉 374
    腊肉 329
    提供 325
    纸巾 325
    奶酪 308
    蔬菜 307
    288
    香肠 284
    海鲜 277

    可以发现,芝士的词频遥遥领先,果然芝士就是腻酿!!
    其中发现了12这个有趣的高频词,其实是很多披萨商品是12寸的,因此12的词频就很高。可以看到分词结果还是比较混乱的,因此我将所有词频高于5且对商品描述有意义的词进行逐一分类,共分为风味、口味、烹制方法、蔬菜类、荤菜类、水果类、海鲜水产类、辅料佐料类。

    大类 子类
    风味 热带风味 夏威夷
    热带
    意式风味 意式
    意大利
    那不勒斯
    博洛尼亚
    西西里
    贝贝罗尼
    罗马
    东南亚风味 泰国
    泰式
    马来西亚
    墨西哥风味 墨西哥
    奥尔良风味 奥尔良
    新奥尔良
    澳洲风味 澳洲
    新西兰
    法式风味 法国
    美式风味 美国
    纽约
    荷兰风味 荷兰
    德式风味 德式
    德国
    **风味 **
    韩式风味 韩国
    韩式
    日式风味 日式
    中式风味 京味
    韩式
    海陆风味 海陆
    全素风味 全素
    口味 香脆
    脆爽
    松脆
    清脆
    薄脆
    酥脆
    鲜美
    香鲜
    酸甜
    酸甜
    香甜
    甜辣
    微辣
    香辣
    麻辣
    辣爽
    甜辣
    香醇
    醇香
    醇厚
    清甜
    清新
    清脆
    柔韧
    柔嫩
    果香
    芝香
    五香
    香甜
    香鲜
    浓香
    烹制方法 薄底 薄底
    薄饼
    厚底 厚底
    铁盘 铁盘
    烤制 烘烤
    BBQ
    烤制
    烧烤
    现烤
    烤肉
    岩烧 岩烧
    炭烧 炭烧
    腌制 腌制
    焗烧
    蜜汁 蜜汁
    蔬菜类 菜椒 彩椒
    青椒
    红椒
    红彩椒
    黄彩椒
    灯笼椒
    甜椒
    辣椒 脆椒
    辣椒
    尖椒
    橄榄 橄榄
    洋葱 洋葱
    onion
    京葱 京葱
    青葱 青葱
    玉米 玉米
    玉米片
    玉米粒
    荞麦 荞麦
    荞麦面
    黄瓜 黄瓜
    酸黄瓜
    番茄 西红柿
    番茄
    蕃茄
    菌菇 蘑菇
    松露菌
    褐菇
    青豆 青豆
    芦笋 芦笋
    莴笋 莴笋
    西葫芦 西葫芦
    南瓜 南瓜
    香菜 香菜
    西兰花 西兰花
    藕片
    茄子 茄子
    土豆 土豆
    土豆片
    薯角
    薯条
    脆薯
    红薯 红薯
    荤菜类 培根 培根
    牛肉 牛肉
    牛排
    鸡肉 鸡肉
    烤鸡
    鸡腿肉
    鸡胸
    鸡胸肉
    鸡丁
    猪肉 猪肉
    里脊
    鸭肉 鸭肉
    烤鸭 北京烤鸭
    烤鸭
    羊肉 羊肉
    腌肉 腊肉
    午餐肉
    叉烧
    肉肠 香肠
    腊肠
    烤肠
    肉肠
    红肠
    热狗
    火腿 火腿
    熟火腿
    肉松 肉松
    脆骨 脆骨
    小龙虾 小龙虾
    鸡蛋 鸡蛋
    水果类 菠萝 菠萝
    凤梨 凤梨
    榴莲 榴莲
    榴梿
    榴莲果
    樱桃 樱桃
    黄桃 黄桃
    柠檬 柠檬
    香蕉 香蕉
    芒果 芒果
    椰子 椰果
    椰蓉
    清椰
    火龙果 火龙果
    苹果 苹果
    蔓越莓 蔓越莓
    海鲜水产类 鱿鱼 鱿鱼
    章鱼 章鱼
    墨鱼 墨鱼
    乌贼
    虾类
    虾仁
    大虾
    鲜虾
    虾球
    虾肉
    金枪鱼 金枪鱼
    三文鱼 三文鱼
    吞拿鱼 吞拿鱼
    鳗鱼 鳗鱼
    银鱼 银鱼
    扇贝 扇贝
    蟹类 蟹肉
    海苔 海苔
    辅料佐料 奶油 奶油
    白汁
    奶酪 奶酪
    乳酪 乳酪
    起士 起士
    小米 小米
    番茄酱 番茄酱
    蛋黄酱 蛋黄酱
    奶盖酱 奶盖酱
    千岛酱 千岛
    千岛酱
    沙拉酱 沙拉酱
    果酱 果酱
    辣酱 辣酱
    蜂蜜 蜂蜜
    芥末 芥末
    黑胡椒 黑椒
    黑胡椒
    花椒 花椒
    迷迭香 迷迭香
    茴香 茴香
    芝麻 芝麻
    香草 香草
    大蒜 大蒜
    蒜蓉
    咖喱 咖喱
    薄荷 薄荷
    慕斯 慕斯
    酸奶 酸奶
    土豆泥 土豆泥
    桃仁 桃仁
    板栗 板栗
    焦糖 焦糖
    松露 松露
    三文治 三文治
    橄榄油 橄榄油
    黄油 黄油
  • 特征与标签提取
    根据上述表格的分类,可以得到130个子类,这些就是我们需要提取的特征了。参考词袋模型表达文本信息,我们也给每个披萨商品构建一个1*130的向量,每个特征占据一个维度,如果该商品包含该特征,则该维度的值为1,否则该维度值为0,这样得到了每个披萨商品的特征向量
    我们想要预测披萨商品畅销与否,有了特征之后还需要提取披萨商品的标签。观察数据发现,我们可以通过判断商品评星是否大于店铺口味评星来判断,如果某件商品评星高于店铺口味评星,则说明该商品畅销,标记为1,否则标记为0

    def rate_to_marker(food_score, item_rating):
        if item_rating >= food_score:
            return np.array([[1]])
        else:
            return np.array([[0]])
    
  • 神经网络模型
    本项目实现的神经网络共有4层,第一层共有64个神经元,且包含0.8的droupout,第二层共有16个神经元,第三层共有8层神经元,最后一层是输出层,所有层的激活函数都是sigmoid函数,本项目使用kears库实现模型

    model = Sequential()
    model.add(Dense(64, activation='sigmoid', input_dim=X.shape[1]))
    model.add(Dropout(0.8))
    model.add(Dense(16, activation='sigmoid'))
    model.add(Dense(8, activation='sigmoid'))
    model.add(Dense(1, activation='sigmoid'))
    

    模型的一些参数是包括:

    参数
    优化函数 梯度下降SGD
    损失函数 均方误差mse
    学习率 0.001
    batch 32
    epoch 100
    交叉验证比例 0.2

    将模型训练中的loss和accuracy绘制成plot展示,可以看到最后训练集和验证集的accuracy都达到了80%以上,且最终趋于稳定,没有产生过拟合现象
    nn_plot
    最后使用测试集测试模型的泛化能力,发现准确率可以达到0.811,与训练样本的准确率0.815相近,证明整个模型的能力还是不错的

  • XGBoost 本项目还实用XGBoost实现了预测模型,具体参数如下

    参数
    booster gbtree
    max_depth 18
    scale_pos_weight 0.8
    eta 0.5
    epoch 100
    objective binary:logistic
    eval_metric ['error', 'auc']
    迭代次数 500
    交叉验证比例 0.2

    由于XGBoost的评价函数中没有precision,本项目结合sklearn自定义了评价函数,应用于每个round中

    def precision_and_recall(preds, dtrain):
        lab = dtrain.get_label()
        preds = [int(i >= 0.5) for i in preds]
        conf = confusion_matrix(lab, preds)
    	  precision = float(conf[0][0]) / float(conf[1][0]+conf[0][0])
        recall = float(conf[0][0]) / float(conf[0][1]+conf[0][0])
        return 'precision', precision
    

    模型的训练过程可视化的结果如下图
    xgboost
    可以看到训练集验证集的精度最终都可以达到0.8以上,但验证集error下降的不是很明显,但这个是效果较好的一组参数了,后期还需要对模型参数多摸索!
    最后用测试集数据对模型进行验证,测试集精度达到0.826,可以看出整个模型的效果还是不错的
    XGBoost通过训练最后可以得到一个特征的重要性评分,我们最后看到重要性排名前20的特征是

    排名 特征
    1 洋葱
    2 菜椒
    3 菌菇
    4 菠萝
    5 培根
    6 牛肉
    7 火腿
    8 鸡肉
    9 肉肠
    10 玉米
    11 番茄
    12 榴莲
    13 意式风味
    14 奶酪
    15 奥尔良风味
    16 烤制
    17 热带风味
    18 土豆
    19 薄底
    20 虾类

    分析可以看出,蔬菜类、水果类和荤菜类还是对销量有很大的影响,烹制方法和配料的影响并不是很大。 随机森林也可以输出特征重要性指标

    rf = RandomForestClassifier()
    rf.fit(X, y)
    names = ["X%s" % x for x in range(0, 130)]
    print "Features sorted by their score:"
    print sorted(zip(map(lambda x: round(x, 4), rf.feature_importances_), names), reverse=True)
    

    随机森林输出的重要性排名前20的特征是

    排名 特征
    1 鸡肉
    2 洋葱
    3 菜椒
    4 菌菇
    5 培根
    6 牛肉
    7 玉米
    8 肉肠
    9 奶酪
    10 烤制
    11 火腿
    12 意式风味
    13 菠萝
    14 番茄
    15 番茄酱
    16
    17 薄底
    18 奥尔良风味
    19 橄榄
    20 热带风味

    对比XGBoost输出的特征重要性结果,两份结果在前20的特征有17个是相同的,洋葱、菜椒、菌菇、鸡肉、培根等特征在两个模型中重要性都非常高,这便是我们想要得到的结果了,这些特征对于销量起到了关键性的作用。同时,可以看到意式风味、奥尔良风味和热带风味是最受欢迎的3个口味,鸡肉牛肉也是比较畅销的品类......

展望

  • 本项目披萨品类的特征仅仅只包含商品描述的语义信息,未来可以探索一些其它重要特征来描述披萨商品,使数据表达更加完整精确
  • XGBoost验证集进度提升较为困难,调参是一门“玄学”,考虑通过网格搜索再优化一下参数的配置,争取达到更好的效果

和我联系

E-mail: [email protected]
GitHub主页: https://github.com/JohnsonKlose
博客园: http://www.cnblogs.com/KloseJiao/
喜欢的朋友们可以加个star,也欢迎留言和邮件与我交流!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.