爬取淘寶商品數據使用Java實現商品推薦系統(含sql文件、算法推導等)

本文所有代碼和sql文件,全放在了github上,可自行下載:https://github.com/8042965/recommenderSystem

一、什麼是推薦系統

幾乎每個人都已經在使用了,如果你是一個購物狂你肯定使用過淘寶:

在這裏插入圖片描述

每個人的首頁肯定是不一樣的,爲什麼我的首頁關於電子產品的比較多,因爲我搜索過:

在這裏插入圖片描述

如果你喜歡聽音樂:不難發現,也會根據你所聽過的歌,給你推薦一些類型相似的歌

在這裏插入圖片描述

如果你喜歡看電視或着電影:

在這裏插入圖片描述

在這裏插入圖片描述

會根據你在看的和歷史看的記錄,給你推薦一些相似的視頻:

在這裏插入圖片描述

如果你經常泡在博客系統或者其他看書的網站會發現也會有推薦:

在這裏插入圖片描述

等等,這些都是推薦,只是推薦的方法不一樣。

可以根據用戶的特徵推薦,也可以根據物品的特徵推薦。

這就是傳說中的基於用戶的推薦系統和基於物品的推薦系統。

二、利用數學解決相似度問題

(一)概念

如下圖所示:

從下圖中就可以看出來A與D的夾角完全重合了,說明了什麼呢?

說明了,這倆相似度高。

再看一下D和B,和D和C;

根據這兩對來比較的話,D與B的相似度要比D與C要高。

在這裏插入圖片描述

我們就可以利用這種求夾角的問題,來解決相似度的問題

(二)舉例說明如何使用餘弦定理解決相似度問題

例子:

例如原來有下面幾位朋友,他們的身高和身上帶着的金錢分別爲:

姓名 身高(釐米) 金錢(元)
胡八一 185 532
二龍湖浩哥 179 550
喜洋洋 156 143
東北酒神九哥 210 340

突然有一天來了一個新朋友(假設就是你),名字叫肖能逗,身高178,身上帶着540元。

假設現在你已經穿越到了未來,可以一眼就看出來上面的信息,你該去和誰主動交朋友

經過一頓很猛的操作,你看出來了二龍湖浩哥跟你的信息是最接近的,那你就可以去跟浩哥交朋友。

來看這個圖:

在這裏插入圖片描述

可以看到肖能逗與二龍湖浩哥的信息是最貼近的,因爲他們的夾角小。所以一定推薦的就是二龍湖浩哥了。

整個過程是:

利用餘弦定理去計算肖能逗其餘所有人的值,這個值越小,說明越相似,相似度越高。

(三)利用餘弦定理來解決相似度的問題

如下圖:看到此圖應該很熟悉了吧,是一個三角形;

在這裏插入圖片描述

我們在求這些夾角時,可以使用如下餘弦定理公式:

在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述

但是我們只有2個維度的值,分別爲身高,金錢,而如上所示的計算夾角的公式,需要三個唯獨的值,所以上面這種方式不能滿足我們的需求,我們繼續推導一下:

繼續推導該公式
cos(θ)=abab cos(θ)={\color{Red} \frac{a\cdot b }{|a||b|}}

向量的點積公式:
ab=x1x2+y1y2 a\cdot b =x1x2+y1y2

絕對值推導:
a=a2 |a| =\sqrt{a^{2}}

把這倆帶入到第一個裏面,就可以得出:
cos(θ)=x1x2+y1y2x12+y12x22+y22 cos(θ)={\color{Red} \frac{x1x2+y1y2 }{\sqrt{x_{1}^{2}+y_{1}^{2}} \sqrt{x_{2}^{2}+y_{2}^{2}}}}

這樣的話,就可以計算出來2個維度的數據了。

但是如果有N個維度的數據該怎麼辦?

我們繼續推導:

這就得出了我們可以求N個數據維度的餘弦定理公式。

我們只需要套進去這個公式,就可以求出我們想要的相似度對比數據(也就是夾角的數據)。

cos(θ)=i=1n(xi×yi)i=1n(xi)2×i=1n(yi)2 cos(θ)={\color{Red} \frac{\sum_{i=1}^{n}(x_{i}\times y_{i})}{ \sqrt{\sum_{i=1}^{n}(x_{i})^{2}} \times \sqrt{\sum_{i=1}^{n}(y_{i})^{2}} }}

套公式時,我們需要給出兩個數列例如:

當前用戶的:[1,2,3,4,5,6,7]

其他用戶的:[1,5,6,7,8,9,0]

如果是N個用戶的話,那就可以把當前用戶的數據與其餘所有用戶的數據都對比一下。

如下圖所示,是我使用三組數據做的對比:結果很明顯

在這裏插入圖片描述

(四)編程實現該算法核心代碼

說是算法,不如說就是計算一個數據。都沒毛病。

如下代碼所示,就是我用java代碼實現的該算法,分別去求一下分子和分母,然後這個最終得出的數據,就是我們最終想要的。


