背景:因營(yíng)銷(xiāo)需求明吩,對(duì)家具市場(chǎng)近期活動(dòng)有明確的了解间学,分析出近期對(duì)手上新的產(chǎn)品&活動(dòng)力度。
數(shù)據(jù):天貓林氏木業(yè)旗艦店各類(lèi)家具商品的詳細(xì)信息印荔,包括但不限于產(chǎn)品詳細(xì)信息低葫,價(jià)格,銷(xiāo)量仍律,評(píng)論量嘿悬,圖片。
目標(biāo):編寫(xiě)爬蟲(chóng)框架水泉,定期獲取目標(biāo)數(shù)據(jù)善涨,導(dǎo)入數(shù)據(jù)庫(kù)。分析出熱銷(xiāo)產(chǎn)品的關(guān)鍵因素草则,活動(dòng)折扣力度钢拧。為圖片打上標(biāo)簽,以便后期對(duì)比劃分炕横,調(diào)整節(jié)日期間活動(dòng)源内。
【一】思路
1、爬蟲(chóng):觀察目標(biāo)網(wǎng)址檢查后發(fā)現(xiàn)數(shù)據(jù)信息通過(guò)js隱藏份殿,需要導(dǎo)出其實(shí)際跳轉(zhuǎn)網(wǎng)址進(jìn)行爬取膜钓。
2、分析:清洗數(shù)據(jù)&特征工程后卿嘲,分析出熱銷(xiāo)產(chǎn)品的關(guān)鍵點(diǎn)颂斜。結(jié)合活動(dòng)力度找出提升銷(xiāo)量的關(guān)鍵因素。
3腔寡、分類(lèi):直接使用百度AI焚鲜。
【二】項(xiàng)目流程
【三】爬蟲(chóng)代碼
1.獲取商品的主要分類(lèi)鏈接
直接獲取分類(lèi)的鏈接,生成表格
2.根據(jù)主鏈接爬取異步加載的鏈接
# 提取主分類(lèi)鏈接的ID
def ID (X):
import pandas as pd
import re
patter = 'category-(.*).htm?'
A = []
for i in range(len(X['子分類(lèi)鏈接'])):
A.append(re.findall(patter, X['子分類(lèi)鏈接'][i]))
A=pd.DataFrame(pd.Series(A),columns=['ID'])
X = pd.concat([X,A],axis = 1)
return(X)
# 提取網(wǎng)址中的編碼
def Encode (X):
import pandas as pd
A = []
for i in range(len(X['子分類(lèi)'])):
a = X['子分類(lèi)'][i].encode('gb2312')
a = str(a)
a = a.replace('\\x','%')
a = a.strip("b'")
A.append(a)
A = pd.DataFrame(pd.Series(A), columns=['url編碼'])
X = pd.concat([X, A], axis=1)
return (X)
再將ID與編碼拼接成異步鏈接網(wǎng)址
# 生成對(duì)應(yīng)分類(lèi)的異步加載鏈接
def URL (X):
import pandas as pd
A = []
for i in range(len(X['ID'])):
a = 'https://lshmy.tmall.com/i/asynSearch.htm?_ksTS=1537406700485_954&\
callback=jsonp955&mid=w-14434271715-0&wid=14434271715&path=/category-[ID]\
.htm&search=y&keyword=[cat]&scene=taobao_shop&catId=[ID]&scid=[ID]'
b = a.replace('[ID]',X['ID'][i][0])
b = b.replace('[cat]',X['url編碼'][i])
A.append(b)
A = pd.DataFrame(pd.Series(A), columns=['異步加載'])
X = pd.concat([X, A], axis=1)
return (X)
3.獲取所有子分類(lèi)鏈接
考慮到翻頁(yè)情況放前,在需要翻頁(yè)的網(wǎng)址后面寫(xiě)上page忿磅。
# 生成子分類(lèi)的鏈接, X = 異步鏈接
def URL_classify (X) :
import pandas as pd
import requests
from lxml import etree
url_classify = []
for i in X['異步加載']:
url_classify.append(i)
r = requests.get(i)
A = etree.HTML(r.text)
B = A.xpath('/html/body/div/div[3]/div/a/text()')
if B != []:
B.remove('1')
if '上一頁(yè)' in B:
B.remove('上一頁(yè)')
if '下一頁(yè)' in B:
B.remove('下一頁(yè)')
if B != []:
for j in B:
C = i + '&pageNo=' +j
url_classify.append(C)
print('--' * 20)
url_classify = pd.DataFrame(pd.Series(url_classify), columns=['分類(lèi)鏈接'])
return (url_classify )
4.獲取產(chǎn)品的鏈接&ID
根據(jù)所有子類(lèi)的鏈接,獲取所有產(chǎn)品的詳細(xì)地址凭语。
#根據(jù)子分類(lèi)的鏈接葱她,爬取貨物的詳細(xì)鏈接,X = 分類(lèi)鏈接
def URL_detailed (X):
import requests
from lxml import etree
import pandas as pd
import re
url_2 = []
for i in X['分類(lèi)鏈接']:
r = requests.get(i)
A = etree.HTML(r.text)
B = A.xpath('/html/body/div/div[3]/div/dl/dd/a/@href')
while '\\"javascript:;\\"' in B:
B.remove('\\"javascript:;\\"')
url_2.append(B)
print ('-'*40)
url_detailed = []
for i in url_2 :
for j in i:
url_detailed.append(j)
url_detailed_2 = []
for i in url_detailed:
i = i.strip('\\"')
i = 'https:' + i +'&tdsourcetag=s_pctim_aiomsg&sku_properties=21433:50753410;10142888:302694851'
url_detailed_2.append (i)
url_detailed_2 = pd.DataFrame(pd.Series(url_detailed_2), columns=['貨物鏈接'])
return (url_detailed_2)
# 根據(jù)貨物的鏈接似扔,爬取貨物的ID吨些,X = 貨物鏈接
def ID_detailed (X):
import pandas as pd
import re
patter = 'id=(.*)&rn'
A = []
for i in X['貨物鏈接']:
A.append(re.findall(patter, i))
A = pd.DataFrame(pd.Series(A), columns=['貨物ID'])
return(A)
5.獲取產(chǎn)品的詳細(xì)屬性
查看需要獲取的屬性搓谆,將其整合成列表。在爬取過(guò)程中生成對(duì)應(yīng)的鍵值對(duì)豪墅。
#根據(jù)貨物的詳細(xì)鏈接泉手,爬去貨物的屬性信息,X = 貨物詳細(xì)鏈接表
def Label (X):
import pandas as pd
import re
import requests
from lxml import etree
requests.adapters.DEFAULT_RETRIES = 5
d1 =['包裝體積:','型號(hào):','是否預(yù)售:','款式定位:','顏色分類(lèi):','尺寸:','是否帶儲(chǔ)物空間:','填充物:','是否帶軟靠:',\
'是否可定制:','材質(zhì):','風(fēng)格:','家具結(jié)構(gòu):']
label = []
number = 0
headers = {
'Connection':'close',
'user-agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/54.0.2840.99 Safari/537.36',
'cookie':'cna=P+QpFIBjZRECAd3tmK78L147; _m_h5_tk=fb7756c8ab31726f5bf669b649827a44_1537527405908; _m_h5_tk_enc=69ada9216bd726de6e5e7b6bd87bc923; t=eba31fabfb5d9e662f3140dda391b1c2; _tb_token_=ed137f8e33b3a;cookie2=147853597cf22040f4cc675132589da6; pnm_cku822=098%23E1hvMpvUvbpvUvCkvvvvvjiPPsS96jtbR2MpQjthPmP9tj1hP2dOtjnjPFdyzjtbRphvCvvvphvEvpCW99K0r30gRbutnV3Xibm0HskTWlK91Ep7%2BulAbMo6eCOtHjnNAbmAdBeK4Z7xfBeK5dXKjdzCHEp7%2B3%2BuaNpArqVTbZkt640AdByapbyCvm9vvhCvvvvvvvvvBJZvvUhCvvCHtpvv9ZUvvhcDvvmCb9vvBJZvvUhKkphvC9hvpyPZ68yCvv9vvhh5RPb9YQhCvvOv9hCvvvvPvpvhvv2MMTwCvvpvvhHh; cq=ccp%3D1; isg=BKqqBC3Mhcz5IwmrkTMwD7HR-xCMsy_cRtO92TRjHP2CZ0ohHKgmhLmV89Nel6YN'
}
for i in X ['貨物鏈接'] :
r = requests.session()
r.keep_alive = False
r = requests.get(i,timeout = 10,headers = headers)
A = etree.HTML(r.text)
B = A.xpath('//*[@id="J_AttrUL"]/li/text()')
C = ''
for j in B:
j = '-' + j + '-'
C = C + j
C = C.replace('\xa0','')
_屬性={}
for k in d1:
A = re.findall('-%s(.*?)-'%k,C)
if A == []:
A = 'NAN'
else :
A = A[0]
_屬性.update({k:A})
label.append(_屬性)
number += 1
print ('---'*20 + str(number))
label = pd.DataFrame(label)
return (label)
5.獲取產(chǎn)品的評(píng)論量
觀察頁(yè)面的信息,發(fā)現(xiàn)累計(jì)評(píng)價(jià)是通過(guò)加載json文件進(jìn)行傳輸?shù)呐计鳌m?yè)面中找到對(duì)應(yīng)的網(wǎng)址斩萌,觀察發(fā)現(xiàn)是由于鏈接最后的ID變動(dòng)導(dǎo)致的。
其中設(shè)置斷連判斷屏轰,如果鏈接斷開(kāi)的話颊郎,就進(jìn)行睡眠。
def Comment (X):
requests.adapters.DEFAULT_RETRIES = 5
r = requests.session()
r.keep_alive = False
url = 'https://dsr-rate.tmall.com/list_dsr_info.htm?itemId='
comment = []
number = 0
for i,j in zip(X['貨物ID'],X['貨物鏈接']):
i = i.strip("[]'")
A = url + i
B = []
headers = {
"Referer":j,
'cookie':'cna=P+QpFIBjZRECAd3tmK78L147; _m_h5_tk=fb7756c8ab31726f5bf669b649827a44_1537527405908; _m_h5_tk_enc=69ada9216bd726de6e5e7b6bd87bc923; t=425bdd5592c604f0d691f808f0a844b1; _tb_token_=73137373e63e8; cookie2=10d98d0bd350def7bc50f0880033ce61; isg=BGNjUSgJPOPwC_DccBAZECAC8qfN8PYzV6AkDpXDFUIy1ID2HSkK6nBFysT_9E-S',
'user-agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36'
}
try:
r = requests.get(A,timeout = 10,headers = headers)
B = re.findall('"rateTotal":(.*?)}',r.text)
comment.append(B)
number += 1
print ('---'*20 + str(number))
except:
# print("Connection refused by the server..")
# print("Let me sleep for 5 seconds")
print("ZZzzzz...")
time.sleep(5)
print("Was a nice sleep, now let me continue...")
comment = pd.DataFrame(pd.Series(comment), columns=['評(píng)論數(shù)量'])
return (comment)
6.獲取產(chǎn)品的價(jià)格
同理根據(jù)隱藏拼接即可獲取相應(yīng)的價(jià)格霎苗。
7.下載圖片
將先前獲取的圖片鏈接逐一清洗出來(lái)姆吭,再進(jìn)行下載。
#清洗出圖片地址唁盏,X = 產(chǎn)品包含的圖片地址
def JPG_url (X):
jpg_url = []
for i in X['圖片']:
if ',' not in i:
url = 'http:' + i.strip(r"[]'")
jpg_url.append(url)
else :
i = i.replace("'",'')
i = i.strip(r"[]'")
i = i.split(',')
for j in i:
j = j.strip()
url = 'http:' + j
jpg_url.append(url)
#jpg_url = pd.DataFrame(pd.Series(jpg_url), columns=['圖片下載地址'])
return (jpg_url)
#通過(guò)圖片地址内狸,將圖片下載到本地,X = 每張圖片的下載地址
def JPG (X):
import os
number = 1
for i in X['圖片下載地址']:
url = i
#圖片存儲(chǔ)的地址
root = "G://python//"
path = root + url.split("/")[-1]
try:
if not os.path.exists(root):
os.mkdir(root)
if not os.path.exists(path):
r = requests.get(url,timeout = 10)
r.raise_for_status()
#使用with語(yǔ)句可以不用自己手動(dòng)關(guān)閉已經(jīng)打開(kāi)的文件流
with open(path,"wb") as f: #開(kāi)始寫(xiě)文件升敲,wb代表寫(xiě)二進(jìn)制文件
f.write(r.content)
print("爬取完成" + '---'*20 +str(number))
number += 1
else:
print("文件已存在")
except Exception as e:
print("爬取失敗:"+str(e))
else :
time.sleep(1)
return ('爬取完成')
8.導(dǎo)入數(shù)據(jù)庫(kù)
python導(dǎo)入DB2數(shù)據(jù)庫(kù)時(shí)答倡,需要調(diào)整ibm_db包,點(diǎn)我查看
【三】建模分析
1.數(shù)據(jù)探索
獲取數(shù)據(jù)后觀察現(xiàn)有的數(shù)據(jù):
1.驴党、這是家具類(lèi)產(chǎn)品的數(shù)據(jù)瘪撇,是消費(fèi)者能獲取的最直觀的信息。
2港庄、有什么類(lèi)型的feature:產(chǎn)品名稱(chēng)倔既、圖片、產(chǎn)品屬性('包裝體積:','型號(hào):','是否預(yù)售:','款式定位:','顏色分類(lèi):','尺寸:','是否帶儲(chǔ)物空間:','填充物:','是否帶軟靠:','是否可定制:','材質(zhì):','風(fēng)格:','家具結(jié)構(gòu): 等)鹏氧、價(jià)格(市場(chǎng)價(jià)格渤涌,本站價(jià)格)、評(píng)論(評(píng)論量把还,最近評(píng)論時(shí)間)实蓬、銷(xiāo)量、活動(dòng)(折扣吊履,活動(dòng)名稱(chēng))安皱。這些都 目前可以獲取的信息 。
再問(wèn)幾個(gè)問(wèn)題:
1.相同的產(chǎn)品艇炎,不同的名稱(chēng)會(huì)不會(huì)導(dǎo)致在train和test中重復(fù)酌伊?
2.數(shù)據(jù)在時(shí)間上的分布是否會(huì)有影響?
3.有多少價(jià)格區(qū)間缀踪,價(jià)格區(qū)間如何劃分居砖,產(chǎn)品的風(fēng)格種類(lèi)有哪些虹脯?不同類(lèi)型的產(chǎn)品是否會(huì)隨時(shí)間的變化?
4.活動(dòng)對(duì)銷(xiāo)量的影響如何判定奏候?
5.近期什么樣的產(chǎn)品是最受歡迎的循集?
以上都是需要與業(yè)務(wù)部門(mén)同事交流探討后確定的重要維度。
2.數(shù)據(jù)清洗
剔除缺失值明顯的數(shù)據(jù)鼻由,數(shù)據(jù)歸一化暇榴,文字類(lèi)型數(shù)據(jù)轉(zhuǎn)化成數(shù)字類(lèi)型數(shù)據(jù)厚棵。
def Sigmoid (X):
return (1.0 / (1 + np.exp(-float(X)));
def Replace (X,columns):
a = X.groupby([columns],as_index=False)[columns].agg({'cnt':'count'})
for i in a[columns]:
X[columns] = X[columns].replace(i,a[(a[columns]== i )].index.tolist()[0])
return (X)
3.特征工程
個(gè)人認(rèn)為特征工程是建模分析的重中之重蕉世,模型大同小異,基本是都是調(diào)用幾種開(kāi)源框架婆硬。所以狠轻,模型都差不多,特征就是關(guān)鍵了彬犯。
利用模型自帶的feature_importance_,寫(xiě)出一個(gè)特征選擇函數(shù):
#alg:模型向楼,columns:特征集合
def select_important_features(alg,columns):
importances=zip(map(lambda x: round(x, 4), alg.feature_importances_), columns)
importance=[]
sum=0
for i in importances:
if i[0]>0.008:
importance.append(i[1])
sum=sum+i[0]
print("Num : %f | Sum: %3f" % (len(importance), sum))
return importance
importance = select_important_features(rfr,x_train.columns)
每個(gè)模型都有獨(dú)特的業(yè)務(wù)背景,想要發(fā)現(xiàn)甚至是自己創(chuàng)造出重要的特征谐区,往往需要深厚的業(yè)務(wù)邏輯&行業(yè)經(jīng)驗(yàn)湖蜕。
比如這個(gè)項(xiàng)目要預(yù)測(cè)沙發(fā)產(chǎn)品,沙發(fā)大小&儲(chǔ)物空間大小的比列宋列,就需要通過(guò)原始數(shù)據(jù)進(jìn)行創(chuàng)造昭抒。
當(dāng)然一切還需要參考業(yè)務(wù)部門(mén)的邏輯&意見(jiàn)。
一般進(jìn)行特此選擇可以參考常用特征的選擇方式
4.模型選擇&調(diào)參
本次測(cè)試中選用的是LightGBM模型炼杖,模型的性能與準(zhǔn)確率都較好灭返。當(dāng)然為了提高效果的話,應(yīng)當(dāng)多個(gè)模型比較坤邪。
LightGBM:
- num_leaves: 葉子節(jié)點(diǎn)的個(gè)數(shù)
- max_depth:最大深度熙含,控制分裂的深度
- learning_rate: 學(xué)習(xí)率
- objective: 損失函數(shù)(mse, huber loss, fair loss等)
- min_data_in_leaf: 葉子節(jié)點(diǎn)必須包含的最少樣本數(shù)
- feature_fraction: 訓(xùn)練時(shí)使用feature的比例
- bagging_fraction: 訓(xùn)練時(shí)使用樣本的比例
調(diào)整參數(shù)還是需要一些玄學(xué),想要找到最佳的參數(shù)很多時(shí)候需要一些經(jīng)驗(yàn)和運(yùn)氣艇纺。
5.模型融合
-
Averaging
Averaging融合方式就是加權(quán)平均怎静。格局模型的多樣性,平均后長(zhǎng)補(bǔ)短黔衡,更準(zhǔn)確第提高模的泛化能力蚓聘。 -
Stacking
Stacking個(gè)人認(rèn)為就是將第一層模型的結(jié)果作為第二層模型的特征,然后訓(xùn)練第二層模型得到最終結(jié)果员帮。
詳細(xì)解釋
6.分析呈現(xiàn)
測(cè)試后或粮,根據(jù)相應(yīng)的得分結(jié)果選擇模型。(分類(lèi):混合矩陣捞高、f1_score 氯材≡酰回歸:rmse、rmae氢哮、r2_score )
直接使用帆軟袋毙,將導(dǎo)入的結(jié)果以BI報(bào)表的形式呈現(xiàn)。
【四】分類(lèi)打標(biāo)
1.圖像識(shí)別分類(lèi)
目前根據(jù)業(yè)務(wù)部門(mén)需求冗尤,需要將競(jìng)品按照風(fēng)格听盖,類(lèi)型等劃分入不同的標(biāo)簽。
經(jīng)過(guò)部門(mén)溝通后裂七,選擇百度 EasyDL進(jìn)行圖像劃分皆看。方便業(yè)務(wù)部門(mén)進(jìn)行操作。
根據(jù)自有圖片進(jìn)行訓(xùn)練背零,多次校準(zhǔn)腰吟。可以將競(jìng)品的圖片劃分入現(xiàn)有的風(fēng)格分類(lèi)徙瓶。
多次訓(xùn)練后毛雇,選擇適合的版本可以有效的劃分相應(yīng)的家具產(chǎn)品分類(lèi)。
對(duì)模型進(jìn)行多次修正侦镇,直到效果滿足預(yù)期灵疮,然后接入API接口,自動(dòng)返回比對(duì)結(jié)果壳繁。
API的接入?yún)⒖?---百度easyDL API接入
【五】總結(jié)分析
本次項(xiàng)目中主要側(cè)重的是數(shù)據(jù)的獲取與處理方面震捣。
建模分析是基于業(yè)務(wù)部門(mén)的支持,產(chǎn)品數(shù)據(jù)的調(diào)整很大一部分都來(lái)自于業(yè)務(wù)邏輯的經(jīng)驗(yàn)之談氮趋。
因?yàn)椴糠衷蛭榕桑荒苷故緮?shù)據(jù)結(jié)果,分析部分只能說(shuō)明思路剩胁。
僅用于記錄個(gè)人工作诉植。