private static Double compare(int[] o1, int[] o2) {

    //分子求和
    Double fenzi = 0.0 ;

    for (int i = 0; i < o1.length; i++) {
        fenzi += o1[i]*o2[i];
    }


    //分母第一部分
    Double fenmu1 = 0.0;
    for (int i = 0; i < o1.length; i++) {
        fenmu1 += o1[i] * o1[i];
    }

    fenmu1 = Math.sqrt(fenmu1);

    //分母第二部分
    Double fenmu2 = 0.0;
    for (int i = 0; i < o2.length; i++) {
        fenmu2 += o2[i] * o2[i];
    }
    fenmu2 = Math.sqrt(fenmu2);

    return fenzi / (fenmu1 * fenmu2);
}

三、(準備數據)爬取淘寶商品數據,來實現我們想要的功能(根據用戶購買的商品,推薦出可能需要的商品)

(一)準備數據

我們就以農產品爲例子,來爬取我們想要的數據。

在這裏插入圖片描述

我們先找出重要的數據:

例如:付款人數售價

在這裏插入圖片描述

點進去可以看到,有:評價數量收藏人數

在這裏插入圖片描述

我們可以利用python或者其他手段進行爬取數據:

在這裏插入圖片描述

我這裏爬去到了70多條:

在這裏插入圖片描述

處理完數據導入數據庫之後還剩下40多條數據:

在這裏插入圖片描述

我們還有用戶表:

在這裏插入圖片描述

還有訂單表

在這裏插入圖片描述

(二)準備數據庫表結構

1、會員用戶表

create table member_user
(
    USER_ID   int(10) auto_increment
        primary key,
    USER_NAME varchar(20) null
)
    engine = MyISAM
    charset = utf8;

INSERT INTO testdb.member_user (USER_ID, USER_NAME) VALUES (1, '鄭成功');
INSERT INTO testdb.member_user (USER_ID, USER_NAME) VALUES (2, '小紅');
INSERT INTO testdb.member_user (USER_ID, USER_NAME) VALUES (7, '小李');
INSERT INTO testdb.member_user (USER_ID, USER_NAME) VALUES (19, '鄭暉');
INSERT INTO testdb.member_user (USER_ID, USER_NAME) VALUES (10, '張三');
INSERT INTO testdb.member_user (USER_ID, USER_NAME) VALUES (11, '二龍湖浩哥');
INSERT INTO testdb.member_user (USER_ID, USER_NAME) VALUES (12, '張三炮');
INSERT INTO testdb.member_user (USER_ID, USER_NAME) VALUES (13, '趙四');
INSERT INTO testdb.member_user (USER_ID, USER_NAME) VALUES (14, '劉能');
INSERT INTO testdb.member_user (USER_ID, USER_NAME) VALUES (15, '劉能逗');

2、用戶訂單表

create table product_order
(
    ORDER_ID     int auto_increment
        primary key,
    USER_ID      int          not null,
    PRODUCT_ID   int          not null,
    GWCOUNT      int          null,
    out_trade_no varchar(100) null
);

INSERT INTO testdb.product_order (ORDER_ID, USER_ID, PRODUCT_ID, GWCOUNT, out_trade_no) VALUES (1, 1, 1, 15, '202001');
INSERT INTO testdb.product_order (ORDER_ID, USER_ID, PRODUCT_ID, GWCOUNT, out_trade_no) VALUES (2, 2, 3, 42, '202002');
INSERT INTO testdb.product_order (ORDER_ID, USER_ID, PRODUCT_ID, GWCOUNT, out_trade_no) VALUES (3, 3, 4, 2, '202003');
INSERT INTO testdb.product_order (ORDER_ID, USER_ID, PRODUCT_ID, GWCOUNT, out_trade_no) VALUES (4, 4, 4, 20, '202004');
INSERT INTO testdb.product_order (ORDER_ID, USER_ID, PRODUCT_ID, GWCOUNT, out_trade_no) VALUES (5, 1, 2, 21, '202005');
INSERT INTO testdb.product_order (ORDER_ID, USER_ID, PRODUCT_ID, GWCOUNT, out_trade_no) VALUES (6, 5, 1, null, null);
INSERT INTO testdb.product_order (ORDER_ID, USER_ID, PRODUCT_ID, GWCOUNT, out_trade_no) VALUES (7, 5, 2, null, null);
INSERT INTO testdb.product_order (ORDER_ID, USER_ID, PRODUCT_ID, GWCOUNT, out_trade_no) VALUES (8, 5, 3, null, null);
INSERT INTO testdb.product_order (ORDER_ID, USER_ID, PRODUCT_ID, GWCOUNT, out_trade_no) VALUES (9, 6, 2, null, null);
INSERT INTO testdb.product_order (ORDER_ID, USER_ID, PRODUCT_ID, GWCOUNT, out_trade_no) VALUES (10, 6, 5, null, null);
INSERT INTO testdb.product_order (ORDER_ID, USER_ID, PRODUCT_ID, GWCOUNT, out_trade_no) VALUES (11, 7, 1, null, null);
INSERT INTO testdb.product_order (ORDER_ID, USER_ID, PRODUCT_ID, GWCOUNT, out_trade_no) VALUES (12, 7, 2, null, null);
INSERT INTO testdb.product_order (ORDER_ID, USER_ID, PRODUCT_ID, GWCOUNT, out_trade_no) VALUES (13, 7, 5, null, null);
INSERT INTO testdb.product_order (ORDER_ID, USER_ID, PRODUCT_ID, GWCOUNT, out_trade_no) VALUES (14, 3, 1, null, null);

3、商品信息表

create table product_table
(
    productID    int auto_increment comment '商品ID'
        primary key,
    product_name varchar(200) charset utf8 null comment '商品名字',
    price        double                    null comment '商品金額',
    volume       int                       null comment '成交數量',
    shopp_name   varchar(100) charset utf8 null comment '商店名稱',
    location     varchar(100) charset utf8 null comment '生產地',
    evaluate     int                       null comment '好評數量',
    collect      int default 0             null comment '收藏數量'
)
    engine = MyISAM
    collate = utf8_unicode_ci;

INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (1, '楊梅新鮮現摘現發正宗仙居東魁大楊梅孕婦農家時令水果6斤裝包郵', 198, 1328, '浙仙旗艦店', '浙江 台州', 240, 1397);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (2, '正宗仙居東魁楊梅新鮮現摘特級大東魁現摘現發農家時令水5斤精選', 268, 497, '浙仙旗艦店', '浙江 台州', 159, 619);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (3, '新鮮洋蔥10斤紫皮紅皮農家2020年蔥頭應季蔬菜圓頭整箱批發包郵', 18.8, 64, '悠鮮源旗艦店', '雲南 昆明', 7, 51);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (4, '軒農谷仙居東魁楊梅新鮮水果浙江現摘現發大楊梅禮盒預定7A6斤', 358, 222, '軒農谷旗艦店', '浙江 台州', 553, 1163);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (5, '軒農谷正宗仙居楊梅新鮮當季水果特級東魁大楊梅5A級6斤高山現摘', 258, 2939, '軒農谷旗艦店', '浙江 台州', 4270, 8737);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (6, '水果小黃瓜新鮮6斤當季山東小青瓜生喫農家蔬菜助白玉女瓜10批發5', 23.8, 4, '喜人喜食品旗艦店', '山東 濰坊', 21685, 10511);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (7, '王小二 新鮮馬蹄荸薺地梨孛薺當季馬蹄蓮5斤餑薺農家自種蔬菜包郵', 19.9, 1780, '王小二旗艦店', '湖北 宜昌', 1428, 858);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (8, '王小二 雲南新鮮蠶豆農家羅漢豆帶殼生蘭花豆胡豆豌豆蔬菜包郵5斤', 29.9, 54, '王小二旗艦店', '雲南 昆明', 72, 68);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (9, '王小二 小米椒新鮮紅辣椒蔬菜包郵紅尖椒燈籠椒朝天包郵農家5斤', 29.9, 601, '王小二旗艦店', '山東 濰坊', 435, 686);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (10, '王小二 湖北土辣椒新鮮青椒農家長辣椒蔬菜包郵尖椒批發特產5斤', 29.9, 86, '王小二旗艦店', '湖北 襄陽', 33, 25);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (11, '小黃瓜水果黃瓜新鮮5斤小青瓜東北旱海陽白玉生喫10山東農家蔬菜', 13.8, 7500, '田園茂旗艦店', '山東 煙臺', 80081, 79562);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (12, '聖女果千禧小番茄新鮮西紅柿千禧長果5斤農家時令蔬菜包郵水果', 18.8, 10000, '時卉源旗艦店', '河南 鄭州', 3014, 2682);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (13, '高端貨!新疆沙瓤西紅柿新鮮自然熟番茄水果普羅旺斯農家順豐包郵', 58.8, 232, '奇蹟匯食品旗艦店', '新疆 吐魯番', 91, 63);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (14, '綠寶石甜瓜小香瓜新鮮水果應季10東北瓜果包郵5斤助農當季整箱', 24.8, 15000, '夢強旗艦店', '山東 臨沂', 34173, 37618);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (15, '5斤新西蘭貝貝南瓜板栗小南瓜板栗味老瓜栗子板粟10農家新鮮帶箱', 14.8, 10000, '劉小牛旗艦店', '山東 日照', 18590, 6991);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (16, '【5A特大】仙居楊梅新鮮孕婦水果現摘現發正宗農家東魁楊梅6斤裝', 238, 260, '巨浪食品專營店', '浙江 台州', 3775, 8498);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (17, '宜興新鮮百合2500g包郵宜興特產百合純農家食用大白合5斤30個左右', 52, 975, '鎵榮旗艦店', '江蘇 無錫', 2767, 2348);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (18, '楊梅新鮮正宗仙居東魁孕婦現摘現發農家時令水果6斤裝東魁楊梅', 95, 5000, '集果邦旗艦店', '浙江 台州', 385, 10413);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (19, '新鮮自然熟黃金籽西紅柿農家水果老品種5斤非鐵皮博士番茄沙瓤', 49, 2164, '黃金籽旗艦店', '山東 濰坊', 4250, 13763);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (20, '雲南小土豆新鮮10斤馬鈴薯農產品蔬菜紅皮洋芋批發迷你小黃心土豆', 19.8, 10000, '紅高粱食品旗艦店', '雲南 昆明', 51861, 40936);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (21, '現挖雲南紫皮洋蔥5斤新鮮紅皮大圓蔥洋蔥頭農家自種特產蔬菜包郵', 9.9, 249, '紅高粱食品旗艦店', '雲南 紅河', 268, 212);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (22, '新鮮芋頭10斤芋艿小芋頭香芋免郵包郵農家粉糯荔浦毛芋頭整箱紫', 29.8, 7500, '紅高粱食品旗艦店', '山東 濰坊', 49152, 45661);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (23, '雲南紫皮洋蔥新鮮10斤包郵洋蔥頭農家自種當季蔬菜紅皮大圓蔥整箱', 19.8, 8500, '紅高粱食品旗艦店', '雲南 紅河', 5733, 2604);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (24, '盒馬河南焦作鐵棍山藥淨重5斤當季農家蔬菜溫縣新鮮山藥包郵', 39.9, 1645, '盒馬鮮生旗艦店', '河南 焦作', 1185, 1476);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (25, '盒馬山東大蔥淨重5斤新鮮長蔥當季時令蔬菜去葉白香蔥產地農產品', 24.9, 49, '盒馬鮮生旗艦店', '上海', 36, 47);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (26, '盒馬山東玉菇甜瓜2粒裝單粒1kg起當季時令水果新鮮甜瓜蜜瓜', 24.9, 566, '盒馬鮮生旗艦店', '山東 濰坊', 97, 69);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (27, '【芭芭農場】新疆普羅旺斯西紅柿淨重5斤當季番茄自然熟水果蔬菜', 39.9, 7500, '盒馬鮮生旗艦店', '陝西 西安', 2262, 1697);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (28, '芋頭新鮮蔬菜小芋頭毛芋頭香芋農家自種芋頭芋艿非荔浦芋頭5斤10', 18.5, 4888, '果鮮萌旗艦店', '山東 濰坊', 14571, 13733);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (29, '農家自種新鮮小米辣椒5斤紅辣椒朝天椒蔬菜泡椒特辣小米椒鮮辣椒', 28.8, 4867, '果品康旗艦店', '海南 海口', 9453, 9562);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (30, '2020年新鮮現挖小洋蔥帶箱10斤 紅皮紫皮洋蔥頭圓蔥農家自種蔬菜', 10.8, 15000, '果戀韻旗艦店', '雲南 紅河', 23575, 15821);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (31, '海南新鮮辣椒小米椒5斤朝天小米辣紅辣椒農家土蔬菜可剁泡椒包郵', 29.9, 1936, '果綽旗艦店', '海南 海口', 2413, 2962);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (32, '甘福園 現挖新鮮甜菜根11斤紫甜菜紅菜頭大蘿蔔農家蔬菜5整箱包郵', 29.9, 1195, '甘福園旗艦店', '江蘇 徐州', 4815, 6574);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (33, '山東新鮮小米椒5斤紅辣椒朝天椒指天七星椒尖椒泡椒農家蔬菜包郵', 29.8, 1789, '甘福園旗艦店', '山東 濰坊', 338, 443);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (34, '山東章丘大蔥5斤 新鮮鐵桿長蔥農家香蔥去葉蔥白甜蔥特產蔬菜包郵', 12.9, 2032, '甘福園旗艦店', '山東 濟南', 1411, 695);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (35, '現挖新鮮蘆筍3斤當季農家蔬菜福建特產綠蘆筍青筍春筍龍鬚菜包郵', 29.9, 637, '甘福園旗艦店', '福建 漳州', 1225, 907);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (36, '豐縣紫皮洋蔥10斤新鮮洋蔥應季農家自種蔬菜圓紅皮洋蔥頭整箱包郵', 19.8, 10000, '福瑞達旗艦店', '江蘇 徐州', 4296, 2422);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (37, '雲南小土豆新鮮10斤馬鈴薯農產品蔬菜洋芋批發迷你小黃心紅皮土豆', 19.8, 10000, '福瑞達旗艦店', '雲南 昆明', 4412, 3528);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (38, '雲南黃皮洋蔥10斤新鮮蔬菜 當季農家自種圓蔥頭整箱批發包郵5', 9.9, 1042, '福瑞達旗艦店', '雲南 昆明', 225, 230);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (39, '特辣小米辣椒新鮮5斤 朝天椒應季農家蔬菜指天椒泡尖椒紅辣椒包郵', 29.9, 122, '福瑞達旗艦店', '貴州 貴陽', 447, 449);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (40, '雲南小土豆新鮮10斤馬鈴薯農產品蔬菜洋芋批發迷你小黃心紅皮土豆', 19.8, 1830, '福瑞達旗艦店', '雲南 昆明', 448, 323);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (41, '新鮮小米辣椒朝天椒特辣小米椒土紅辣椒5農家蔬菜鮮尖椒4斤菜椒10', 19.9, 10000, '芳泰食品旗艦店', '廣東 茂名', 35707, 49750);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (42, '水果黃瓜新鮮5斤生喫脆嫩旱黃瓜山東農家蔬菜涼拌小青瓜當季包郵', 13.9, 176, '饞尚皇旗艦店', '山東 濰坊', 16, 73);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (43, '西紅柿陝西農家生喫新鮮沙瓤自然熟蔬菜水果番茄5/10斤帶箱包郵', 24.9, 947, '璨掌櫃旗艦店', '陝西 咸陽', 364, 223);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (44, '正宗廣西荔浦芋頭新鮮香芋10斤整箱農家當季檳榔芋大芋艿毛芋免郵', 35.9, 2774, '百泰源旗艦店', '廣西 桂林', 682, 1015);
INSERT INTO testdb.product_table (productID, product_name, price, volume, shopp_name, location, evaluate, collect) VALUES (45, '雲南土豆新鮮10斤大號紅皮黃心農家自種小蔬菜產品包郵馬鈴薯洋芋', 19.8, 3597, '阿樸食品旗艦店', '雲南 曲靖', 772, 805);

四、實戰java項目實現該功能

(一)項目目錄結構

在這裏插入圖片描述

由於代碼量比較多,這裏就不一一的貼出來了,都貼出來也沒意思,這裏只把重要的代碼貼出來

這些代碼我都放在了github上:

https://github.com/8042965/recommenderSystem

(二)數據庫幫助類(操作數據庫)

每個接口都有註釋,很清楚,代碼也比較簡單

package com.db;

import com.entity.MemberUser;
import com.entity.ProductOrder;
import com.entity.ProductTable;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class DBHelp {



	static String url = "jdbc:mysql://127.0.0.1:3306/testdb";
	static String user= "root";
	static String password= "123456";



	static {
		try {
			Class.forName("com.mysql.jdbc.Driver");
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	public static Connection getConnection() {
		try {
			return DriverManager.getConnection(url, user, password);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}


	static Connection conn = DBHelp.getConnection();
	static Statement st = null;
	static ResultSet rs = null;


	/**
	 * 獲取所有的商品信息
	 * @return
	 * @param sqlId
	 */
	public static List<ProductTable> getProductList(String sqlId){

		List<ProductTable> productTables = new ArrayList<>();

		try {
			st = conn.createStatement();
			rs = st.executeQuery("select * from product_table where productID in ("+sqlId+")");

			while (rs.next()){
				productTables.add(new ProductTable(
						rs.getInt("productID"),
						rs.getString("product_name"),
						rs.getDouble("price"),
						rs.getInt("volume"),
						rs.getString("shopp_name"),
						rs.getString("location"),
						rs.getInt("evaluate"),
						rs.getInt("collect")));
			}

		} catch (SQLException e) {
			e.printStackTrace();
		}

		return productTables;
	}

	//獲取用戶訂單信息
	public static List<ProductOrder> getProductOrderList(Integer userId){
		List<ProductOrder> productTables = new ArrayList<>();

//		String sql = "select * from product_order where USER_ID=(select USER_ID from member_user where USER_NAME=\""+name+"\")";

		String sql = "select * from product_order "+(userId==null?"":"where USER_ID="+userId);
//		System.out.println("執行的 sql: "+sql);
		try {
			st = conn.createStatement();
			rs = st.executeQuery(sql);

			while (rs.next()){
				productTables.add(new ProductOrder(
						rs.getInt("order_id"),
						rs.getInt("user_id"),
						rs.getInt("product_id"),
						rs.getInt("gwcount")));
			}

		} catch (SQLException e) {
			e.printStackTrace();
		}

		return productTables;
	}

	//獲取用戶信息
	public static List<MemberUser> getMemberUserList(){
		List<MemberUser> productTables = new ArrayList<>();

		try {
			st = conn.createStatement();
			rs = st.executeQuery("select * from member_user");

			while (rs.next()){
				productTables.add(new MemberUser(
						rs.getInt("user_id"),
						rs.getString("user_name")));
			}

		} catch (SQLException e) {
			e.printStackTrace();
		}

		return productTables;
	}
}

(三)計算時所用到的實體類

package com.entity;

import java.util.Arrays;

/**
 * @Auther: truedei
 * @Date: 2020 /20-6-13 22:53
 * @Description:
 */
public class UserR {

    private String userName;

    private Integer userId;

    private Integer[] ProductIds;//存放每個用戶所購買的商品的id

    private Double cos_th; //存儲每個用戶與當前用戶所計算出來的cos值

    ....省略get和set方法
}

(四)用戶登錄接口的實現

接口隨便定義哈,我這裏只是做實驗,就隨便起了個名字;

我的本意是在用戶登錄的時候,根據該用戶的id去拿到該用戶的訂單數據,然後進行數據處理–推薦等

/**
     * 登錄後推薦接口
     * @param userId 模擬登錄的用戶ID
     */
public void login(Integer userId){

    //1,使用該用戶的名字獲取訂單信息
    System.out.println("----------------");
    //查詢登錄用戶的訂單信息
    List<ProductOrder> productOrderList = DBHelp.getProductOrderList(userId);

    //存儲個人 購買的所有的商品id
    Integer[] ints = new Integer[productOrderList.size()];

    //存儲個人信息,封裝成對象,方便計算
    UserR userR = new UserR();

    //篩選出來個人訂單中的商品的id
    System.out.println("個人的:");
    for (int i = 0; i < productOrderList.size(); i++) {
        ints[i] = productOrderList.get(i).getProduct_id();
        System.out.println(productOrderList.get(i).toString());
    }
    userR.setUserId(productOrderList.get(0).getUser_id());
    userR.setProductIds(ints);


    //2,拿到所有用戶的訂單信息
    List<ProductOrder> productOrderLists = DBHelp.getProductOrderList(null);

    //存儲所有人的訂單信息
    List<UserR> userRS = new ArrayList<>();

    //利用map的機制,計算出來其餘用戶的所有的購買商品的id  Map<用戶id,商品ID拼接的字符串(1,2,3,4)>
    Map<Integer,String> map = new HashMap<>();


    System.out.println("所有人的:");
    //篩選出來訂單中的商品的id
    for (int i = 0; i < productOrderLists.size(); i++) {
        System.out.println(productOrderLists.get(i).toString());

        map.put(productOrderLists.get(i).getUser_id(),
                map.containsKey(productOrderLists.get(i).getUser_id())?
                map.get(productOrderLists.get(i).getUser_id())+","+productOrderLists.get(i).getProduct_id():
                productOrderLists.get(i).getProduct_id()+"");
    }

    //開始封裝每個人的數據
    for (Integer key:map.keySet() ) {

        //new出來一個新的個人的對象,後面要塞到list中
        UserR userR2 = new UserR();

        //把其他每個人購買的商品的id 分割成數組
        String[] split = map.get(key).split(",");

        //轉換成int數組 進行存儲,方便後期計算
        Integer[] ints1 = new Integer[split.length];
        for (int i = 0; i < split.length; i++) {
            ints1[i] = Integer.valueOf(split[i]);
        }

        //用戶id 就是key
        userR2.setUserId(key);
        //用戶購買的商品id的數組
        userR2.setProductIds(ints1);

        //塞到list中
        userRS.add(userR2);
    }

    //二值化 處理數據
    List<UserR> userRList = jisuan(userR, userRS);

    System.out.println("得出的結果:");
    for (int i = 0; i < userRList.size(); i++) {
        System.out.println(userRList.get(i).toString());
    }

    System.out.println("過濾處理數據之後:");
    //過濾處理
    String sqlId = chuli(userRList, userR);

    System.out.println("推薦的商品:");
    //通過拿到的拼接的被推薦商品的id,去查數據庫
    List<ProductTable> productList = DBHelp.getProductList(sqlId);
    //最終拿到被推薦商品的信息
    for (int i = 0; i < productList.size(); i++) {
        System.out.println(productList.get(i).toString());
    }

}

(五)過濾數據接口的實現

因爲存在着一些數據定是不可用的,所以我們要過濾處理掉之後的數據,就是我們想要的。

 /**
     * 過濾處理
     * @param userRList 所有用戶的訂單數據
     * @param userR 當前登錄用戶的訂單數據
     * @return
     */
private String chuli(List<UserR> userRList,UserR userR) {

    //爲了方便下面過濾數據,預先把登錄用戶的訂單購物的商品的id做一個map,在過濾的時候,只需要查一下map中是否存在key就ok
    Map<Integer,Integer> map1 = new HashMap<>();
    for (int i = 0; i < userR.getProductIds().length; i++) {
        map1.put(userR.getProductIds()[i],userR.getProductIds()[i]);
    }


    //盛放最終過濾出來的數據 Map<商品id,出現的次數>
    Map<Integer,Integer> map = new HashMap<>();

    for (int i = 0; i < userRList.size(); i++) {
        //userRList.get(i).getCos_th()>0:過濾掉相似度等於0,也就是完全不匹配的
        //userRList.get(i).getUserId()!=userR.getUserId():過濾掉當前用戶的訂單信息
        if(userRList.get(i).getCos_th()>0 && userRList.get(i).getUserId()!=userR.getUserId()){
            //求當前登錄用戶的購買商品的id和其他用戶的所購買商品的差集,例如:A=[1, 2],B=[1, 2, 3]  那麼這個3就是最終想要的結果
            Integer[] j = QJ.getC(userRList.get(i).getProductIds(), userR.getProductIds());

            //遍歷求差集之後的結果
            for (int i1 = 0; i1 < j.length; i1++) {
                //如果其餘的用戶所購買撒謊那個品的id不在當前用的所購買商品的id,那麼就存起來
                if(!map1.containsKey(j[i1])){
                    //存儲時,數量每次都+1,方便後面排序,出現的次數多,說明被推薦的機會越高
                    map.put(j[i1],map.containsKey(j[i1])?(map.get(j[i1])+1):1);
                }
            }
        }
    }


    System.out.println("處理之後的map:");
    for (Integer key:map.keySet()) {
        System.out.println("商品id="+key+"--用戶所購數量="+map.get(key));
    }

    //把map進行降序排序
    Map<Integer, Integer> map2 = MapSortUtil.sortByKeyDesc(map);
    System.out.println("按降序" + map2);


    //拼接成一個sql,方便去查數據庫
    String sqlId = "";
    for (Integer key:map2.keySet()) {
        sqlId = sqlId+key +",";
    }

    sqlId = sqlId.substring(0,sqlId.length()-1);

    System.out.println("最終拿到的被推薦給當前用戶的商品id--->"+sqlId);

    return sqlId;
}

(六)二值化數據庫處理

這裏是爲了計起來方便,特意把數據進行二值化處理


/**
     * 二值化 處理數據
     * @param userR 當前登錄用戶的訂單信息
     * @param userRS 其他用戶的訂單信息
     * @return 二值化處理之後的結果
     */
private List<UserR> jisuan(UserR userR, List<UserR> userRS) {

    //對個人做二值化處理,爲了好計算 [0,0,0,0,0,1,1,0,1]這種
    //個人的
    int userErzhihua[] = new int[10];
    for (int i = 0; i < userR.getProductIds().length; i++) {
        userErzhihua[userR.getProductIds()[i]]=1;
    }


    //庫裏所有人的
    int erzhihua[] = new int[10];
    //對其他人,做二值化處理,爲了好計算 [0,0,0,0,0,1,1,0,1]這種
    for (int i = 0; i < userRS.size(); i++) {
        UserR product = userRS.get(i);
        for (int j = 0; j < product.getProductIds().length; j++) {
            erzhihua[product.getProductIds()[j]]=1;
        }
        //計算當前登錄用戶與其餘每個人的餘弦值 cos_th
        Double compare = compare( erzhihua,userErzhihua);
        product.setCos_th(compare);

        //把計算好的值,重新塞到原來的位置,替換到舊的數據
        userRS.set(i,product);

        //防止數組中的值重複,起到清空的作用
        erzhihua = new int[10];
    }

    return userRS;

}

(七)核心計算相似度代碼

  /**
     * 代碼核心內容
     * @param o1 當前登錄用戶的
     * @param o2 其他用戶的 n1 n2 n3 n4 n....
     * @return
     */
private static Double compare(int[] o1, int[] o2) {
    //分子求和
    Double fenzi = 0.0 ;

    for (int i = 0; i < o1.length; i++) {
        fenzi += o1[i]*o2[i];
    }
    //分母第一部分
    Double fenmu1 = 0.0;
    for (int i = 0; i < o1.length; i++) {
        fenmu1 += o1[i] * o1[i];
    }
    fenmu1 = Math.sqrt(fenmu1);
    //分母第二部分
    Double fenmu2 = 0.0;
    for (int i = 0; i < o2.length; i++) {
        fenmu2 += o2[i] * o2[i];
    }
    fenmu2 = Math.sqrt(fenmu2);
    return fenzi / (fenmu1 * fenmu2);
}

五、測試結果

(一)用戶1推薦結果

使用用戶id爲1的用戶登錄:

 login(1);

計算結果:

----------------
個人的:
ProductOrder{order_id=1, user_id=1, product_id=1, gwcount=15}
ProductOrder{order_id=5, user_id=1, product_id=2, gwcount=21}
所有人的:
ProductOrder{order_id=1, user_id=1, product_id=1, gwcount=15}
ProductOrder{order_id=2, user_id=2, product_id=3, gwcount=42}
ProductOrder{order_id=3, user_id=3, product_id=4, gwcount=2}
ProductOrder{order_id=4, user_id=4, product_id=4, gwcount=20}
ProductOrder{order_id=5, user_id=1, product_id=2, gwcount=21}
ProductOrder{order_id=6, user_id=5, product_id=1, gwcount=0}
ProductOrder{order_id=7, user_id=5, product_id=2, gwcount=0}
ProductOrder{order_id=8, user_id=5, product_id=3, gwcount=0}
ProductOrder{order_id=9, user_id=6, product_id=2, gwcount=0}
ProductOrder{order_id=10, user_id=6, product_id=5, gwcount=0}
ProductOrder{order_id=11, user_id=7, product_id=1, gwcount=0}
ProductOrder{order_id=12, user_id=7, product_id=2, gwcount=0}
ProductOrder{order_id=13, user_id=7, product_id=5, gwcount=0}
ProductOrder{order_id=14, user_id=3, product_id=1, gwcount=0}
得出的結果:
UserR{userName='null', userId=1, ProductIds=[1, 2], cos_th=0.9999999999999998}
UserR{userName='null', userId=2, ProductIds=[3], cos_th=0.0}
UserR{userName='null', userId=3, ProductIds=[4, 1], cos_th=0.4999999999999999}
UserR{userName='null', userId=4, ProductIds=[4], cos_th=0.0}
UserR{userName='null', userId=5, ProductIds=[1, 2, 3], cos_th=0.8164965809277259}
UserR{userName='null', userId=6, ProductIds=[2, 5], cos_th=0.4999999999999999}
UserR{userName='null', userId=7, ProductIds=[1, 2, 5], cos_th=0.8164965809277259}
過濾處理數據之後:
處理之後的map:
商品id=3--用戶所購數量=1
商品id=4--用戶所購數量=1
商品id=5--用戶所購數量=2
按降序{5=2, 4=1, 3=1}
最終拿到的被推薦給當前用戶的商品id--->5,4,3
推薦的商品:
ProductTable{productID=3, product_name='新鮮洋蔥10斤紫皮紅皮農家2020年蔥頭應季蔬菜圓頭整箱批發包郵', price=18.8, volume=64, shopp_name='悠鮮源旗艦店', location='雲南 昆明', evaluate=7, collect=51}
ProductTable{productID=4, product_name='軒農谷仙居東魁楊梅新鮮水果浙江現摘現發大楊梅禮盒預定7A6斤', price=358.0, volume=222, shopp_name='軒農谷旗艦店', location='浙江 台州', evaluate=553, collect=1163}
ProductTable{productID=5, product_name='軒農谷正宗仙居楊梅新鮮當季水果特級東魁大楊梅5A級6斤高山現摘', price=258.0, volume=2939, shopp_name='軒農谷旗艦店', location='浙江 台州', evaluate=4270, collect=8737}

可以看到推薦的結果是:3,4,5號商品

推薦的商品:
ProductTable{productID=3, product_name='新鮮洋蔥10斤紫皮紅皮農家2020年蔥頭應季蔬菜圓頭整箱批發包郵', price=18.8, volume=64, shopp_name='悠鮮源旗艦店', location='雲南 昆明', evaluate=7, collect=51}
ProductTable{productID=4, product_name='軒農谷仙居東魁楊梅新鮮水果浙江現摘現發大楊梅禮盒預定7A6斤', price=358.0, volume=222, shopp_name='軒農谷旗艦店', location='浙江 台州', evaluate=553, collect=1163}
ProductTable{productID=5, product_name='軒農谷正宗仙居楊梅新鮮當季水果特級東魁大楊梅5A級6斤高山現摘', price=258.0, volume=2939, shopp_name='軒農谷旗艦店', location='浙江 台州', evaluate=4270, collect=8737}

我們可以看到相似度爲:

UserR{userName='null', userId=1, ProductIds=[1, 2], cos_th=0.9999999999999998}
UserR{userName='null', userId=2, ProductIds=[3], cos_th=0.0}
UserR{userName='null', userId=3, ProductIds=[4, 1], cos_th=0.4999999999999999}
UserR{userName='null', userId=4, ProductIds=[4], cos_th=0.0}
UserR{userName='null', userId=5, ProductIds=[1, 2, 3], cos_th=0.8164965809277259}
UserR{userName='null', userId=6, ProductIds=[2, 5], cos_th=0.4999999999999999}
UserR{userName='null', userId=7, ProductIds=[1, 2, 5], cos_th=0.8164965809277259}

通過過濾,得出:

UserR{userName='null', userId=3, ProductIds=[4, 1], cos_th=0.4999999999999999}
UserR{userName='null', userId=5, ProductIds=[1, 2, 3], cos_th=0.8164965809277259}
UserR{userName='null', userId=6, ProductIds=[2, 5], cos_th=0.4999999999999999}
UserR{userName='null', userId=7, ProductIds=[1, 2, 5], cos_th=0.8164965809277259}

最終去除重複的,去除原賬戶已有的,就是我們想要的結果:4,3,5

(二)用戶2推薦結果

----------------
個人的:
ProductOrder{order_id=2, user_id=2, product_id=3, gwcount=42}
所有人的:
ProductOrder{order_id=1, user_id=1, product_id=1, gwcount=15}
ProductOrder{order_id=2, user_id=2, product_id=3, gwcount=42}
ProductOrder{order_id=3, user_id=3, product_id=4, gwcount=2}
ProductOrder{order_id=4, user_id=4, product_id=4, gwcount=20}
ProductOrder{order_id=5, user_id=1, product_id=2, gwcount=21}
ProductOrder{order_id=6, user_id=5, product_id=1, gwcount=0}
ProductOrder{order_id=7, user_id=5, product_id=2, gwcount=0}
ProductOrder{order_id=8, user_id=5, product_id=3, gwcount=0}
ProductOrder{order_id=9, user_id=6, product_id=2, gwcount=0}
ProductOrder{order_id=10, user_id=6, product_id=5, gwcount=0}
ProductOrder{order_id=11, user_id=7, product_id=1, gwcount=0}
ProductOrder{order_id=12, user_id=7, product_id=2, gwcount=0}
ProductOrder{order_id=13, user_id=7, product_id=5, gwcount=0}
ProductOrder{order_id=14, user_id=3, product_id=1, gwcount=0}
得出的結果:
UserR{userName='null', userId=1, ProductIds=[1, 2], cos_th=0.0}
UserR{userName='null', userId=2, ProductIds=[3], cos_th=1.0}
UserR{userName='null', userId=3, ProductIds=[4, 1], cos_th=0.0}
UserR{userName='null', userId=4, ProductIds=[4], cos_th=0.0}
UserR{userName='null', userId=5, ProductIds=[1, 2, 3], cos_th=0.5773502691896258}
UserR{userName='null', userId=6, ProductIds=[2, 5], cos_th=0.0}
UserR{userName='null', userId=7, ProductIds=[1, 2, 5], cos_th=0.0}
過濾處理數據之後:
處理之後的map:
商品id=1--用戶所購數量=1
商品id=2--用戶所購數量=1
按降序{2=1, 1=1}
最終拿到的被推薦給當前用戶的商品id--->2,1
推薦的商品:
ProductTable{productID=1, product_name='楊梅新鮮現摘現發正宗仙居東魁大楊梅孕婦農家時令水果6斤裝包郵', price=198.0, volume=1328, shopp_name='浙仙旗艦店', location='浙江 台州', evaluate=240, collect=1397}
ProductTable{productID=2, product_name='正宗仙居東魁楊梅新鮮現摘特級大東魁現摘現發農家時令水5斤精選', price=268.0, volume=497, shopp_name='浙仙旗艦店', location='浙江 台州', evaluate=159, collect=619}

六、我有話要對你說

如果對你有幫助,可以分享給你身邊的朋友。或者給俺點個大大的贊和大大的評論,點贊和評論就是給我最大的支持,感謝。
水平有限,難免會有疏漏或者書寫不合理的地方,歡迎交流討論。
作者:TrueDei
作者唯一博客CSDN:https://truedei.blog.csdn.net/
轉載說明:如需轉載請註明原地址和作者名。

如果喜歡我的文章,還沒看夠可以關注我,我會用心寫好每一篇文章。

我已加入CSDN合夥人計劃

親愛的各位粉絲:可以添加我的CSDN官方企業微信號,和我近距離互動聊天,爲您答疑解惑

直接使用微信掃碼即可,不用下載企業微信。

在這裏插入圖片描述

也可以加入此羣:到期後,可以加我上面微信哦
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章